一、两两交换链表中的节点 24
题目简述:
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
基本思路:
这里需要了解和确定步骤,即改动节点的指针域顺序,这个在链表操作上比较重要,因为要注意操作过程中是否会丢失节点。
以下图为例,这里具体分为四个步骤:
- 步骤一:将cur指向头结点2;
- 步骤二:头节点2指向下一个节点1;
- 步骤三:节点1指向节点3;
- 最后cur指向节点3(下一个过程的开始)。
Note:这里需要注意的是,操作cur时会丢失节点1地址,操作节点2时会丢失节点3地址。所以需要保存节点1和节点3地址---这里我用tmp1和tmp2存储。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* cur = dummyhead; //dummyhead只改动一次,所以需要cur
ListNode* tmp1;
ListNode* tmp2;
while (cur->next != nullptr && cur->next->next != nullptr)
{
tmp1 = cur->next; //记录节点1
tmp2 = cur->next->next->next; //记录节点3
cur->next = cur->next->next; //cur指向节点2
cur->next->next = tmp1; //节点2指向节点1
tmp1->next = tmp2; //节点1指向节点3
cur = cur->next->next; //下一个循环的开始
}
return dummyhead->next;
}
};
二、删除链表的倒数第N个结点 19
题目简述:
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
思路和想法:
①先进行一次遍历,知道链表的长度,之后再while循环到正向的size - n 个节点,将其指向到它要删除的节点的下一个节点。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode();
dummyhead->next = head;
ListNode* cur = dummyhead;
ListNode* cur2 = dummyhead;
ListNode* temp = new ListNode();
int size = 0;
//步骤一
while(cur->next != nullptr){
cur = cur->next;
size++;
}
//步骤二,移动到要删除的节点前一个节点
while(size - n){
cur2 = cur2->next;
n++;
}
//步骤三,进行节点的删除
if(size >= 2){
temp = cur2->next;
cur2->next = cur2->next->next;
delete temp;
}
//删除头结点
else if(size == 1){
dummyhead->next = nullptr;
}
return dummyhead->next;
}
};
②其实可以一次遍历,结合之前所学的知识,我们可以用双指针法来实现,只需做到快慢指针之间的间隔对应为n+1即可。 ----fastIndex指向null,slowIndex为删除节点的前一个节点。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode();
dummyhead->next = head;
ListNode* slowIndex = dummyhead;
ListNode* fastIndex = dummyhead;
ListNode* temp; //临时节点,用于delete
//步骤一,快指针先走n+1步
n++;
while(n-- && fastIndex != NULL){
fastIndex = fastIndex->next;
}
//步骤二:快指针和慢指针一起走,直到快指针到尾节点指向的nullptr
while(fastIndex != nullptr){
slowIndex = slowIndex->next;
fastIndex = fastIndex->next;
}
//步骤三:进行节点的删除
temp = slowIndex->next;
slowIndex->next = slowIndex->next->next;
delete temp;
return dummyhead->next;
}
};
三、链表相交
题目简述:
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal
- 相交的起始节点的值。如果不存在相交节点,这一值为0
listA
- 第一个链表listB
- 第二个链表skipA
- 在listA
中(从头节点开始)跳到交叉节点的节点数skipB
- 在listB
中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA
和 headB
传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例:
题解:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
//步骤一:求得链表A和B的长度
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
//步骤二:定义最长链表A,之后求长度差,移动curA直到与curB同起点
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
//步骤三:遍历curA和curB看他们的地址是否相同
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
四、环形链表II 142
题目简述:
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
思路和想法:
这里有两个问题,判断是否有环以及找到环的入口。这里采用双指针的方法来解决以上的问题。
- 判断有环,即快慢指针相遇了,则有环。
- 找到环的入口,这里有个数学推论,从头节点出发到入口和相遇的节点出发到入口是会相遇的。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fastIndex;
ListNode* slowIndex;
fastIndex = head;
slowIndex = head;
//步骤一,先判断是否有环,快指针走两步,慢指针一步
while(fastIndex != NULL && fastIndex->next != NULL) {
slowIndex = slowIndex->next;
fastIndex = fastIndex->next->next;
//步骤二 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slowIndex == fastIndex) {
ListNode* index1 = fastIndex;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
break;
}
}
return NULL;
}
};