给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4] 输出:[2,1,4,3]
本题基本思路是隔两步执行一次交换的操作,操作的方法可以是交换数值或者交换指针,对于交换数值来说,其实就是swap(a,b);对于交换指针来说,需要使用虚拟头结点的方法,因为需要知道要交换元素的前一个元素是啥,继而连接到其下一个元素,另外,按照图中所示,当虚拟头结点连接到2前,要把1、3都保存下来,因为链路会断。
// 交换数值法
class Solution {
public:
ListNode *swapPairs(ListNode *head)
{
if (head == nullptr || head->next == nullptr)
return head;
int tmp = 0, pos = 0;
ListNode *cur = head;
while (cur != nullptr && cur->next != nullptr) {
if (pos % 2 == 0) {
tmp = cur->val;
cur->val = cur->next->val;
cur->next->val = tmp;
}
cur = cur->next;
pos++;
}
return head;
}
};
// 交换指针法
class Solution {
public:
ListNode *swapPairs(ListNode *head)
{
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode *cur = dummyHead;
while (cur->next != nullptr && cur->next->next != nullptr) {
ListNode *tmp1 = cur->next; // 以示例一为例,要把1,3的node给先存下来,因为链路会断
ListNode *tmp2 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = tmp1;
cur->next->next->next = tmp2; // 以示例一为例,要摆成2-1-3的阵型
cur = cur->next->next; // 每一步要挪两格
}
return dummyHead->next;
}
};
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
初看题目,第一步想的是翻转链表,然后用n--找到对应元素删除,接着再翻转回来,这样做太麻烦,看到讲解视频后发现快慢指针的方法非常巧妙,核心就是利用慢指针比快指针慢n步的思想,将快慢指针同时遍历,当快指针到链表尾部时,慢指针就到了位置n,这里还添加了虚拟头结点,感觉以后遇到找链表倒数位置的题目都可以用这种方法,总结步骤如下:
1、定义快慢指针和虚拟头结点,都从虚拟头结点开始
2、快指针先走n+1步,因为如果要删除元素的必须找到其上一个节点(慢指针)
3、遍历,快慢指针同时移动,当快指针==null时结束
4、删除元素,最稳妥的方式是用 tmp = 该节点,然后删除tmp
class Solution {
public:
ListNode *removeNthFromEnd(ListNode *head, int n)
{
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode *fast = dummyHead;
ListNode *slow = dummyHead; // 快指针先走n步,然后快慢指针一起走,当快指针指向nullptr时,慢指针指向倒数第n个节点
n++; // 要让slow等于相对应节点的前一个节点
while (n-- && fast != nullptr)
fast = fast->next;
while (fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
ListNode *tmp = slow->next;
slow->next = slow->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next;
}
};
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8'
最初想用循环两个链表暴力解,看完讲解视频后发现可以先将两个链表的长度求出来,然后把长链表的head并其短链表的head,接着head一起移动,当两个链表指针相等时则返回(注意,必须是指针相等而非数值相等),且必须先判断是否两个是否相等再移动
// 移动指针解
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
ListNode *countA = headA;
ListNode *curA = headA;
ListNode *countB = headB;
ListNode *curB = headB;
int sizeA = 0, sizeB = 0;
// 计算headA和headB长度
while (countA != NULL) {
countA = countA->next;
sizeA++;
}
while (countB != NULL) {
countB = countB->next;
sizeB++;
}
if (sizeA >= sizeB) {
int minA = sizeA - sizeB;
while (minA--)
curA = curA->next; // 如果headA长度大于B,则把headA移动懂和B末尾对齐的位置
while (curA != NULL) {
if (curA == curB)
return curA; // 注意,这里是指针相等而不是数值相等
curA = curA->next;
curB = curB->next;
}
} else {
int minB = sizeB - sizeA;
while (minB--)
curB = curB->next; // 同上,headB大于A的操作
while (curB != NULL) {
if (curB == curA)
return curB;
curA = curA->next;
curB = curB->next;
}
}
return NULL;
}
};
// 暴力解
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
ListNode *cur = headA;
while (cur != NULL) {
ListNode *cur1 = headB;
while (cur1 != NULL) {
if (cur == cur1)
return cur1; // 注意,必须是指针相等而不是数字相等
cur1 = cur1->next;
}
cur = cur->next;
}
return NULL;
}
};
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点,即元素2
这题是完全懵了,没有任何思路,看完讲解视频后发现需要用快慢指针,核心是一个数学表达式,下面列出分析步骤:
1、设置快慢指针,快指针每次走两步,慢指针每次走一步,这样快指针只比慢指针多走一步,保证在套圈是不至于遗漏慢指针
2、设从头结点出发到环起点长度为x,在环内两指针时相遇长度为y,环内从相遇位置到环起点位置为z,如下图
3、可以得出,相遇时慢指针走过的路程为x+y,快指针走过的路程为x+y+n(y+z)(因为快指针可能在环里走了n圈),又因为快指针速度是慢指针两倍,则路程也是两倍,即:
2(x+y) = x +y+n(y+z) -> x = n (y + z) - y -> x = (n - 1) (y + z) + z
从上述等式中可以看出,x比z多了n-1个环的路程,也就是说,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是环形入口的节点,那么在两指针相遇时可以设pos1 = head; pos2 = 相遇时节点,pos1和pos2再相遇时就是环的入口
class Solution {
public:
ListNode *detectCycle(ListNode *head)
{
ListNode *fast = head;
ListNode *slow = head;
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next; // 快指针走两步,慢指针走一步,这样快比慢每次只多一步,在套圈的过程中不会漏掉慢指针
if (fast == slow) { // 如果快慢指针相等,证明有环
ListNode *pos1 = fast;
ListNode *pos2 = head;
while (pos1 != pos2) {
pos1 = pos1->next;
pos2 = pos2->next;
} // 有环证明从起点和快慢指针相遇的位置同时出发一定能再次相遇,这时相遇点就是环的起点
return pos1;
}
}
return NULL;
}
};