携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情
前言
数据结构与算法刷题专栏第二篇,本文是基于C语言实现的。
本文就来分享一波作者对单链表的一些习题的学习心得与见解。
笔者水平有限,难免存在纰漏,欢迎指正交流。
Leetcode203——移除链表元素
题目描述
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
链接:Leetcode203
示例
示例 1:
输入: head = [1,2,6,3,4,5,6], val = 6 输出: [1,2,3,4,5]
示例 2 :
输入: head = [ ], val = 1 输出: [ ]
示例 3:
输入: head = [7,7,7,7], val = 7 输出: [ ]
提示:
- 列表中的节点数目在范围 [0, 104] 内
- 1 <= Node.val <= 50
- 0 <= val <= 50
核心代码模式
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val)
{
}
思路分析与代码实现
1.前后指针原地修改法
要删掉值为val的结点,那我们就遍历链表,遇到符合条件的结点就删掉。毕竟是要删掉结点,那就设置两个指针prev和cur一前一后。cur不为NULL就一直找下去,找到要删除结点时,要注意一下是不是头结点,要是头结点的话就就是头删了,先把cur下一个结点地址给头指针head,释放掉cur位置的结点,再把head值给cur让它指向下一个结点;若不是头结点,那就先把cur下一个结点地址给prev的next指针,让prev指向cur下一个结点,释放掉cur位置的结点,再把prev下一个结点的地址给cur,让它指向下一个结点。没找到要删除的结点的话就继续找下去。
代码实现
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev = head;
struct ListNode* cur = head;
while(cur)
{
if(cur->val == val)
{
if(cur == head)
{
head = cur->next;
free(cur);
cur = head;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
return head;
}
2.新链表尾插法
可不可以新建一个链表,定义新的头指针为newHead,然后就把值不等于val的结点放到新链表去,等于val的结点我们把它释放掉,所以我们要一个指针来遍历链表,定义cur指针。这里把值不等于val的结点放到新链表用的是尾插,那好了,尾插要是还要来个指针一个一个去找前一结点可太麻烦了,不妨定义一个尾指针tail用来指向新链表的尾部,这样尾插就方便多了。要注意有个坑,尾插到最后要记得把新链表最后一个结点的next指针给置为NULL。
\
代码实现
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* cur = head;
struct ListNode* newHead = NULL;
struct ListNode* tail = NULL;
while(cur)
{
if(cur->val != val)
{
if(tail == NULL)
{
newHead = cur;
tail = cur;
}
else
{
tail->next = cur;
tail = cur;
}
cur = cur->next;
}
else
{
struct ListNode* del = cur;
cur = cur->next;
free(del);
}
}
if(tail)
tail->next = NULL;
return newHead;
}
试试带头链表
我们可以试试看用带头链表,头指的就是哨兵结点,不是用来存放有效数值的,而是用作辅助结点的。用哨兵结点在这有什么用呢?你想啊,之前不用它时是不是还要判断一下tail是不是NULL,但是用哨兵结点后tail至少是指向哨兵结点的,不会为NULL,是不是就省了点事?这里的哨兵结点我们命名为guard。
代码实现
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* cur = head;
struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* tail = guard;
while(cur)
{
if(cur->val != val)
{
tail->next = cur;
tail = cur;
cur = cur->next;
}
else
{
struct ListNode* del = cur;
cur = cur->next;
free(del);
}
}
tail->next = NULL;
head = guard->next;
free(guard);
return head;
}
Leetcode160——相交链表
题目描述
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据保证整个链式结构中不存在环。
注意:函数返回结果后,链表必须 保持其原始结构 。
提示:
- listA 中节点数目为 m
- listB 中节点数目为 n
- 1 <= m, n <= 3 * 104
- 1 <= Node.val <= 105
- 0 <= skipA <= m
- 0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]
进阶:
你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?
链接:Leetcode160
核心代码模式
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
}
思路分析与代码实现(C语言)
这里不考虑暴力求解。如果两链表相交就一定会有公共交点,不过不能通过比较结点值来判断相交,因为结点值相同并不意味着相交,相交是指向同一结点,而结点值相同可以是两个位置完全不同的结点。比结点地址才能判断是不是公共结点,不过怎么比呢?是不是用两个指针分别指向两个链表,然后一个一个对应比较地址呢?在这里是行不通的,为什么?因为两链表的长度不一定相同,这样比会发生错位。
我们先看看如何判断相交,你想啊,两链表要是相交的话,链表的尾结点是不是一定是同一个?是吧,要是不相交的话就不是同一个尾结点。只要判断尾结点是不是同一个结点就可以判断出两链表是否相交。
不过题目要我们求出来的是相交的起始结点,这怎么求呢?前面说的比结点地址的方法有可能会有错位是吧,那可不可以消除了错位问题再一个一个比对呢?我们这里使用快慢指针,快指针指向长的链表,让快指针先走差距步,差距步就是两链表长度的差值,然后再让快慢指针同时走,每次走一步,比对两链表的结点地址是否相同,若是相同了就说明找到相交的起始结点了。
要注意链表为空的可能性,可以在一开始判断一下。链表长度就用指针遍历链表计数器计数来求,顺便还把尾结点找到了,顺便比一下尾结点地址是否相同,要是不同直接返回NULL,对应链表不相交的情况。
代码实现
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
if(headA == NULL && headB == NULL)
return NULL;
struct ListNode* tailA = headA;
struct ListNode* tailB = headB;
int cntA = 1;
int cntB = 1;
while(tailA->next)
{
tailA = tailA->next;
cntA++;
}
while(tailB->next)
{
tailB = tailB->next;
cntB++;
}
if(tailA != tailB)
return NULL;
struct ListNode* longList = headA;
struct ListNode* shortList = headB;
if(cntA < cntB)
{
longList = headB;
shortList = headA;
}
int gap = abs(cntA - cntB);
while(gap--)
{
longList = longList->next;
}
while(longList != shortList)
{
longList = longList->next;
shortList = shortList->next;
}
return longList;
}
以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~