【Leetcodenowcode数据结构】单链表的应用(进阶)
系列文章目录
《Leetcode&nowcode代码强化刷题》
文章目录
- 系列文章目录
- 前言
- 正文
- 一.链表分割
- 代码块1
- 代码块2
- 代码块3
- 代码块4
- 二.随机链表的复制
- 代码块1(拷贝)
- 代码块2(插入)
- 代码块3
- 代码块4
- 代码块5
- 总结
前言
数据结构与算法
是计算机领域的核心,既是面试考察重点,也是优化项目性能的关键。而刷题是掌握它最有效的方式,能帮我们巩固理论、提升解题能力。
我选择LeetCode
和NowCode
作为主要刷题平台:LeetCode 题目丰富、分类清晰,适合夯实基础;NowCode贴近国内企业
笔试场景,助力对接实战需求,二者互补性强。
这份刷题记录不只是题目与答案的罗列,更会记录解题思路、难点易错点,以及解法优化过程。希望它能成为我的复盘工具
,也为其他学习者提供参考
。
接下来,就从第一道题开始,在刷题中积累提升,让数据结构与算法成为解决问题的有力工具。
正文
一.链表分割
题目链接:链表分割:
题目描述:
题目分析:
题目很短,大致的意思就是:给我们一个链表,让我们在不改变数据顺序的前提下,将链表中值小于x
的结点放在前面
,其他
放在后面
这里的不改变顺序,指的是相对顺序
,例如下面这个链表
如果我们x=3
,这时候按照题目要求,不改变相对顺序,划分链表
对于小于3
的1和2结点,我们要排在前面,并且顺序还是1在前,2在后;
对于不小于3
的结点,我们都排在后面,顺序是6,3,5,最后排成这样:
思路讲解
我们首先可能会想到遍历链表
,把每一个比x大
的数据都进行尾插再删除
这个方法是可以的,但是考虑到这个方法时间复杂度
有点大,我们可以再想想其他的
既然是划分链表,我们可以先创建两个新链表,一个用存小于x
的结点,另一个用来存不小于x
的结点,最后再把两个链表拼接
起来
我们先创建
这两个链表
,考虑到空链表
情况,我们需要创建哨兵位
代码块1
//创建大小链表//小链表ListNode* lessHead = NULL, *lessTail = NULL;lessHead = lessTail = (LTN*)malloc(sizeof(LTN));//大链表ListNode* greaterHead = NULL, *greaterTail = NULL;greaterHead = greaterTail = (LTN*)malloc(sizeof(LTN));
之后我们需要遍历原链表
,按照划分要求把每个结点分别插入到不同的链表
代码块2
LTN* pcur = pHead;
//遍历原链表,按照大小插入不同链表while (pcur != NULL) {if (pcur->val < x) { //插入小链表lessTail->next = pcur;lessTail = lessTail->next;} else { //插入大链表greaterTail->next = pcur;greaterTail = greaterTail->next;}//向后遍历pcur = pcur->next;}
最后拼接
两个链表
代码块3
//拼接大小链表lessTail->next = greaterHead->next;
这时候我们提交代码,会出现这样的报错
原来是我们拼接后半段的大链表,最后一个节点不一定指向NULL
于是我们还需要手动置空
代码块4
//把大链表最后一个结点的next置空greaterTail->next = NULL;
这样这道题目就差不多了
代码展示
/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {public:typedef struct ListNode LTN;ListNode* partition(ListNode* pHead, int x) {//创建大小链表//小链表ListNode* lessHead = NULL, *lessTail = NULL;lessHead = lessTail = (LTN*)malloc(sizeof(LTN));//大链表ListNode* greaterHead = NULL, *greaterTail = NULL;greaterHead = greaterTail = (LTN*)malloc(sizeof(LTN));LTN* pcur = pHead;
//遍历原链表,按照大小插入不同链表while (pcur != NULL) {if (pcur->val < x) { //插入小链表lessTail->next = pcur;lessTail = lessTail->next;} else { //插入大链表greaterTail->next = pcur;greaterTail = greaterTail->next;}//向后遍历pcur = pcur->next;}
//把大链表最后一个结点的next置空greaterTail->next = NULL;
//拼接大小链表lessTail->next = greaterHead->next;//保存新链表的第一个结点LTN* ret = lessHead->next;
//释放空间free(lessHead);free(greaterHead);
//返回新链表return ret;}
};
博主题解
二.随机链表的复制
题目链接:138.随机链表的复制
题目描述:
题目分析:
这道题目,让我们去构造链表的深拷贝
什么是深拷贝
呢?
假设我们现在要对a=3,继续拷贝,这时候说b也等于3,但是a和b公用3这块内存,这是浅拷贝
深拷贝
就是,我们额外在开辟一块新空间
,里面放拷贝的数据
也就是说,这道题目让我们去把原链表的每个节点都去深拷贝
一份,再对拷贝出来结点,按照原链表的指向去连接
思路讲解
这里深拷贝我们可以用malloc
,next指针也可以按顺序去指,但是random
这个指针是无序的,这给我们额外创建链表,再去连接这个做法造成了困难
那么有没有什么方法可以在原有链表的基础上去完成呢?
我们可以先对每个结点进行复制
,然后把每个复制结点,插入原结点的后面
,如下图
这时候我们让两个指针,copy
和cur
去分别遍历复制结点和原来结点
当我们原链表的random指向NULL
的时候,我们让对应的复制结点random也指向NULL;当我们原链表的random不指向NULL
的时候,就会发现:
copy->random=cur->random->next
恰好就可以让复制结点的random指针,按照原链表的顺序指好
所以我们先把所有结点拷贝
插入链表中
代码块1(拷贝)
// 复制结点Node* newNode = (Node*)malloc(sizeof(Node));newNode->val = x;newNode->next = newNode->random = NULL;return newNode;
代码块2(插入)
Node* pcur = head;while (pcur != NULL) {// 记录下一个结点位置Node* next = pcur->next;Node* copyNode = buyNode(pcur->val);// 复制结点指向nextcopyNode->next = next;// pcur指向复制结点pcur->next = copyNode;// pcur继续遍历pcur = next;}
然后设置复制节点的random
指针
代码块3
Node* pcur = head; // 遍历原结点while (pcur != NULL) {Node* copy = pcur->next; // 遍历复制结点// 标记下一个结点Node* next = copy->next;// 原来的random指向空,复制结点random还是指向空if (pcur->random == NULL) {copy->random = NULL;}// 原来结点的random不指向NULL// copy->random等于cur->random->nextelse {copy->random = pcur->random->next;}pcur = next;}
最后我们让复制的结点单独连接
代码块4
// 断开与原结点的连接Node* pcur = head;Node *copyHead, *copyTail;copyHead = copyTail = pcur->next;while (copyTail->next!=NULL) {pcur = copyTail->next;copyTail->next = pcur->next;copyTail = copyTail->next;}
我们每个循环,我们都需要仔细去想它的循环条件
是什么
提交之后,会报错
样例是空链表
,所以我们需要特判
代码块5
// 特判空链表if (head == NULL)return head;
这样我们这道题目就可以解决了
代码展示
/*** Definition for a Node.* struct Node {* int val;* struct Node *next;* struct Node *random;* };*/
typedef struct Node Node;// 复制结点
Node* buyNode(int x) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->val = x;newNode->next=newNode->random=NULL;return newNode;
}void AddNode(Node* head) {Node* pcur = head;while (pcur != NULL) {// 记录下一个结点位置Node* next = pcur->next;Node* copyNode = buyNode(pcur->val);// 复制结点指向nextcopyNode->next = next;// pcur指向复制结点pcur->next = copyNode;// pcur继续遍历pcur = next;}
}
void setRandom(Node* head) {Node* cur = head; // 遍历原结点while (cur != NULL) {Node* copy = cur->next; // 遍历复制结点// 标记下一个结点Node* next = copy->next;// 原来的random指向空,复制结点random还是指向空if (cur->random == NULL) {copy->random = NULL;}// 原来结点的random不指向NULL// copy->random等于cur->random->nextelse {copy->random = cur->random->next;}cur = next;}
}struct Node* copyRandomList(struct Node* head) {// 特判空链表if (head == NULL)return head;// 添加结点AddNode(head);// 设置复制结点的random指针setRandom(head);// 断开与原结点的连接Node* copy = head->next;Node* cur = copy->next;while (cur != NULL) {copy->next = cur->next;copy = copy->next;cur = copy->next;}return head->next;
}
博主题解
总结
进阶题目,代码量
和思考量
相比较初阶更多一些(尤其第二道),我们需要多去练习
掌握,也许第一遍不会去做(第二题第一次真的可以想到这个解法吗ovo),但是我们要不断地去反复总结
下一篇我会为大家带来顺序表相关
的题目,希望大家可以多多支持,有什么问题可以评论留言或者后台私信,感谢大家
有佬佬问,题目可以讲的详细一点吗,于是博主加上了
思路分析
,以后题目如果不是特别简单
,博主都会写上思路分析滴,希望对大家有所帮助