本文包含链表的以下内容:
1、查找单链表中的倒数第k个结点(剑指offer,题15)
2、合并两个有序的单链表,合并之后的链表依然有序【出现频率高】(剑指offer,题17)
3、单链表的反转【出现频率最高】(剑指offer,题16)
4、从尾到头打印单链表(剑指offer,题5)
5、单链表中,取出环的起始点(剑指offer,题56)
6、判断两个单链表相交的第一个交点(剑指offer,题37)
7、复杂链表的复制(剑指offer,题26)
8、删除链表中重复的结点(剑指offer,题56)
1. 两个指针思想,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点。然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead==NULL||k==0)
return NULL;
ListNode* head1=pListHead;
ListNode* head2=pListHead;
for(int i=0;i<k-1;i++)
{
if(head2->next!=NULL) //防止K的大小比整个链表个数还多
head2=head2->next;
else
return NULL;
}
while(head2->next!=NULL)
{
head1=head1->next;
head2=head2->next;
}
return head1;
}
};
2.归并排序的思想(非递归版本)
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(!pHead1) //检查判断不能少
return pHead2;
if(!pHead2)
return pHead1;
ListNode* pNode=NULL;
ListNode* pHead=NULL;
if(pHead1->val<pHead2->val) //确定头指针
{
pHead=pHead1;
pHead1=pHead1->next;
}
else
{
pHead=pHead2;
pHead2=pHead2->next;
}
pNode=pHead;
while((pHead1!=NULL)&&(pHead2!=NULL)) //开始使用归并的思想
{
if( pHead1->val<pHead2->val)
{
pNode->next=pHead1;
pHead1=pHead1->next;
pNode=pNode->next;
}
else
{
pNode->next=pHead2;
pHead2=pHead2->next;
pNode=pNode->next;
}
}
if(pHead1==NULL) //最后的多余的部分直接指向就可以,因为链表是连接的。
{
pNode->next=pHead2;
}
if(pHead2==NULL)
{
pNode->next=pHead1;
}
return pHead;
}
};
3.采用3个指针的思想(保存,反转,前移)
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pNode=pHead;
ListNode* pPreNode=NULL;
while(pNode!=NULL)
{
ListNode* pNext=pNode->next; //用一个临时指针来保存下一个结点,防止断开后丢失。
pNode->next=pPreNode; //保存完之后,就可以连接翻转,指向前一个结点
pPreNode=pNode; //完成一次翻转后,把结点往前移
pNode=pNext;
}
return pPreNode;
}
};
4.堆栈的思想
class Solution {
public:
vector <int> dev;
// vector<int> printListFromTailToHead(ListNode* head) { //递归解法
// if(head!=NULL)
// {
// if(head->next!=NULL)
// dev = printListFromTailToHead(head->next);
// dev.push_back(head->val);
// }
// return dev;
// }
vector<int> printListFromTailToHead(ListNode* head) { //采用堆栈的思想,后进先出
std::stack<ListNode *>nodes;
ListNode* pNode=head;
while(pNode!=NULL)
{
nodes.push(pNode);
pNode=pNode->next;
}
while(!nodes.empty())
{
pNode=nodes.top();
dev.push_back(pNode->val);
nodes.pop();
}
return dev;
}
//也可以直接先存到dev,在调用翻转函数即可.总共3种方法
};
5.先判断是否有环,如果有环返回相遇点,再统计环内结点的数目n。最后先让快指针走n步,然后快慢指针一起走,相遇的地点就是环的入口处。
class Solution {
public:
ListNode* MeetingNode(ListNode* pHead) //慢指针走一步,快指针走两步,若有环,必在环内相遇,返回该相遇点
{
if(pHead==NULL||pHead->next==NULL)
return NULL;
ListNode* slow=pHead;
ListNode* fast=pHead;
while(slow->next!=NULL && fast->next->next!=NULL)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
return fast;
}
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* meetingNode=MeetingNode(pHead);
if(meetingNode==NULL)
return NULL;
int LoopNodesNum=1; //从相遇节点出发,一边计数一边移动,当再次回到该节点时,就可以得到环中节点数目
ListNode* node=meetingNode;
while(node->next!=meetingNode)
{
node=node->next;
++LoopNodesNum;
}
ListNode* node1=pHead; //先让第一个节点走LoopNodesNum步,随后在一起走,直到相遇的地方就是环的入口点
for(int i=0;i<LoopNodesNum;i++)
node1=node1->next;
ListNode* node2=pHead;
while(node1!=node2)
{
node1=node1->next;
node2=node2->next;
}
return node1;
}
};
6.用到两个指针的思想。因为每个结点只有一个next指针,所以从第一个公共结点开始,之后他们所有结点都是重合的。因此拓扑形状看起来像一个Y,而不是X;
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
int GetLength(ListNode* pHead) //遍历统计链表的各自长度
{
ListNode* node=pHead;
int num=0;
while(node!=NULL)
{
num++;
node=node->next;
}
return num;
}
ListNode* preWalk(ListNode* pHead,int k) //让长的链表先走len1-len2
{
ListNode* node=pHead;
while(k--)
{
node=node->next;
}
return node;
}
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
int len1=GetLength(pHead1);
int len2=GetLength(pHead2);
if(len1>len2)
{
pHead1=preWalk(pHead1,len1-len2);
}
else
{
pHead2=preWalk(pHead2,len2-len1);
}
while(pHead1!=NULL&&pHead2!=NULL&&pHead1!=pHead2)
{
pHead1=pHead1->next;
pHead2=pHead2->next;
}
return pHead1;
}
};
7.复杂链表的复制,采用三个步骤
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
void CloneNodes(RandomListNode* pHead) //复制原始链表的任一节点N并创建新节点N',再把N'链接到N的后边
{
RandomListNode* pNode=pHead;
while(pNode!=NULL)
{
RandomListNode* pClone=new RandomListNode(0); //先进行克隆结点的初始化
pClone->label=pNode->label;
pClone->random=NULL;
pClone->next=pNode->next; //将克隆结点分别插入到相应位置
pNode->next=pClone;
pNode=pClone->next;
}
}
void ConnectSiblingNodes(RandomListNode* pHead) //如果原始链表上的节点N的random指向S,则对应的复制节点N'的random指向S的下一个节点S'
{
RandomListNode* pNode=pHead;
while(pNode!=NULL)
{
RandomListNode* pClone=pNode->next;
if(pNode->random!=NULL)
pClone->random=pNode->random->next;
pNode=pClone->next;
}
}
RandomListNode* SplitNodes(RandomListNode* pHead) //把得到的链表拆成两个链表,奇数位置上的结点组成原始链表,偶数位置上的结点组成复制出来的链表
{
RandomListNode* pNode=pHead;
RandomListNode* pClone=NULL;
RandomListNode* pCloneHead=NULL;
if(pNode!=NULL) //初始化先将头部分离
{
pClone=pCloneHead=pNode->next;
pNode->next=pClone->next;
pNode=pNode->next;
}
while(pNode!=NULL) //循环依次往后分离
{
pClone->next=pNode->next;
pClone=pClone->next;
pNode->next=pClone->next;
pNode=pNode->next;
}
return pCloneHead;
}
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNodes(pHead);
ConnectSiblingNodes(pHead);
return SplitNodes(pHead);
}
};
8.一个指向前一个节点pre,另一个指向当前节点p,再用一个指针next指向后一个结点,如果遇到相等的节点,next向后移动,pre不动,直到遇到p和next不相等,pre就可以指向next。注意:因为头结点也可能重复被删除,所以新建一个指针newHead指向头结点
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead==NULL||pHead->next==NULL)
return pHead; //注意这里不能返回NULL
ListNode* newHead=new ListNode(-1); //因为头结点也可能重复被删除,所以新建一个指针指向头结点
newHead->next=pHead;
ListNode* pre=newHead; //需要两个指针,一个指向前一个节点preNode,另一个指向当前节点node,
ListNode* p=pHead;
ListNode* next=NULL;
while(p!=NULL && p->next!=NULL)
{
next=p->next;
if(p->val==next->val) //如果当前节点的值和下一个节点的值相等
{
while(next!=NULL && p->val==next->val) //向后重复查找.移动
next=next->next;
pre->next=next; //直到不相等为止,进行连接
p=next;
}
else //如果当前节点的值和下一个节点的值不相等,直接向后移一个
{
pre=p;
p=next;
}
}
return newHead->next;
}
};
参考链接:https://blog.csdn.net/u012129558/article/details/52575543