C/C++中链表操作
刷面试宝典和剑指offer过程中,链表的操作是非常重要的一块儿,这里把遇到的问题汇总一下,值得注意的是,链表的很多技巧都是通过前后指针实现的。
(一)链表的倒数第K个节点(从1开始计数)
如果知道链表的长度len,就比较容易了,倒数第k个节点就是正数第len-k+1个节点,直接遍历就行了。如果len是未知的,当让也可以先遍历一次整个链表数出长度,然后在从头开始找到第len-k+1个,这样其实遍历了两次链表。
但是,能不能只遍历一次就找到倒数第k个节点呢。当然了,可以使用前后两个指针,开始时,前边的指针比后边的指针超前 K-1 步,然后两个指针同时向后移动,当前边的指针到达尾节点时,后边的指针指的就是倒数第k个节点。
struct Node{
int v;
Node* next;
};
Node* FindLastKth(Node* phead, int k){
if(phead == NULL || k == 0)
return NULL;
//前后两个指针
Node* pAhead = phead;
Node* pBehind = phead;
//pAhead先走k-1步
for(int i = 0; i < k-1; i++){
//
if(pAhead->next != NULL)
pAhead = pAhead->next;
else
return NULL;
}
//两个指针同时向后移动
while(pAhead->next != NULL){
pAhead = pAhead->next;
pBehind = pBehind->next;
}
return pBehind;
}
有几处需要处理的细节:
1. 输入的链表为空
2. k为0,或者 k > len
3. 函数在特殊情况返回NULL,如果在主函数中有数出节点值的操作需要判断是否是NULL。
(二)链表中是否存在环
同样考虑前后(快慢)指针的技巧:一个指针每次走一步,另一个每次走两步,如过存在环,那么两个指针肯定能相遇;如果走的快的指针走到了尾节点两指针没有相遇,则不存在环。
bool HasCircle(Node* head){
if(head == NULL)
return false;
Node* pFast = head;
Node* pSlow = head;
while(pFast->next != NULL){
pFast = pFast->next;
if(pFast->next != NULL){
pFast = pFast->next;
pSlow = pSlow->next;
if(pFast == pSlow)
break;
}
else
break;
}
if(pFast == pSlow)
return true;
else
return false;
}
(三)链表的中间节点
如果链表的节点个数是偶数,可以输出中间两个的任意一个。
这个问题同样可以使用快慢指针,一个指针每次走两步,另一个每次走一步。当快指针到达末尾时,慢指针指向的就是中间节点。
Node* Find_mid_node(Node* head){
if(head == NULL)
return NULL;
Node* pAhead = head;
Node* pBehind = head;
while(pAhead->next != NULL){
pAhead = pAhead->next;
if(pAhead->next != NULL){
pAhead = pAhead->next;
pBehind = pBehind->next;
}
}
return pBehind;
}
(四)链表中环的入口节点
一个链表中存在环,找出环的入口节点。
如果知道环中节点的个数n,可是使用前后指针,前边的指针先后n步,然后两个指针同时向前走。当两个指针相遇时,指向的就是环的入口节点。
怎么得到环中节点的个数呢?可以借鉴(三)的方法,找到环中的任意一个节点,然后用一个指针从该节点遍历,当再次遍历的该节点时,正好走了一圈,可以输出节点个数。
//找到环中的任意节点,如果不存在环,返回NULL
Node* Find_meet_node(Node* head){
if(head == NULL)
return NULL;
Node* pFast = head;
Node* pSlow = head;
while(pFast->next != NULL){
pFast = pFast->next;
if(pFast->next != NULL){
pFast = pFast->next;
pSlow = pSlow->next;
if(pFast == pSlow)
break;
}
else
break;
}
if(pFast == pSlow)
return pFast;
else
return NULL;
}
//数环中节点个数,找到入口
Node* EntryOfCircle(Node* head){
if(head == NULL)
return NULL;
Node* meetingNode = Find_meet_node(head);
if(meetingNode == NULL)
return NULL;
//计数,环中节点个数
int nums=1;
Node* p = meetingNode;
while(p->next != meetingNode){
p = p->next;
nums++;
}
//前后指针,遍历链表
Node* pA = head;
Node* pB = head;
for(int i=0; i < nums; i++)
pA = pA->next;
while(pA != pB){
pA = pA->next;
pB = pB->next;
}
return pA;
}