1.输出链表中倒数第k个节点
思想:先让快指针向前走k-1步,然后快慢指针同时开始走,快指针走到末尾时,慢指针正号走到倒数第k个节点。这里的k是从1开始的。
易错点:忘记把k设置为unsigned类型;向前走k步的时候忘记判断
ListNode* FindKtoTail(ListNode * phead,unsigned int k)
{
if(phead == NULL || k == 0)//(1)整个链表为NULL,(2)k不能为0,k时无符号整数,k-1是一个非常大的无符号整数,要不停循环
return NULL;
ListNode * pfast = phead;
ListNode * pslow = NULL;//这里第一次置空,不要忘了
for(unsigned int i=0;i <k-1;++i)
{
if(pfast->pnext != NULL)//如果k大于链表节点数,会导致pfast越界,这里要判断
{
pfast = pfast->pnext;
}
else //如果k大于链表节点数,直接return
{
return NULL;
}
}
pslow = phead;//因为上面置空了,所以这里要重新指一下
while(pfast->pnext != NULL)
{
pfast = pfast->pnext;
pslow = pslow->pnext;
}
return pslow;
}
2.删除链表第k个节点 方法同上,快慢指针
bool DeleteTailToK(ListNode* phead,unsigned int k)
{
if(phead == NULL || k == 0) return false;
ListNode* pfast = phead;
ListNode* pslow = NULL;
for(int i=0;i<k-1;++i)
{
if(pfast->pnext != NULL)
{
pfast = pfast->pnext;
}
else
{
return false;
}
}
pslow = phead;
while(pfast->pnext != NULL)
{
pfast = pfast->pnext;
pslow = pslow->pnext;
}
DeleteListNode(phead,pslow);//直接调用删除的方法
return true;
}
3.求链表的中间节点 方法和上面差不多,快慢指针
思想:快指针走两步,慢指针走一步,快指针到达末尾时,慢指针到达中间节点
ListNode* ListMidNode(ListNode* phead)
{
if(phead == NULL) return 0;
ListNode *pfast = phead;
ListNode *pslow = phead;
while(pfast->pnext != NULL)
{
pfast = pfast->pnext;
if(pfast->pnext != NULL)
{
pfast = pfast->pnext;
}
pslow = pslow->pnext;
}
return pslow;
}
4.判断链表是否有环
用快慢指针,快指针每次走两步,慢指针走一步,总有一个时刻两者会碰上
ListNode* IsListLoop(ListNode* phead)
{
if(phead == NULL) return NULL;
ListNode* pfast = phead;
ListNode* pslow = phead;
while(pfast->pnext != NULL)
{
pfast = pfast->pnext;
if(pfast->pnext != NULL)//快指针走两步
{
pfast = pfast->pnext;
}
pslow = pslow->pnext;//慢指针走一步
if(pfast == pslow)//快慢指针碰面,说明有环
{
return pslow;
}
}
return NULL;
}
5.求环的入口地址
调用上面判断链表是否有环,有环则求环的入口地址
由公式推导,从链表头到入口地址和长度和从判断链表是否有环的相遇点到入口地址的长度是相同的,所以两个指针同时开始走,再一次相遇的地方即为入口地址
ListNode* FindLoopAddr(ListNode* phead)
{
ListNode* pslow = IsListLoop(phead);
if(pslow == NULL) return NULL;//没环
ListNode* pfast = phead;
while(pfast != pslow)//相遇则跳出
{
pfast = pfast->pnext;
pslow = pslow->pnext;
}
return pslow;//相遇点即入口地址,返回
}
6.求环的长度,直接把判断是否有环的代码调用一下,然后计算长度
int GetLoooLength(ListNode* phead)
{
ListNode* pmeet = IsListLoop(phead);
if(pmeet == NULL) return 0;
ListNode *pslow = pmeet->pnext;
int count = 1;//从相遇点的下一个节点开始走,所以把count初始化为1
while(pslow != pmeet)
{
pslow = pslow->pnext;
count++;
}
return count;
}
7.判断两个链表是否相交,返回第一个交点
【如果两个单链表有交点,那么从交点开始,它们的pnext都指向同一个节点,不可能再分叉】
第一种方法是把两个链表连起来,判断是否有环,判断有环的代码上面有
第二种方法是求出链表1的长度,求出链表2的长度,然后求出长度差len,让快指针先走len步,再让快慢指针同时走,时间复杂度O(m+n)
ListNode* List2InterPoint(ListNode* phead1,ListNode* phead2)
{
int len1 = 0;
int len2 = 0;
int len = 0;
ListNode *p1 = phead1;
ListNode *p2 = phead2;
for(;p1 != NULL;p1 = p1->pnext)
len1++;
for(;p2 != NULL;p2 = p2->pnext)
len2++;
ListNode* pfast = phead1;
ListNode* pslow = NULL;
if(len1 > len2)//分情况计算长度之差
{
len = len1-len2;
pfast = phead1;
pslow = phead2;
}
else
{
len = len2-len1;
pfast = phead2;
pslow = phead1;
}
for(int i=0;i<len;++i)//快指针先走len步
pfast = pfast->pnext;
while(pfast != pslow)//快慢指针同时走
{
pfast = pfast->pnext;
pslow = pslow->pnext;
}
return pslow;
}
8.合并两个有序链表
(1)用递归合并
ListNode* Merge2SortedList(ListNode* phead1,ListNode* phead2)
{
if(phead1 == NULL) return phead2;
if(phead2 == NULL) return phead1;
ListNode* pmerge = NULL;
if(phead1->mval < phead2->mval)//如果phead1小
{
pmerge = phead1;//phead1作为新的头节点
pmerge->pnext = Merge2SortedList(phead1->pnext,phead2);//递归
}
else //如果phead2小或者两个相等
{
pmerge = phead2;//phead2作为新的头节点
pmerge->pnext = Merge2SortedList(phead1,phead2->pnext);//递归
}
return pmerge;
}
(2)不用递归 合并两个有序链表
ListNode* Merge2SortedList2(ListNode* phead1,ListNode* phead2)
{
if(phead1 == NULL) return phead2;
if(phead2 == NULL) return phead1;
ListNode* p1 = phead1;
ListNode* p2 = phead2;
ListNode* plist = (ListNode*)malloc(sizeof(ListNode));//新链表头节点,一会要返回
ListNode* newphead=plist;//新链表
while(p1 != NULL && p2 != NULL) 合并也需要合并最后一个节点,所以要走到最后
{
if(p1->mval < p2->mval)
{
newphead->pnext = p1;//(1)p1小则把p1作为头节点
p1 = p1->pnext;
newphead = newphead->pnext;
}
else
{
newphead->pnext = p2;//(2)p2小则把p2作为头节点
p2 = p2->pnext;
newphead = newphead->pnext;
}
}
if(p1 != NULL)//(3)p1如果没有遍历完剩下的全部放入新链表
{
newphead->pnext = p1;
}
if(p2 != NULL)//(4)p2如果没有遍历完剩下的全部放入新链表
{
newphead->pnext = p2;
}
return plist;
}