24. 两两交换链表中的节点
题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点_哔哩哔哩_bilibili
解题思路:
struct ListNode* swapPairs(struct ListNode* head) {
struct ListNode *dummyhead = (struct ListNode *)malloc(sizeof (struct ListNode));
//为虚拟头结点分配内存空间
dummyhead -> next = head;
//将dummyhead变为头结点
struct ListNode *cur = dummyhead;
//创建遍历链表的指针
while (cur -> next && cur -> next -> next) {
struct ListNode *tmp1 = cur -> next;
//用于储存cur所指节点的右边第一个节点
struct ListNode *tmp2 = cur -> next -> next -> next;
//用于储存cur所指节点的右边第三个节点
cur -> next = cur -> next -> next;
//将cur所指节点的下一个节点转化为所指节点的右边第二个节点
//此时cur右边的第一个节点由tmp1保存
cur -> next -> next = tmp1;
//将cur所指节点的下一个已经转化为第一个节点的指针指向
//未转化前的cur所指节点的右边第一个节点
cur -> next -> next -> next = tmp2;
//将cur所指节点的下一个的下一个已经转化为第二个节点的指针指向
//cur所指节点的右边第三个节点,也就是由tmp2保存的节点
cur = cur -> next -> next;
//将cur向右移2个节点,并进行下一轮循环
}
return dummyhead -> next;
}
时间复杂度:O(n)
该题总结:这道题的链表操作较为复杂,需要定义两个分别指向cur的右边的第一个节点和第三个节点的临时指针,这点想清楚了之后再用上之前学的虚拟头结点就把问题解决了。
19.删除链表的倒数第N个节点
题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili
解题思路:
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode *dummyhead = (struct ListNode *)malloc(sizeof (struct ListNode));
dummyhead -> next = head;
//为虚拟头结点分配内存空间并使其成为头结点
struct ListNode *fast = dummyhead;
//定义一个快指针
struct ListNode *slow = dummyhead;
//定义一个慢指针
n++;
//让快指针比慢指针多走n+1步
while (n-- && fast) {
fast = fast -> next;
}
//让快指针往右走n+1步
while (fast) {
fast = fast -> next;
slow = slow -> next;
}
//让快慢指针同时右移直到快指针为空
struct ListNode *tmp = slow -> next;
//临时保存需要删除的节点
slow -> next = slow -> next -> next;
//删除目标节点
free(tmp);
//释放目标节点
return dummyhead -> next;
//返回真正的头结点
}
时间复杂度:O(n)
该题总结:这题的关键点在于以另一种思维方式来使用双指针法,使得该题的解题思路从思考怎么让指针指向倒数第N个节点转化为两指针之间的距离问题,需要注意的是两指针的距离是n+1而不是n,因为需要让慢指针指向需要删除的节点的前一个节点,在理解了这些之后题目也就迎刃而解了。
面试题 02.07. 链表相交
题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:该题无视频讲解
解题思路:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
int lenA = 0,lenB = 0;
//初始化两链表长度计数器
struct ListNode *curA = headA;
struct ListNode *curB = headB;
//初始化两链表的遍历指针
while (curA) {
curA = curA -> next;
lenA++;
}
//计算a链表的长度
while (curB) {
curB = curB -> next;
lenB++;
}
//计算b链表的长度
curA = headA;
curB = headB;
//重新让遍历指针指向两链表的头节点
int gap = 0;
//初始化两链表长度差计数器
if (lenA > lenB) {
gap = lenA - lenB;
while (gap > 0) {
curA = curA -> next;
gap--;
}
} else {
gap = lenB - lenA;
while (gap > 0) {
curB = curB -> next;
gap--;
}
}
//计算两链表长度差,并让两链表尾部对齐
while (curA) {
if (curA == curB) {
return curA;
} else {
curA = curA -> next;
curB = curB -> next;
}
}
return NULL;
}
时间复杂度:O(n)
该题总结:这道题的解题过程较为繁琐,首先需要计算并比较两个链表的长度,并操作长的那个链表的指针使其与短链表的尾部对齐,再去让两个指针分别遍历两个表,写出来就这么两行字,但实现过程真的很繁琐,需要思路清晰才能把这道题写出来。
142.环形链表II
题目链接:142. 环形链表 II - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili
解题思路:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast = head;
//初始化快指针
struct ListNode *slow = head;
//初始化慢指针
while (fast && fast -> next) {
//快指针每次走两步所以要确保快指针节点及其下一个节点不为空
fast = fast -> next -> next;
//快指针每次走两步
slow = slow -> next;
//慢指针每次走一步
if (fast == slow) {
//快指针和慢指针相遇说明有环
struct ListNode *p1 = head;
struct ListNode *p2 = fast;
//让两个指针分别从head和相遇处出发
while (p1 != p2) {
p1 = p1 -> next;
p2 = p2 -> next;
//两指针每次走一步
}
return p1;
//相遇点即为环入口
}
}
return NULL;
//没有环返回NULL
}
时间复杂度:O(n)
该题总结:今天的重量级题目,代码不长,但要用到数学方法去计算快慢指针之间的距离关系,光是想到这点就很不容易,在计算出未进环之前的节点个数与相遇之后的节点个数之间的关系之后这道题才知道怎么解,感觉过几天可能就忘了是怎么写出这个算法的了,希望以后有机会再复习一下这道题。
总用时:约3.5小时