链表合集(easy难度)

合并两个有序链表

双指针法

由于list1和list2都是递增的,可以想到用双指针法。假如当前list1这个指针指向的节点被收入完成,那就list1++;如果是list2被收入,那就list2++。 

具体是list1和节点被收入还是list2的节点被收入?比较list1->val和list2->val的大小即可。

算法流程:

1. 创建dum节点(leetcode貌似都没有给虚假头节点的),作为新链表的虚假头节点。最后返回dum->next即可。

2. 写循环主体,注意循环终止的条件,list1或list2这两个指针有一个指向nullptr,说明有一个链表被选完了,那剩下另一个没被选完的直接往后插就行了。(为什么能这么干脆?因为list1和list2这两个链表本身就是有序的!)

3. 退出循环后,注意把还没选完的链表往新链表后面一插。

基于插入节点的算法设计,此处采用尾插法。

尾插法,需要用一个指针来记录尾节点。此处使用Cur

/*** 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* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode* dum = (ListNode*)malloc(sizeof(ListNode));ListNode* cur = dum;while(list1 != nullptr && list2 != nullptr) {if(list1->val < list2->val) {cur->next = list1;list1 = list1->next;}else {cur->next = list2;list2 = list2->next;}cur = cur->next;}if(list1 == nullptr) cur->next = list2;else cur->next = list1;return dum->next;}
};

递归

这直接不用创建什么dum了。

/*** 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* mergeTwoLists(ListNode* list1, ListNode* list2) {if(list1 == nullptr) {return list2;}if(list2 == nullptr) {return list1;}if(list1->val <= list2->val) {list1->next = mergeTwoLists(list1->next, list2);return list1;}list2->next = mergeTwoLists(list1, list2->next);return list2;}
};
//这里不能再写第四个if,不然编译器觉得如果你四个条件都不满足的话,你return啥呢?

woc太深奥了。

删除有序链表中的重复元素

双指针法

用pre和cur两个指针。pre指针作为最新确定的“标杆”,cur指针始终为pre-next(写cur是为了阅读和理解方便),是我们当前需要检验的节点。

循环退出的条件是,我们没有需要检验的节点了。也就是说,cur不存在了,即cur指向nullptr。

/*** 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* deleteDuplicates(ListNode* head) {//链表为空或链表只有一个节点时,直接return//不过其实这里也可以只写链表为空的情况if(head == nullptr || head->next == nullptr) {return head;}ListNode* pre = head;ListNode* cur = pre->next;while(cur != nullptr) {if(cur->val == pre->val) {ListNode* r = cur->next;delete cur;pre->next = r;cur = r;}else if(cur->val != pre->val) {pre = cur;cur = cur->next;}}return head;}
};

这里为什么可以返回head?因为head绝对不会被删掉! 

移除链表元素

单指针迭代

单指针即可,双指针完全没必要啊。这里必须要创建虚假头节点dum!因为head可能会被删掉。注意退出循环的条件,pre后面没有东西了。因为我们每次要检验的是pre-next,那你pre后面都没东西了我还检验啥呢?

/*** 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* removeElements(ListNode* head, int val) {//链表为空直接返回不用犹豫if(head == nullptr) {return head;}ListNode* dum = new ListNode(0);dum->next = head;//dum的位置要记住,因为head可能被删掉!ListNode* pre = dum;//用pre走,而不用dum走while(pre->next != nullptr) {if(pre->next->val == val) {ListNode* r = pre->next->next;delete pre->next;pre->next = r;}elsepre = pre->next;}return dum->next;}
};

递归

“链表的定义具有递归的性质”?好好好。也就是说递归法是求解链表题的常用方法是吧。其实也很好理解。

/*** 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* removeElements(ListNode* head, int val) {//递归终止的条件为head为空if(head == nullptr) {return head;}//递归,就是只用关注每层的工作,以及每层传递给上层的东西//本题,每层的工作就是把该节点之后的所有节点进行一次remove操作+对本层的头节点进行判断,以确定返回值是head还是head-next//所以,每层肉眼可见做的事情,只有判断本层头节点head->next = removeElements(head->next, val);//最后判断头节点if(head->val == val) {return head->next;}return head;}
};

反转链表

迭代法

把后继节点记住,用r表示。pre、cur进行迭代。cur是我们要处理的节点。退出循环的条件是cur为空,就是没有需要处理的节点了。

/*** 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) {ListNode *cur = head, *pre = nullptr;while(cur != nullptr) {ListNode *r = cur->next;cur->next = pre;pre = cur;cur = r;}return pre;}
};

最初的pre是nullptr,第一个需要处理的节点cur就是头节点。( pre最初设置成nullptr作为反转前链表头节点将要指向的东西,还挺重要的。我以前好像没意识到。)

整个while循环可以直接运转,无需特殊处理。最后返回pre。

递归

/*** 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) {return cur(head, nullptr);}
private:ListNode* recur(ListNode* cur, ListNode* pre) {if(cur == nullptr) return pre;ListNode* res = recur(cur->next, cur);cur->next = pre;return res;}
};

太抽象了nb。 

回文链表

先反转链表

本来是想套用上题函数,但。。。这方法有大bug!

你反转完,原来的链表不就不在啦?!所以你要用这方法就必须再复制一个链表。

所以额外还要再写两个函数,一个reverseList一个copyList。

/*** 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) {//链表为空的特殊讨论if(head == nullptr) {return true;}ListNode* head1 = copyList(head);ListNode* head2 = reverseList(head);while(head1 != nullptr) {if(head1->val != head2->val) {return false;}head1 = head1->next;head2 = head2->next;}return true;}ListNode* reverseList(ListNode* head) {ListNode* cur = head, *pre = nullptr;while(cur != nullptr) {ListNode* r = cur->next;cur->next = pre;pre = cur;cur = r;}return pre;}ListNode* copyList(ListNode* head) {ListNode* newHead = new ListNode(head->val);ListNode* r = newHead;while(head->next != nullptr) {ListNode* newNode = new ListNode(head->next->val);r->next = newNode;r = newNode;head = head->next;}return newHead;}
};

把链表转换成数组

(我感觉可能是数据结构课上魔怔了,我的脑洞都打不开了!思维发散都不会了QAQ)

数组和链表,都是线性表的表现形式。数组是顺序表,链表是链式表。其实二者是可以相互转换的。至少在本题,回文数组可是很好求的!

我自己写的是纯数组。。因为他最多就100000个数,我开10005大小的数组。(刚开始脑抽,数组类型写成ListNode了/笑)

/*** 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) {int arr[100005] = {};int i = 0;while(head != nullptr) {arr[i++] = head->val;head = head->next;}for(int j = 0, q = i-1; j < i/2; j++,q--) {if(arr[j] != arr[q]) return false;}return true;}
};

优雅vector,这样空间复杂度小了。

/*** 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) {vector<int> vals;while(head != nullptr) {vals.emplace_back(head->val);head = head->next;}for(int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {if(vals[i] != vals[j]) {return false;}}return true;}
};

插播一下std中的emplace_back函数:

 

相交链表

哈希集合

没想到这么做是因为对stl太不熟悉了,我最初只想到数组存储值,但后来一看,节点的值可以重复取,遂放弃。好伟大的哈希集合啊!

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {unordered_set<ListNode *> visited;ListNode *temp = headA;while(temp != nullptr) {visited.insert(temp);temp = temp->next;}temp = headB;while(temp != nullptr) {if(visited.count(temp)) {return temp;}temp = temp->next;}return nullptr;}
};

时间复杂度:Om+n,m为链表A的长度,n为链表B的长度

空间复杂度:Om

双指针

这个太妙了只能说。思路就看看吧先。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *pA = headA, *pB = headB;if(pA == nullptr || pB == nullptr) {return nullptr;}while(pA != pB) {if(pA == nullptr) {pA = headB;}else {pA = pA->next;}if(pB == nullptr) {pB = headA;}else {pB = pB->next;}}return pA;}
};

移除无序链表的重复节点

哈希集合

感觉哈希集合其实是很好用的。(上面那个删除有序链表中的节点,我感觉也可以用这个方法)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* removeDuplicateNodes(ListNode* head) {if(head == nullptr) return nullptr;unordered_set<int> visited;//你要说删除节点,我可要创建dum了哦//但其实这里不会删除头节点ListNode* dum = new ListNode(0);dum->next = head;ListNode* temp = dum;while(temp->next != nullptr) {if(visited.count(temp->next->val)) {temp->next = temp->next->next;} else {visited.insert(temp->next->val);temp = temp->next;}}return head;}
};

(不释放内存了我就展示算法)

时间复杂度:On,n为链表的节点个数

空间复杂度:On,哈希集合的长度(最坏情况n个节点的值都不同)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/578212.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Netty核心原理剖析与RPC实践21-25

Netty核心原理剖析与RPC实践21-25 21 技巧篇&#xff1a;延迟任务处理神器之时间轮 HahedWheelTimer Netty 中有很多场景依赖定时任务实现&#xff0c;比较典型的有客户端连接的超时控制、通信双方连接的心跳检测等场景。在学习 Netty Reactor 线程模型时&#xff0c;我们知道…

使用filezilla连接Ubuntu22.04虚拟机

获取电脑IP和虚拟机IP ① 在windows下ctrlR再输入cmd&#xff0c;打开指令窗口&#xff0c;输入 ipconfig 虚拟机连接电脑用的是NAT模式&#xff0c;故看VMnet8的IP地址 ② 查看虚拟机IP地址 终端输入 ifconfig 如果没安装&#xff0c;按提示安装net-tools sudo apt install …

HarmonyOS 应用开发之创建PageAbility

开发者需要重写app.js/app.ets中的生命周期回调函数&#xff0c;开发者通过DevEco Studio开发平台创建PageAbility时&#xff0c;DevEco Studio会在app.js/app.ets中默认生成onCreate()和onDestroy()方法&#xff0c;其他方法需要开发者自行实现。接口说明参见前述章节&#xf…

5.11 Vue配置Element UI框架

Vue配置Element UI框架 目录一、 概要二、 开发前准备1. 搭建Vue框架 三、 安装 Element UI1. 引入 Element UI 依赖2. 在 mian.js 中引入 Element UI 和相关样式&#xff1a;3. 按需引入(非必须, 可忽略)4. 简单构建一个主页面 目录 一、 概要 Element UI 是一个基于 Vue.js …

窥探未来:Web3如何颠覆传统互联网

随着科技的迅速发展&#xff0c;Web3正逐渐成为人们关注的焦点。与传统的Web2相比&#xff0c;Web3代表了一种全新的互联网模式&#xff0c;其潜力和影响力引发了人们对未来的期待和探索。本文将深入探讨Web3如何颠覆传统互联网的各个方面&#xff0c;并展望其可能带来的未来变…

卷积神经网络(CNN)基础知识整理

卷积神经网络&#xff08;CNN&#xff09;基础知识整理 0写在前面 这两天陆续看了一些关于卷积神经网络的视频和博文&#xff0c;把我觉得比较有用的知识和内容梳理一下&#xff0c;理顺逻辑&#xff0c;自己也可加深理解&#xff0c;写在这里&#xff0c;日后想看&#xff0…

3D人体姿态估计项目 | 从2D视频中通过检测人体关键点来估计3D人体姿态实现

项目应用场景 人体姿态估计是关于图像或视频中人体关节的 2D 或 3D 定位。一般来说&#xff0c;这个过程可以分为两个部分&#xff1a;(1) 2D 视频中的 2D 关键点检测&#xff1b;(2) 根据 2D 关键点进行 3D 位姿估计。这个项目使用 Detectron2 从任意的 2D 视频中检测 2D 关节…

太阳能发电园区3D可视化:揭秘绿色能源新纪元

在科技飞速发展的今天&#xff0c;绿色能源已成为推动社会进步的重要力量。太阳能发电作为绿色能源的重要代表&#xff0c;正在全球范围内掀起一股清洁能源的革命浪潮。 太阳能发电园区作为集中展示太阳能发电技术和应用的场所&#xff0c;其规模之大、设备之复杂&#xff0c;常…

政安晨:【Keras机器学习实践要点】(三)—— 编写组件与训练数据

目录 介绍 编写组件 训练模型 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 介绍 通过 Ker…

封装性练习

练习 1 &#xff1a; 创建程序&#xff1a;在其中定义两个类&#xff1a; Person 和 PersonTest 类。定义如下&#xff1a; 用 setAge() 设置人的合法年龄 (0~130) &#xff0c;用 getAge() 返回人的年龄。在 PersonTest 类中实例化 Person 类的对象 b &#xff0c;调用 set…

李雅普诺夫函数

李雅普诺夫函数是一种用于描述动力系统稳定性的数学工具。它在动力系统和控制理论中具有广泛的应用&#xff0c;尤其是在研究非线性系统的稳定性方面。 李雅普诺夫函数通常用于证明动力系统在一些条件下是稳定的。一个李雅普诺夫函数是一个实数值函数&#xff0c;通常表示为 V…

【Django学习笔记(二)】CSS语言介绍

CSS语言介绍 前言正文1、CSS 快速了解2、CSS 应用方式2.1 在标签上应用2.2 在head标签中写style标签2.3 写到文件中 3、问题探讨&#xff1a;用Flask框架开发不方便4、选择器4.1 ID选择器4.2 类选择器4.3 标签选择器4.4 属性选择器4.5 后代选择器4.6 注意事项 5、样式5.1 高度和…