假设单链表节点为
struct node{ int data; struct node* next; }
链表名为list,已知头结点为head
一. 单链表反转
1. pre-cur-next法,最常用的方法
2. 前插法,实质就是在原链表的基础上新建一个链表,每次新节点都插到head后面,而不是最后面
structnode* p = head; struct node* q = NULL; head = NULL; while(p) { q = p-> next; p-> next = head; head = p; p = q; }
二. 单链表逆序输出
有很多方法。
1. 栈,每读一个,将节点地址压栈,读完后,弹栈读出
时间复杂度, 空间复杂度
2. 递归,先递归输出当前节点下一结点为头结点的单链表,然后输出当前节点
时间复杂度, 空间复杂度,适合比较短的单链表,不然递归深度太深容易爆栈
3. 先逆序,先求单链表的逆序,然后再按照新的逆序链表顺序输出。
其中逆序有两种方法,pre-cur-next法和前插法。
时间复杂度, 空间复杂度,要遍历两遍链表,而且还破坏了其结构,若要保持结构就要再反转一次,共3次遍历
三. 判断单链表里面是否有环
算法的思想是设定两个指针p, q,其中p每次向前移动一步,q每次向前移动两步。那么如果单链表存在环,则p和q相遇;否则q将首先遇到null
原理:
假设p走了k步就和q相遇了
则 ,即 ,k=n,即p必在最后一个节点与q相遇。若无环,则q走了n步,p走了n/2步,q就遇到了遇到null
这里一个简单的理解是,p和q同时在操场跑步,其中q的速度是p的两倍,当他们两个同时出发时,p跑一圈到达起点,而q此时也刚好跑完两圈到达起点。
如果p,q不是同一起点出发,例如p到达 时,q到达
那么如果有环,则,即k=n-m,当,k=n-m时,p,q相遇
四. 找链表中第一个在环里的节点
如上图,p,q同时从1出发,当p到达i之后,q在2i,把1,2,…i-1删除,剩下环,那就想当与p,q不是同一起点出发,有上面“三”中最后的结论可知,p,q必将交与n-i,(n是环的节点数),在换上n-i到0是i步,从红色起点0开始到i处也是i步,所以在找到交点A之后,让p从链表起点出发,q从A出发,当p,q相等的时候,那个节点就是环的第一个节点
总结:
1. p,q 同时从起点出发,p每次走一步,q每次走两步
2. 如果q走到了null,终止,无交点,否则一直到p,q相遇
3. p,q相遇时,p回到起点,q不动,都每次走一步,直到p,q相遇,相遇的点就是交点
五. 判断两个单链表是否交?第一个交点在哪里?
1. 因为如果相较,则最后一个节点肯定是相同的,所以,只有两个链表都找到最后一个节点,看是否相同
2. 第一个交点问题有两种解法
六. 一是转化为上面的找环的第一个节点,如下图,将一个链表的尾部链接到另一个的头部,就形成了上面的“找链表中第一个在环里的节点”问题了。
二是直接先计算两个链表的长度,len1,和len2,假设len1>len2,p从L1的len1-len2那个节点开始,q从L2的其实节点开始,两个指针依次后移,第一次两个指针相等的地方就是交点