前言
思路及算法思维,指路 代码随想录。
题目来自 LeetCode。
day 4,一个工作日的周六,坚持一下也不是很困难~
题目详情
[24] 两两交换链表中的节点
题目描述
解题思路
前提:单链表 + 交换链表节点(不是节点数值)
思路:节点两两一组,改变涉及的3个node的next指针指向即可。
重点:考虑是否使用虚拟头结点,如果不适用虚拟头结点,需要单独处理头节点一组的情况,所以建议使用虚拟头节点,所有节点统一处理。
代码实现
C语言
虚拟头节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* swapPairs(struct ListNode* head) {
struct ListNode *vHead = (struct ListNode *)malloc(sizeof(struct ListNode));
vHead->next = head;
struct ListNode *cur = vHead;
while (cur->next && cur->next->next)
{
struct ListNode *pre = cur;
struct ListNode *tmp = cur->next->next;
cur->next->next = tmp->next;
tmp->next = cur->next;
pre->next = tmp;
cur = cur->next->next;
}
head = vHead->next;
free(vHead);
return head;
}
[19] 删除链表的倒数第N个节点
题目描述
解题思路
前提:单链表 + 删除倒数第n个结点
思路:双指针方法,两个指针间隔n个结点,当快指针到链表表尾时,慢指针指向的节点就是需要删除的节点。(暴力解法:遍历链表节点长度,再遍历到节点位置,时间复杂度高~)
重点:删除结点时,需要使用该结点的前置结点。
代码实现
C语言
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode *vHead = (struct ListNode *)malloc(sizeof(struct ListNode));
vHead->next = head;
struct ListNode *fast = vHead;
struct ListNode *slow = vHead;
while (n--)
{
fast = fast->next;
}
struct ListNode *tmp = NULL;
while (fast)
{
fast = fast->next;
tmp = slow;
slow = slow->next;
}
tmp->next = slow->next;
free(slow);
head = vHead->next;
free(vHead);
return head;
}
[面试题02.07] 链表相交
题目描述
解题思路
前提:链表相交
思路:链表相交,是指同一个结点,而不是结点数值一致;并且,当链表相交之后,相交点之后的结点均是同一个结点。
重点:链表若相交,从相交点后,结点的长度、地址均一致。
代码实现
C语言
计算两个链表的长度,找到公共长度部分,开始比较结点地址是否一致。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
int listSizeA = 0;
int listSizeB = 0;
int size = 0;
struct ListNode *curA = headA;
struct ListNode *curB = headB;
while (curA)
{
listSizeA++;
curA = curA->next;
}
while (curB)
{
listSizeB++;
curB = curB->next;
}
curA = headA;
curB = headB;
if (listSizeA > listSizeB)
{
size = listSizeB;
int offset = listSizeA - listSizeB;
while ((curA) && (offset--))
{
curA = curA->next;
}
curB = headB;
}
else
{
size = listSizeA;
int offset = listSizeB - listSizeA;
while ((curB) && (offset--))
{
curB = curB->next;
}
curA = headA;
}
while (curA && curB)
{
if (curA == curB)
{
break;
}
curA = curA->next;
curB = curB->next;
}
return curA;
}
[142] 环形链表II
题目描述
解题思路
前提:单链表 + 表中可能有环
思路:双指针,快指针每次走两步,慢指针每次走一步,如果存在环,当快指针和慢指针均在环中时,总会相遇,此时快指针比慢指针多执行nC步(C为环长度,n=1, 2, 3……),当n = 1时,慢指针环外的长度 = 快指针环内该圈未经过的长度,所以快指针调整为一步一步前行,慢指针从链表头一步一步前行,相遇的点就是链表入环的结点;当n > 1时,无非就是快指针在环内多循环几圈,相遇点依旧是链表入环的结点。
重点:双指针判断链表是否有环的思路,以及,如果确定链表入环的结点。
代码实现
双指针
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast = head;
struct ListNode *slow = head;
bool found = false;
if (head == NULL || head->next == NULL ||head->next->next == NULL)
{
return NULL;
}
slow = head->next;
fast = head->next->next;
while (slow && fast)
{
if (slow == fast)
{
found = true;
break;
}
if (!slow->next || !fast->next || !fast->next->next)
{
break;
}
slow = slow->next;
fast = fast->next->next;
}
if (!found)
{
return NULL;
}
slow = head;
while (slow && fast)
{
if (slow == fast)
{
break;
}
slow = slow->next;
fast = fast->next;
}
return slow;
}
今日收获
- 链表的使用。
- 链表相交,就是同一结点。
- 链表有环的判断方法,以及判断链表入环的结点。