单链表基础操作题记录

每次做算法基础练习就意识到自己有多笨,还是做一下笔记,时常看一看,能开拓一下思路,希望能积少成多。

  1. 链表原地反转:
    思路: 边遍历,边反向改next指针
    1. pre -> current -> next
    2. pre <- current next
    3. 将 pre 赋值为 current, current 赋值为 next, next 赋值为next.next ,相当于整体后移一位。
    4. 重复 2的操作,每次反向改一次next指向。
class Node : NSObject {
    
    var value : NSObject;
    var next : Node?;
    init(value:NSObject) {
        self.value = value;
    }
    
}

//链表原地反转
func reverse(head : Node?) {
    
    if (head == nil || head?.next==nil) {
        return;
    }
    
    var pre : Node? = nil;
    var current :Node = head!;
    var next : Node? = head!.next!;
    
    while head!.next != nil {
        current.next = pre;
        pre = current;
        current = next!;
        next = current.next;
    }

}

2.链表成环判断
/*
如果链表成环,只能是O型环,或者6形状环。 如果是O型的环,一个指针p站在原地不动,一个指针q出去跑一圈,一定等遇到q,但如果是6形状的环,前种方法就不行了。因此,p和q就都要出去跑圈,如果,让q跑快一点,p跑慢一点,如果链表成环的话,无论是O型状的,还是6型状的,q肯定会在某个时间节点,比q多跑一圈,而追上p,及p和q从header出发后,首次相遇。
*/

func hasCircle(head : Node?) -> Bool {
    
    if (head == nil || head?.next == nil) {return false};
    
    var current : Node? = head;
    var another : Node? = head!.next;
    while current?.next != nil {
        if current! === another! {
            return true;
        } else {
            current = current?.next;
            another = another?.next?.next;//让一个点跑快一点。。。。
        }
    }
    
    return false;
}

//链表相交
/*
链表的相交,一定是Y型相交,既然是Y型相交,必定有相同的trail,如果有相通的trail,则相交。相交后找交点 ,遍历两个链表的同时,记录长度,max链表长-min链表长 = n,长链表和短链表一起向下找相同元素,长链表从第n个节点开始,找到第一个相同的节点。

*/

func findSameNode (head1:Node?, head2:Node?) -> Node? {
    if head1 == nil || head2 == nil {return nil}
    
    var length1 : Int = 1;
    var length2 : Int = 1;

    var p : Node = head1!;
    var q : Node = head2!;
    
    while p.next != nil || q.next != nil {
        if (p.next != nil) {
            length1 += 1;
            p = p.next!;
        }
        
        if (q.next != nil) {
            length2 += 1;
            q = q.next!;
        }
    }
    
    if (p === q) {
        //有共同的尾部
        let sub : Int = abs(length1 - length2);
        
        var maxList : Node = length1>length2 ? head1! : head2!;
        var minList : Node = length1>length2 ? head2! : head1!;
        for _ in 1...sub {
            maxList = maxList.next!;
        }
        
        for _ in 1...max(length2, length1) {
            
            if (maxList === minList) {
                return maxList;
            } else {
                if maxList.next != nil && minList.next != nil {
                    maxList = maxList.next!;
                    minList = minList.next!;
                } else {
                    return nil;
                }
               
            }
        }
        
    } else {
        //没有共同的尾部,没有相交
        return nil;
    }
    
    return nil;
}

//给定链表的头指针和一个节点指针,在O(1)时间删除该节点
/*
主要思想都是「狸猫换太子」 将给定节点的值改为,该节点next的值,将该节点next指针设为next的next节点。

*/

//求链表倒数第k个节点
/* 题目描述:输入一个单向链表,输出该链表中倒数第k个节点,链表的倒数第0个节点为链表的尾指针。

分析:设置两个指针 p1、p2,首先 p1 和 p2 都指向 head,然后 p2 向前走 k 步,这样 p1 和 p2 之间就间隔 k 个节点,最后 p1 和 p2 同时向前移动,直至 p2 走到链表末尾。
*/

//求链表的中间节点,要求时间复杂度是O(n),遍历不得超过一次。

/*
受上面一题的启发,应该也可以只用两个指针同时遍历列表,如果指针p,指针q,同时从起点出发,如果q的速度是p的2倍的时候,那么当q到达终点的时候,p刚好才走到全程的一半。

及 q在遍历时每次走两步,q = q.next.next ,p走一步,p = p.next
*/

//找到环的入口点
//输入一个单向链表,判断链表是否有环。如果链表存在环,如何找到环的入口点?

/*
又是环的问题,环的问题一般都可转化到数学里的,同向运动的相遇问题,p,和q,同时出发,如果q比p跑得快,那么总有一时间点,q会比p跑一圈而相遇。那么可以设p的速度是1⃣️,q的速度是2⃣️,在遍历链表的过程中记录p走过的路程,及节点数n,q走过的路程及是2n。 此时可以得处环状的长度是n。得到环的长度后,可以进入第二步,找环的入口。

让p,q都回到链表的初始位置,q先行n步,然后,p,q同时以1⃣️的速度向下一个节点移动,直到,p和q相遇,此时p,q所处的位置即环的入口。
*/

//8. 扩展:链表有环,如何判断相交
//题目描述:上面的问题都是针对链表无环的,那么如果现在,链表是有环的呢?上面的方法还同样有效么?

/*
此时可以设想一下,如果两个链表相交,且两个链表相交,那么两个链表一定共同一个环。那么第一步,按照上述方法,假设链表A,B
1⃣️:利用p,q指针找到链表A的环入口 E1,和环的长度n。由于A和B同环,B的环长也是n,记录链表A的全长。

2⃣️:让p,q同时都在B链表的head处,让q先行n步,若在N步,然后p,q同时,同速度向后移近,在p,q相遇前,若q能等于E1,那么A、B链表相交于环部。否则,p,q,相遇时,记录链表B的长度。进入第3⃣️步

3⃣️:计算链表A,B的长度差 X,让p,q分别从A,B链表的head出发,长链表的指针先行X步,然后p,q同时移动,在p到达E1点前,如果p==q,那么AB相交于环外节点,否则无交点。
*/

你可能感兴趣的:(单链表基础操作题记录)