(不记得 是什么时候写的)链表相关
创始人
2025-05-30 02:32:20
0

Leetcode:

T1:利用 “归并排序” 对 链表 进行 排序:

关键:

(1)merge_sort函数 : 递归 函数--出口,直到只有1个 或者 0个 元素为止,直接返回这个节点,作用就是 链表 分成 2半, 

(2)merge_sort函数中:因为是链表,所以需要 利用 fast ,slow快慢指针 找到中间位置,然后分别找到 left链表 和 right链表的 头节点(注意把 left链表的 尾节点 设置为 NULL)

(3)merge函数:不需要用递归 实现 ,直接用while循环即可, 直到其中有一个指针为NULL,最后接上剩下的 那个链表(等价于上课 讲到的 2个有序 链表的 合并)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
//其中 一些 注释 用于之前的 调试,因为 函数体里面的 临时指针很容易被 销毁掉
class Solution {
public://利用 归并排序加以实现:ListNode* sortList(ListNode* head) {head=merge_sort(head);return head;}ListNode* merge_sort(ListNode* head){//static int time=1;//cout<<"这是第"<next==nullptr){return head;}//(1)利用 快慢指针 找到中间位置ListNode* fast=new ListNode(0);ListNode* slow=new ListNode(0);fast=head->next; //这种链表没有头节点,每个节点都有valslow=head;//fast 每次移动2格 , slow移动一格while(fast!= NULL && fast->next!=NULL){fast=fast->next->next;slow=slow->next;}//(2)此时slow指向的位置就是向下取整的中间位置//递归,直到1个元素 或0 个元素ListNode* right=new ListNode(0);ListNode* left=new ListNode(0);right=merge_sort(slow->next); //右边链表的起点slow->next=NULL; //左边链表的尾部 置NULLleft=merge_sort(head);head=merge(left,right);//尝试 添加一个新的 堆区空间ListNode* ans=new ListNode(0);ans=head;//show(ans);return ans; //head这个指针式传递进来的,可以返回,不是临时变量}//输出 函数void show(ListNode* node){while(node!=NULL){cout<val<next;}}ListNode* merge(ListNode* left,ListNode* right){//cout<<"left:"<val<<"right:"<val<<"merge一下"<next=NULL;while(left!=NULL && right!=NULL){if(left->val < right->val){//left先进root->next= left;root=root->next;left=left->next;}else{//right进root->next=right;root=root->next;right=right->next;}}//(2)处理 剩余的 节点if(left!=NULL){//左边非空root->next=left;}else if(right != NULL){root->next= right;}return root_->next;}
};

T2: 重排链表 (有点像 2个 链表交错)

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

 L0 → L1 → … → Ln-1 → Ln 
请将其重新排列后变为:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

解:

1.关键:

(1)总的来说 : 就是3个 操作: 快慢指针找到中间点  --  翻转后半链表  --  “交错合并”2个链表

(2) 注意 ,交错合并的 时候 指针的指向 次序 需要 “小心翼翼”

2.代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:void reorderList(ListNode* head) {//妙啊,这种 “交错的结构”--找中间点 , 然后对后半部分翻转 ,最后合并2个list//特殊,只有一个节点,直接返回if(head==NULL || head->next == NULL){return ;}//1.找到中间点:(利用快慢指针 -- 找到“向下取整”的中间点)ListNode* fast=head->next;ListNode* slow=head;while(fast!=NULL && fast->next!=NULL){fast=fast->next->next;slow=slow->next;}//此时slow就是那个“向下取整的”中间点//无所谓 ,反正 多出的哪一个 最后接上就好了//2.将slow->next 一直到 NULL前的 node进行翻转ListNode* start=slow->next;ListNode* tmp=start->next;  //tmp永远维持 start的原序的 下一个节点start->next=NULL; //翻转后的 “尾部” 置NULL//别忘记了,需要把 head的 最后一个节点的next置NULLslow->next=NULL;while(tmp!=NULL && start!=NULL){//tmp需要 指向现在的 start然后tmp给start,把u给tmpListNode* u=tmp->next;tmp->next=start;start=tmp;tmp=u;//成功翻转}//----测试: --好了, 上面的 步骤都没问题//show(head);//show(start);//tmp==NULL,start指向新的起点//3.好了2个链表的起点分别是head 和 start//开始 “交错合并”2个链表ListNode* pa=head;ListNode* pb=start;ListNode* pc=new ListNode(0);//头节点ListNode* new_head=pc;while(pa!=NULL && pb!=NULL){pc->next=pa;pa=pa->next;//--次序pc=pc->next;pc->next=pb;pb=pb->next;pc=pc->next;}if(pa!=NULL){pc->next = pa;}if(pb!=NULL){pc->next=pb;}head=new_head->next;}//测试函数void show(ListNode* node){while(node!=NULL){cout<val<<" ";node=node->next;}cout<

T3:(链表的必备熟练操作之一)翻转链表

给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。

解:

1.关键:

由于 翻转的 指针变化 ,需要逻辑清晰的 变化tmp , head ,临时u这3个指针,次序很重要

2.代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {//这是一个基本的 操作,应该作为像“冒泡排序”一样熟练//tmp指向 head的 下一个节点,u作为临时节点//特殊情况if(head == NULL || head->next == NULL){return head;}//--ListNode* tmp=head->next;head->next=NULL;  //翻转后的 为节点的 next指针 置NULLwhile(tmp!=NULL && head!=NULL){//将tmp->next 给到u ,然后及那个tmp->next指向head,tmp给head,u给tmpListNode* u=tmp->next;tmp->next=head;head=tmp;tmp=u;}return head;}
};

T4: 判断一个链表是否为 “回文链表”

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

解:

1.关键:

(1) 就是 3个 基本 操作的 组合: 快慢指针找到中间点 -- 翻转链表 -- 2个链表对应位置的比较

2.代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:bool isPalindrome(ListNode* head) {//3个操作: 找中间点 - 翻转后半部分 - 2个链表的val比较//--特殊情况:if(head == NULL || head->next == NULL){return true;}//1.利用 快慢指针 找到中间点ListNode* fast=head->next;ListNode* slow=head;while(fast!=NULL && fast->next!=NULL){fast=fast->next->next;slow=slow->next;}ListNode* start=slow->next;slow->next= NULL;//2.翻转后半 链表ListNode *tmp = start->next;start->next=NULL; //翻转 后的 为节点的 next指针 置NULLwhile(tmp!=NULL && start!=NULL){ListNode* u=tmp->next;tmp->next=start;start=tmp;tmp=u;}//3. 2个链表之间的 比较 ,头节点分别 是 head 和 startwhile(head !=NULL && start!=NULL){if(head->val != start->val){return false;}head = head->next;start=start->next;}return true;}
};

T5:合并 多个 有序链表 

给定一个链表数组,每个链表都已经按升序排列。

请将所有链表合并到一个升序链表中,返回合并后的链表。

解:

1.关键:

(1)基础:2个有序链表的 排序

(2)思维: 利用 多元gcd的求解思维, 多元 == 依次二元

2.代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeKLists(vector& lists) {//这个题目 就是 多个 链表的 merge//借鉴 求解多元gcd的 思路,多个 == 依次 2个,所以只要不断调用 2个链表的 合并//特殊情况if(lists.size() == 0){return NULL;}ListNode* ans = lists[0];int size= lists.size();for(int i=1;ival < right->val){tmp->next=left;left=left->next;tmp=tmp->next;}else{tmp->next=right;right=right->next;tmp=tmp->next;}}//--那个还有剩余if(left!=NULL){tmp->next=left;}if(right!=NULL){tmp->next=right;}return ans->next;}};

T6:复杂链表的 复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

解:

1.关键:

(1)这是一道 关于  unordered_map 的问题

(2)首先 遍历 一次 , 存储下 对应的 next指针 和 在map中存储 对应的Node的对应关系

(3)最后遍历一次, 存储对应的 random指针

2.代码:

/*
// Definition for a Node.
class Node {
public:int val;Node* next;Node* random;Node(int _val) {val = _val;next = NULL;random = NULL;}
};
*/
class Solution {
public:Node* copyRandomList(Node* head) {//特殊if(head == NULL){return NULL;}//设置一个 递增变量指针Node* tmp=head;//设置一个map用于存储对应的 Node的关系unordered_map Map;//利用一个map进行存储 两者对应的节点Node* new_head=new Node(head->val);Map[head]=new_head;tmp=tmp->next;Node* tmp2=new_head;while(tmp!=NULL){//每次开辟一个新的 NOde空间,Node* new_node=new Node(tmp->val);Map[tmp]=new_node;tmp2->next=new_node;tmp2=tmp2->next;tmp=tmp->next;//Map中存储对应的 Node的 关系:}//2.遍历了之后,需要 再次遍历 ,并且处理 random指针tmp=head;tmp2=new_head;while(tmp!=NULL){tmp2->random = Map[tmp->random];tmp=tmp->next;tmp2=tmp2->next;}return new_head;}
};

T7: 向 有序 环形链表中 insert一个节点

给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。

给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。

如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。

如果列表为空(给定的节点是 null),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。

解:

1.关键:

(1) 模拟遍历查找 + insert节点操作(熟练)

(2)需要考虑 一些 特殊操作 和 取等、、、那些情况

2.代码:

/*
// Definition for a Node.
class Node {
public:int val;Node* next;Node() {}Node(int _val) {val = _val;next = NULL;}Node(int _val, Node* _next) {val = _val;next = _next;}
};
*/class Solution {
public:Node* insert(Node* head, int insertVal) {//思路: 直接模拟 遍历查找 + 插入一个节点的 操作即可//特殊情况:if(head == NULL){Node* new_head =new Node(insertVal);new_head->next = new_head;return new_head;}if(head->next == head){Node* new_head =new Node(insertVal);new_head->next = head;head->next = new_head;return head;}//(1)copy 保留开始 的head节点Node* tmp = head;Node* new_node=new Node(insertVal);//将这个new_node通过遍历 找到合适的 位置, 然后 执行insert操作//(2)遍历//符合条件的位置: tmp->val < next->val tmp->val < insertVal && tmp->next->val//或者: tmp->val > next->val  && tmp->val < insertVal int i=1;while(1){if(tmp == head && i!=1){//说明 所有的val都相同new_node->next = tmp->next;tmp->next = new_node;return head;}i++;cout<<"来过吗 "<val <val < tmp->next->val && tmp->next->val >= insertVal && tmp->val <= insertVal){new_node->next = tmp->next;tmp->next = new_node;return head;}else if(tmp->val > tmp->next->val && (tmp->val <= insertVal || tmp->next->val >= insertVal)){new_node->next = tmp->next;tmp->next = new_node;return head;}//case3:tmp=tmp->next;}//:只剩 一种case 需要考虑, 就是所有的 val都相等怎么办?//很简单 , 那时候tmp一定会回到head,直接insert到这个位置即可}
};

T8:删除 链表 节点(基本操作)

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

解:

1.关键:

(1)利用 “双指针 ”找到需要删除的 节点的前驱节点

(2)然后 next 指next->next即可

2.代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* deleteNode(ListNode* head, int val) {//找到这个 节点的 前驱节点的指针即可ListNode* tmp=head->next;ListNode*tmp2=head;//特殊情况:if(head->val == val){head = head->next;return head;}//--一般情况:while(tmp->val !=val){tmp2=tmp;tmp = tmp->next;}tmp2->next = tmp2->next->next;return head;}
};

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...