算法学习(六)链表问题总结,相交,成环
求链表倒数第K个结点
题目描述:
输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第0个结点为链表的尾指针。
分析:
首先链表是一个顺序结构,不像数组寻址比较方便,链表一般都是通过遍历来寻址,所以碰到倒数这种情况,转化为求L-K个结点,L为链表长度,所以你需要先遍历,得到链表的长度,然后再遍历找到第L-K个结点。这样是不是有点low啊,是的,下面就是经典的双指针方法,这里要再次强调下链表的一个特性,特指单向链表,最后一个结点Lastnode ->next = NULL,这个是链表使用的关键点,可以作为一个标志位,作为循环结束的标志。p1 指向头指针,p2指向和p1相隔K的结点,然后两个指针一起向后移,直到p2到达链表尾部,这时候,p1的结点就是倒数第k个结点。
//代码中顺便写了链表的增删查功能
#include.h>
#include
typedef struct Node * Position;
typedef Position List;
struct Node
{
int value;
Position next;
};
void Insert(Position P,List L,int x)//List L 表示头指针
{
Position temp;
temp = (Node *)malloc(sizeof(struct Node));
temp->value = https://www.it610.com/article/x;
temp->next = P->next;
P->next = temp;
}
Position Find(List L,int x)
{
Position start;
start = L->next;
while(start != NULL && start->value != x)
start = start->next;
return start;
}
int IsEmpty(List L)
{
return L->next == NULL;
}
void Delete(List L,int x)
{
Position P,temp;
P = L;
while(P->next != NULL && P->next->value != x)
P= P->next;
if(P->next != NULL)
{
temp = P->next;
P->next = temp->next;
free(temp);
}
}
//查找倒数第K个结点
Position FindListBackNode(List L ,int k)
{
Position P1,P2;
assert(k>=0);
P1 = P2 = L;
int i;
for(i = k;
i> 0 && P2 != NULL ;
i--)//使p2和p1相隔k个结点
{
P2 = P2->next;
}
if(k > 0)
return NULL;
while(P2 != NULL)
{
P1 = P1->next;
P2 = P2->next;
}
return P1;
}
判断两个链表是否相交
题目描述:给出两个单向链表L1,L2 ,判断两个链表是否相交,假设两个链表都无环。
分析:
相交意思就是是否存在结点相同,遍历链表L1的每个结点,判断是否在第二个链表中,时间复杂度为O (length(L1) * length(L2))。
你会发现这个就是个查找问题,查找最快的当然是hashtable,先遍历链表L1,构造hash表,然后遍历链表L2,判断L2结点是否能再hash表中找到,时间复杂度为O(length(L1) + length(L2)),不过空间复杂度为O(length(L1))。
如果两个无环链表有交点,那么这个交点之后的所有结点都相同,当然最后的结点也一定是共有的,so,我们直接判断最后一个结点时候相等就好了,先遍历链表L1,找到尾结点,再遍历遍历L2,找到尾结点,然后比较,相同就相交,时间复杂度为O(length(L1) + length(L2)),空间复杂度为O(1)。
那么问题来了,如果链表有环呢?
有环的话,上面的判断尾结点方法就行不通了,
如果一个带环一个不带环呢,当然肯定不相交了。
所有问题就转化为先判断是否有环?
链表是否有环?
文章图片
用画图画的,凑合着看吧,这个就是带环的链表,首先要区分循环链表和带环链表的区别。循环链表就是首尾相连,相当于一个圆,带环链表意思是没有结尾,不会指向NULL ,感觉循环链表应该是有环链表的一种特殊情况,此处我们去掉循环链表这种特殊情况,判断链表是否有环,可以联想到以前数学中的追赶问题,两个人从相同起点出发,一个速度快,一个速度慢,经过一段时间,速度快的从追上速度慢的人,比速度慢的人多跑了一圈,这个逻辑可以用在判断链表是否有环,一个指针P1每次移动一步,一个指针p2移动两步,如果这个两个指针相等(而且不等于null),意思就是p2绕了n圈之后再次追上p1。
bool isCircle(List L,Positon lastnode,Position circleNode)
{
Position fast= L->next;
//快的要先出发
Position slow = L;
while(fast != slow && fast != NULL && slow != NULL)
{
if(fast->next != NULL)
fast = fast->next;
//快的比慢的多走一步
if(fast->next == NULL)
lastnode = fast;
//最后的尾结点
if(slow->next == NULL)
lastnode = slow;
fast = fast->next;
slow = slow->next;
}
if(fast == slow && fast != NULL && slow != NULL)
{
circleNode = fast;
//两个快慢指针第一次相遇的地点,肯定在环上。
return true;
}
else
return false;
}
再次回到判断链表是否有交点上,
先判断是否有环,
如果无环,判断尾结点是否相等,
如果有环,遍历L1环上的每个结点是否和L2环上某个结点相等
bool detect(List L1,List L2)
{
Position circleNode1;
//环上的结点
Position lastNode1 ;
Position circleNode2;
Position lastNode2;
bool iscircle1 = isCircle(L1,circleNode1,lastNode1);
bool iscircle2 = isCircle(L2,circleNode2,lastNode2);
if(iscircle1 != iscircle2)
return false;
else if(!iscircle1 && !iscircle2)
return lastNode1 == lastNode2;
else
{
Position temp = circleNode1->next;
while(temp != circleNode1)
{
if(temp == circleNode2)
return true;
temp = temp->next;
}
return false;
}
return false;
}
链表成环,环的入口在哪?
文章图片
又来秀画图了,viso懒得用,电脑打开都费劲
下面是数学时间:
链表的总长度为L,链表起点为A,环入口为B,第一次相遇的点为C(就是上面那个快慢指针相遇的点),A到B 的长度为a,B到C的长度为x,环一圈的长度为r。
fast 和slow指针相遇的时候,slow肯定没有遍历完链表,fast此时已经在环里转了n圈,此时slow走了s步,那么fast已经走了2s步(是慢的两倍),所以可以建立表达式:
s+nr = 2s
s = nr
因a+x = s, r = L-a
所以 a+x = nr
a+x = (n-1)r+r = (n-1)r +L-a
a = (n-1)r + L-a-x
(L-a-x)是C 到B的距离,相遇点到环入口的距离
可以知道,从链表头到环入口等于(n-1)循环内环+相遇点到环入口,
所以P1从链表头出发,而P2从相遇点出发,当两者再次相遇时的结点就是环入口。
【算法学习(六)链表问题总结,相交,成环】待续,后续会继续添加
推荐阅读
- 任时光绽放成六月繁花
- 赢在人生六项精进二阶Day3复盘
- 财商智慧课(六)
- 由浅入深理解AOP
- 继续努力,自主学习家庭Day135(20181015)
- python学习之|python学习之 实现QQ自动发送消息
- 画解算法(1.|画解算法:1. 两数之和)
- 六步搭建ES6语法环境
- Guava|Guava RateLimiter与限流算法
- 一起来学习C语言的字符串转换函数