链表合集(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;
            }
            else
                pre = 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个节点的值都不同)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java NIO详解

一、概念 NIO, 即new io&#xff0c;也叫非阻塞io 二、NIO三个核心组件&#xff1a; Buffer数据缓冲区Channel通道Selector选择器 1、Buffer缓冲区 缓冲区本质上是一个可以存放数据的内存块&#xff08;类似数组&#xff09;&#xff0c;可以在这里进行数据写入和读取。此…

webpack项目打包console git分支、打包时间等信息 exec

相关链接 MDN toLocaleString child_process Node.js strftime 格式 代码 buildinfo.js const { execSync, exec } require("child_process"); // exec: 在 Windows 执行 bat 和 cmd 脚本// execSync 同步 // exec 异步// exec 使用方法 // exec(git show -s,…

notepad++里安装32位和64位的16进制编辑器Hex-Editor

这个16进制编辑器确实是个好东西&#xff0c;平时工作种会经常用到&#xff0c; 这是hex-editor的官网。这个里边只能下载32位的(64位的看最下边)&#xff0c;选一个合适的版本&#xff0c;我当时选的是最新的版本 https://sourceforge.net/projects/npp-plugins/files/Hex%20E…

[机器学习]练习KNN算法-曼哈顿距离

曼哈顿距离(Manhattan distance) 曼哈顿距离是指在几何空间中两点之间的距离&#xff0c;其计算方法是通过将两点在各个坐标轴上的差值的绝对值相加得到。在二维空间中&#xff0c;曼哈顿距离可以表示为两点在横纵坐标上的差值的绝对值之和&#xff1b;在三维空间中&#xff0…

物联网实战--入门篇之(三)嵌入式STM32

目录 一、Keil简介 二、工程结构 三、文件目录 四、STM32简介 五、编码风格 六、总结 一、Keil简介 Keil是一款常用的单片机开发工具&#xff0c;主要包含了编译、仿真、调试和开发界面(IDE)&#xff0c;后被ARM公司收购&#xff0c;与其MDK-ARM合并为MDK-ARM Keil软件包…

如何用 C++ 部署深度学习模型?

深度学习模型在诸多领域如图像识别、自然语言处理、语音识别等展现出强大的应用潜力。然而&#xff0c;模型训练与实际部署是两个不同的环节&#xff0c;许多开发者在使用Python进行模型训练后&#xff0c;出于性能、集成便利性或特定平台要求等因素&#xff0c;会选择使用C进行…

[机器学习]练习-KNN算法

1&#xff0e;&#x1d458;近邻法是基本且简单的分类与回归方法。&#x1d458;近邻法的基本做法是&#xff1a;对给定的训练实例点和输入实例点&#xff0c;首先确定输入实例点的&#x1d458;个最近邻训练实例点&#xff0c;然后利用这&#x1d458;个训练实例点的类的多数来…

基于单片机声音分贝采集和显示控制系统设计

**单片机设计介绍&#xff0c;基于单片机声音分贝采集和显示控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机声音分贝采集和显示控制系统设计&#xff0c;主要目标是实现声音分贝的实时采集、处理以及显示…

Java复习第十三天学习笔记(HTML),附有道云笔记链接

【有道云笔记】十三 3.29 HTML https://note.youdao.com/s/Ru3zoNqM 一、基本标签 HTML:超文本标记语言 定义页面结构 CSS&#xff1a;层叠样式表 页面显示的样式、排版 BootStrap JS: JavaScript 界面交互(动态交互、逻辑) JQuery <!DOCTYPE html> <html> &l…

[羊城杯 2020]EasySer

[羊城杯 2020]EasySer 进入页面&#xff0c;发现是ubuntuapache2&#xff0c;但是好像没啥用 尝试访问/robots.txt&#xff0c;得到 访问/star1.php/&#xff0c;查看源码&#xff0c;得到提示 一看就知道是ssrf&#xff0c;使用http://127.0.0.1/ser.php&#xff0c;得到…

阿里云CentOS7安装Flink1.17

前提条件 阿里云CentOS7安装好jdk&#xff0c;官方文档要求java 11&#xff0c;使用java 8也可以。可参 hadoop安装 的jdk安装部分。 下载安装包 下载安装包 [hadoopnode1 ~]$ cd softinstall/ [hadoopnode1 softinstall]$ wget https://archive.apache.org/dist/flink/flin…

【物联网】Qinghub MQTT 连接协议

基础信息 组件名称 &#xff1a; mqtt-connector 组件版本&#xff1a; 1.0.0 组件类型&#xff1a; 系统默认 状 态&#xff1a; 正式发布 组件描述&#xff1a;通过MQTT 连接网关&#xff0c;发布或订阅MQTT broker相关的数据信息。 配置文件&#xff1a; 配置文件作为MQT…

前端-css-2

1.背景样式 属性名作用属性值background-color背景颜色颜色background-image设置背景图像地址url(地址)background-repeat设置背景图像重复方式 repeat&#xff1a;重复。 repeat-x&#xff1a;横向重复。 repeat-y&#xff1a;纵向重复。 no-repeat&#xff1a;不重复。 back…

Flink集群主节点JobManager启动分析

1.概述 JobManager 是 Flink 集群的主节点&#xff0c;它包含三大重要的组件&#xff1a; ResourceManager Flink集群的资源管理器&#xff0c;负责slot的管理和申请工作。 Dispatcher 负责接收客户端提交的 JobGraph&#xff0c;随后启动一个Jobmanager&#xff0c;类似 Yarn…

ssm 设备采购管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 ssm 设备采购管理系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模…

61、服务攻防——中间件安全CVE复现K8sDockerJettyWebsphere

文章目录 K8sDockerWebSphere K8s k8s&#xff1a;简单来说&#xff0c;跟docker一样&#xff0c;是个容器系统。 k8s对外攻击面总结 常见漏洞&#xff1a;未授权访问、提权漏洞 Docker docker逃逸&#xff1a;1、由内核漏洞引起&#xff1b;2、由Docker软件设计引起&#x…

vue3+threejs新手从零开发卡牌游戏(二十):添加卡牌被破坏进入墓地逻辑

在game目录下新建graveyard文件夹存放墓地相关代码&#xff1a; game/graveyard/p1.vue&#xff0c;这里主要设置了墓地group的位置&#xff1a; <template><div></div> </template><script setup lang"ts"> import { reactive, ref,…

HTML5 和 CSS3 提高

一、HTML5 的新特性 HTML5 的新增特性主要是针对于以前的不足&#xff0c;增加了一些新的标签、新的表单和新的表单属性等。这些新特性都有兼容性问题&#xff0c;基本是 IE9 以上版本的浏览器才支持&#xff0c;如果不考虑兼容性问题&#xff0c;可以大量使用这些新特性。 声明…

架构之第三方框架pinyin4j与hutool搭配使用原理

一、工作原理 Hutool工具将包括pinyin4j等翻译工具插件绑定。实现通过spi接口的方式实现调用&#xff0c;底层实现可自由切换 注&#xff1a;Hutool绑定的pinyin插件有如下图几种。也就是没有添加maven依赖如pinyin4j等拼音插件。 注&#xff1a;若没有依赖pinyin插件。使用时…

性能与压力测试

一、性能监控 1.1 jvm内存模型—堆 由于所有的对象实例以及数组都要在堆上分配内存&#xff0c;并且堆是垃圾收集器管理的主要区域&#xff0c;也被称为“GC堆”&#xff0c;所以堆是我们优化最多考虑的地方。 首先&#xff0c;堆可细分为&#xff1a; Young区&#xff08;新…