链表
24. 两两交换链表中的节点
题意:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即只能进行节点交换)
示例:
思路:本题其实和昨天链表反转非常类似,我们使用三指针的方法,按照一定的链表重构顺序即可完成一次相邻节点之间的交互。其次,我们需要注意:1.本题是两个相邻节点交互,例如,1->2->3->4,为1和2交互,3和4交互;但是,如果出现1->2->3,那么我们只需要交换1和2即可,无法匹配的节点不作改变。2.为了使返回时节点固定,因此我们可以创建一个头节点,返回的就是头节点的下一个节点,就不需要做额外标记了;并且在之后的重构中,头节点也能为我们省去代码。
C++代码:
ListNode* swapPairs(ListNode* head) {
ListNode* cur=new ListNode(0);//创建头节点
cur->next=head;
ListNode* rear=cur;//让一个节点代替cur向后移动
while(rear->next&&rear->next->next)//这里就可以排除mid和front越界的情况
{
ListNode* mid=rear->next;
ListNode* front=rear->next->next;
mid->next = front->next;//链表重构的顺序不能变
front->next = mid;
rear->next = front;
rear=mid;//只需要将开头节点改变即可
}
return cur->next;//此时返回的就是头节点的下一个节点
}
19. 删除链表的倒数第 N 个结点
题意:给你一个链表,删除链表的倒数第n个结点,并且返回链表的头结点。
示例:
思路:先创建头节点,目的是为了将头结点的删除和其他节点删除同化为一种算法;其次是定义两个节点slow和fast,slow是需要删除节点的前一个节点,fast是用于计算slow走多少距离,两指针的距离就是链表中节点到nullptr的距离,这样在fast走到nullptr时,slow正好走到需要删除节点的前一个节点。
C++代码:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* cur=new ListNode(0);//创建一个头节点
cur->next=head;
ListNode* slow=cur;//用于记录需要删除节点的前一个节点
ListNode* fast=head;//用于计算slow需要走动的距离
while(n--)//计算好slow到节点的距离
{
fast=fast->next;
}
while(fast)//当fast为nullptr时,就说明slow走到了要删除节点的前一个节点
{
slow=slow->next;
fast=fast->next;
}
slow->next=slow->next->next;
return cur->next;
}
面试题 02.07. 链表相交
题意:给你两个单链表的头节点headA和headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null 。
示例:
思路:本题能想到两种思路,1.按照遍历数组的方式,找出两个数组相同的元素,此时就是两个for循环,以一条链表作为基准,从另一条链表中寻找是否有与该链表相交的节点,若有则直接输出相交节点;2.分别遍历两条链表,统计它们的节点个数,然后让长的链表起始位置向前移动,之后两指针分别向后移动,若出现相等地址,则说明两链表相交,返回即可。
方法1代码:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* cur=headA;
while(cur)//两次遍历,从A链表中找出与B链表相交的节点,并返回
{
ListNode* tmp=headB;
while(tmp)
{
if(tmp!=cur)
{
tmp=tmp->next;
continue;
}
return cur;
}
cur=cur->next;
}
return cur;//若找到nullptr还没有,则说明没有交点
}
方法2代码:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int sumA=0,sumB=0;
ListNode* curA=headA;
ListNode* curB=headB;
while(curA)//算出两链表的节点大小
{
sumA++;
curA=curA->next;
}
while(curB)
{
sumB++;
curB=curB->next;
}
curA=headA,curB=headB;
if(sumA>sumB)//使两节点处于平等的起跑线
{
int poor=sumA-sumB;
while(poor--)
{
curA=curA->next;
}
}
else
{
int poor=sumB-sumA;
while(poor--)
{
curB=curB->next;
}
}
while(curA)
{
if(curA==curB)
{
return curA;
}
curA=curA->next;
curB=curB->next;
}
return nullptr;
}
142. 环形链表 II
题意:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例:
思路:本题我有两种思路供大家参考:1.使用unordered_map记录每个指针出现次数,若出现次数等于2,则说明该指针指向的地址就是链表成环的入口,否则链表就不成环,返回nullptr;该方法的优点就在于代码量小,思路非常清晰。2.快慢指针法:定义两个指针fast和slow,规定fast一次走两格,slow一次走一格,直到它们相交,此时的交点并非起始位置,很有可能是环中的交点,这里我们画图说明:
此时可以算得:慢指针移动距离为X+Y,快指针移动距离为X+Y+n(Y+Z),由速度距离公式得:慢指针移动距离*2=快指针移动距离,因此X+Y=n(Y+Z)。我们需要得出X得距离,因此X=(n-1)(Y+Z)+Z;由于Y+Z是环得周长,因此当n=1时,X=Z。所有我们可以得出,当指针第一次相遇后,慢指针回到起点,两指针以相同的速度前进,再次相遇的地址就是环的入口。
方法1代码:
ListNode *detectCycle(ListNode *head) {
unordered_map<ListNode*,int> map;
while(head)
{
map[head]++;
if(map[head]==2)
{
return head;
}
head=head->next;
}
return nullptr;
}
方法2代码:
ListNode *detectCycle(ListNode *head) {
if(head==nullptr||head->next==nullptr)
{
return nullptr;
}
ListNode* slow=head;
ListNode* fast=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)//第一次相遇,这里也并不需要考虑为nullptr的情况,因此,如果快指针先为空,就说明该链表不成环
{
slow=head;//返回起点,此时快慢指针一起走,相遇的地方就是环的入口
while(slow!=fast)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
}
return nullptr;
}