算法思想之链表

news2025/5/18 5:39:27

欢迎拜访:雾里看山-CSDN博客
本篇主题算法思想之链表
发布时间:2025.4.18
隶属专栏:算法

在这里插入图片描述

目录

  • 算法介绍
    • 常用技巧
  • 例题
    • 两数相加
      • 题目链接
      • 题目描述
      • 算法思路
      • 代码实现
    • 两两交换链表中的节点
      • 题目链接
      • 题目描述
      • 算法思路
      • 代码实现
    • 重排链表
      • 题目链接
      • 题目描述
      • 算法思路
      • 代码实现
    • 合并 K 个升序链表
      • 题目链接
      • 题目描述
      • 算法思路1
      • 代码实现1
      • 算法思路2
      • 代码实现2
    • K 个一组翻转链表
      • 题目链接
      • 题目描述
      • 算法思路
      • 代码实现

算法介绍

链表(Linked List)是一种基于指针或引用的动态数据结构,通过节点间的链接关系实现数据存储。其核心特点是非连续内存分配与高效增删操作,是算法设计中处理动态数据的核心工具之一。

常用技巧

  1. 画图
    画图可以让我们更加直观、形象的理解具体过程。便于我们的理解
  2. 引入虚拟头结点
    便于我们处理边界情况
    方便我们对链表进行操作
  3. 不要吝啬空间,大胆定义变量
    在进行链表操作时,多定义几个变量更有利于操作
  4. 快慢双指针
    对于判环找链表中环的入口找链表中倒数第n个节点等问题都比较好用

例题

两数相加

题目链接

2. 两数相加

题目描述

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1

在这里插入图片描述
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

算法思路

两个链表都是逆序存储数字的,即两个链表的个位数、十位数等都已经对应,可以直接相加。

在相加过程中,我们要注意是否产生进位,产生进位时需要将进位和链表数字一同相加。如果产生进位的位置在链表尾部,即答案位数比原链表位数长一位,还需要再 new 一个结点储存最高位。

代码实现

/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* cur1 = l1, *cur2 = l2;
        ListNode* newhead = new ListNode(0);
        ListNode* cur = newhead;
        int num = 0;
        while(cur1 != nullptr || cur2 != nullptr || num!= 0)
        {
            if(cur1 != nullptr)
            {
                num+=cur1->val;
                cur1 = cur1->next;
            }
            if(cur2 != nullptr)
            {
                num+=cur2->val;
                cur2 = cur2->next;
            }                
            ListNode* tmp = new ListNode(num%10);
            num/=10;
            cur->next = tmp;
            cur = tmp;
        }

        cur =  newhead->next;
        delete newhead;
        return cur;
    }
};

在这里插入图片描述

两两交换链表中的节点

题目链接

24. 两两交换链表中的节点

题目描述

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1

在这里插入图片描述
输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2

输入:head = []
输出:[]

示例 3

输入:head = [1]
输出:[1]
提示

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

算法思路

认真画图,进行模拟即可

代码实现

/**
 * 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* swapPairs(ListNode* head) {
        if(head == nullptr || head->next == nullptr)
            return head;
        ListNode* newhead = new ListNode(0);
        newhead->next  = head;
        ListNode *prev = newhead, *cur = head, *next = head->next, *nnext = next->next;
        while(cur!=nullptr && next!=nullptr)
        {
            prev->next = next;
            next->next = cur;
            cur->next = nnext;
            prev = cur;
            cur = nnext;
            if(cur)
                next = cur->next;
            if(next)
                nnext = next->next;  
        }
        prev = newhead->next;
        delete newhead;
        return prev;
    }
};

在这里插入图片描述

重排链表

题目链接

143. 重排链表

题目描述

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

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

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

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

示例 1

在这里插入图片描述
输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2

在这里插入图片描述
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

提示

  • 链表的长度范围为 [1, 5 * 104]
  • 1 <= node.val <= 1000

算法思路

画图画图画图,重要的事情说三遍~

  1. 找中间节点;
  2. 中间部分往后的逆序;
  3. 合并两个链表

代码实现

/**
 * 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) {
        if(head == nullptr || head->next ==nullptr || head->next->next == nullptr)  
            return ;
        // 1. 找到中间节点
        ListNode *slow = head,*fast = head;
        while(fast && fast->next)
        {
            slow = slow->next;
                fast=fast->next->next;
        }
        // 2. 逆序后半部分
        ListNode* head2 = new ListNode(0);
        ListNode *cur = slow->next;
        slow->next = nullptr;//断开两个链表
        while(cur)
        {
            ListNode* next = cur->next;
            cur->next = head2->next;
            head2->next=cur;
            cur = next;
        }
        // 3. 合并
        ListNode *ret = new ListNode(0);
        ListNode* prev = ret;
        ListNode *cur1 = head, *cur2 = head2->next;
        while(cur1)
        {
            prev->next = cur1;
            cur1 = cur1->next;
            prev = prev->next;
            if(cur2)
            {
                prev->next = cur2;
                prev = prev->next;
                cur2 = cur2->next;
            }    
        }

        delete head2;
        delete ret;
    }
};

在这里插入图片描述

合并 K 个升序链表

题目链接

23. 合并 K 个升序链表

题目描述

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

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

示例 1

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2

输入:lists = []
输出:[]

示例 3

输入:lists = [[]]
输出:[]

提示

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

算法思路1

合并两个有序链表是比较简单且做过的,就是用双指针依次比较链表 1 、链表 2 未排序的最小元素,选择更小的那一个加入有序的答案链表中。

合并 K 个升序链表时,我们依旧可以选择 K 个链表中,头结点值最小的那一个。那么如何快速的得到头结点最小的是哪一个呢?用堆这个数据结构就好啦~

我们可以把所有的头结点放进一个小根堆中,这样就能快速的找到每次 K 个链表中,最小的元素是哪个。

代码实现1

/**
 * 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 {
    struct cmp
    {
        bool operator()(ListNode* l1, ListNode*l2)
        {
            return l1->val > l2->val;
        }
    };
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap;
        ListNode *newhead = new ListNode(0);
        ListNode* cur = newhead;
        for(auto &iter : lists)
        {
            if(iter)
                heap.push(iter);
        }

        // 合并k个有序链表
        while(!heap.empty())
        {
            ListNode* tmp = heap.top();
            cur->next = tmp;
            heap.pop();
            cur = tmp;
            if(tmp->next)
                heap.push(tmp->next);
        }
        cur = newhead->next;
        delete newhead;
        return cur;
    }
};

算法思路2

利用递归和链表合并

  1. 特判,如果题目给出空链表,无需合并,直接返回;
  2. 返回递归结果。
    递归函数设计:
  3. 递归出口:如果当前要合并的链表编号范围左右值相等,无需合并,直接返回当前链表;
  4. 应⽤二分思想,等额划分左右两段需要合并的链表,使这两段合并后的长度尽可能相等;
  5. 对左右两段分别递归,合并[l, r]范围内的链表;
  6. 再调用 mergeTwoLists 函数进行合并(就是合并两个有序链表)

代码实现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<ListNode*>& lists) {
        return mergeSort(lists, 0, lists.size()-1);
    }
    ListNode* mergeSort(vector<ListNode*>& lists, int left, int right)
    {
        if(left>right)
            return nullptr;
        if(left == right)
            return lists[left];
        // 1. 找中间节点
        int mid = (left + right) >> 1;

        // 2.递归排序左右链表
        ListNode* l1 = mergeSort(lists, left, mid);
        ListNode* l2 = mergeSort(lists, mid + 1, right);

        // 3. 合并
        return mergeTowList(l1, l2);
    }
    ListNode* mergeTowList(ListNode* l1, ListNode*l2)
    {
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;
        
        ListNode *newhead = new ListNode(0);
        ListNode* cur = newhead;
        while(l1 && l2)
        {
            if(l1->val < l2->val)
            {
                cur->next = l1;
                l1 = l1->next;
                cur = cur->next;
            }
            else
            {
                cur->next = l2;
                l2 = l2->next;
                cur = cur->next;
            }
        }
        if(l1) cur->next = l1;
        if(l2) cur->next = l2;
    
        cur = newhead->next;
        delete newhead;
        return cur;
    }
};

在这里插入图片描述

K 个一组翻转链表

题目链接

25. K 个一组翻转链表

题目描述

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

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

示例 1

在这里插入图片描述
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2

在这里插入图片描述
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

提示

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

算法思路

本题的目标非常清晰易懂,不涉及复杂的算法,只是实现过程中需要考虑的细节比较多。

我们可以把链表按 K 个为一组进行分组,组内进行反转,并且记录反转后的头尾结点,使其可以和前、后连接起来。思路比较简单,但是实现起来是比较复杂的。

我们可以先求出一共需要逆序多少组(假设逆序 n 组),然后重复 n 次长度为 k 的链表的逆序即可。

代码实现

/**
 * 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* reverseKGroup(ListNode* head, int k) {
        if(head == nullptr) return nullptr;
        // 1. 计算需要翻转多少组
        int n = 0;
        ListNode* cur = head;
        while(cur)
        {
            n++;
            cur = cur->next;
        }
        n /= k;
        // 2.完成翻转操作
        ListNode* newhead = new ListNode(0);
        ListNode* prev = newhead, *next, *tmp;
        cur = head;
        while(n--)
        {
            tmp = cur;
            int m = k;
            while(m--)
            {
                next = cur->next;
                cur->next = prev->next;
                prev->next = cur;
                cur = next; 
            }
            prev = tmp;
        }
        prev->next = cur;
        cur = newhead->next;
        delete newhead;
        return cur; 
    }
};

在这里插入图片描述

⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oceanbase单机版上手示例

本月初Oceanbase单机版发布&#xff0c;作为一个以分布式起家的数据库&#xff0c;原来一个集群动辄小十台机器&#xff0c;多着十几台几十台甚至更多&#xff0c;Oceanbase单机版的发布确实大大降低了硬件部署的门槛。 1.下载安装介质 https://www.oceanbase.com/softwarece…

架构师面试(三十二):注册中心数据结构

问题 提到【注册中心】&#xff0c;我们对它的基本功能&#xff0c;肯定可以顺手拈来&#xff0c;比如&#xff1a;【服务注册】【服务发现】【健康检查】【变更通知】等。 透过这些基本功能&#xff0c;一个普适的注册中心的数据结构应该如何设计呢&#xff1f; 可以结合着…

《软件设计师》复习笔记(11.5)——测试原则、阶段、测试用例设计、调试

目录 1. 测试基础概念 2. 测试方法分类 3. 测试阶段 真题示例&#xff1a; 题目1 题目2 题目3 4. 测试策略 5. 测试用例设计 真题示例&#xff1a; 6. 调试与度量 真题示例&#xff1a; 1. 测试基础概念 定义&#xff1a;系统测试是为发现错误而执行程序的过程&…

闲来无事,用HTML+CSS+JS打造一个84键机械键盘模拟器

今天闲来无聊&#xff0c;突发奇想要用前端技术模拟一个机械键盘。说干就干&#xff0c;花了点时间搞出来了这么一个有模有样的84键机械键盘模拟器。来看看效果吧&#xff01; 升级版的模拟器 屏幕录制 2025-04-18 155308 是不是挺像那么回事的&#xff1f;哈哈&#xff01; 它…

Java学习手册:常见并发问题及解决方案

在Java并发编程中&#xff0c;开发者常常会遇到各种并发问题&#xff0c;这些问题可能导致程序行为不可预测、性能下降甚至程序崩溃。以下是一些常见的并发问题及其解决方案&#xff1a; 1.竞态条件&#xff08;Race Condition&#xff09; 竞态条件是指多个线程同时访问共享…

【免费下载】中国各省市地图PPT,可编辑改颜色

很多同学做PPT时&#xff0c;涉及到中国地图或省份展示&#xff0c;自己绘制和调色难度大&#xff0c;下面为大家准备了中国地图的可编辑模板&#xff0c;可以根据PPT整体色或想突出的省份&#xff0c;直接调整颜色。 需要这份数据&#xff0c;请在文末查看下载方法。 一、数…

【Reading Notes】(8.2)Favorite Articles from 2025 February

【February】 高阶智驾别被短期市占率迷住眼&#xff01;&#xff08;2025年02月01日&#xff09; 2024年&#xff0c;高阶智驾发展迅猛&#xff0c;粗略计算中国市场&#xff08;特斯拉之外&#xff09;的城市NOA车型的年度搭载量超过了100万台。但相比于中国乘用车市场2000万…

探索大语言模型(LLM):循环神经网络的深度解析与实战(RNN、LSTM 与 GRU)

一、循环神经网络&#xff08;RNN&#xff09; 1.1 基本原理 循环神经网络之所以得名&#xff0c;是因为它在处理序列数据时&#xff0c;隐藏层的节点之间存在循环连接。这意味着网络能够记住之前时间步的信息&#xff0c;并利用这些信息来处理当前的输入。 想象一下&#xf…

山东大学软件学院创新项目实训开发日志(15)之中医知识问答历史对话查看bug处理后端信息响应成功但前端未获取到

在开发中医知识问答历史对话查看功能的时候&#xff0c;出现了前后端信息获取异同的问题&#xff0c;在经过非常非常非常艰难的查询之后终于解决了这一问题&#xff0c;而这一问题的罪魁祸首就是后端没有setter和getter方法&#xff01;&#xff01;&#xff01;&#xff01;&a…

优先级队列的实模拟实现

优先级队列底层默认用的是vector来存储数据&#xff0c;实现了类似我们数据结构中学习过的堆的队列&#xff0c;他的插入和删除都是优先级高先插入和删除。下面我们来模拟实现它们常见的接口来熟悉优先级队列。 仿函数 在介绍优先级队列之前&#xff0c;我们先熟悉一个概念&a…

swagger 导入到apipost中

打开swagger json链接 保存到本地转为json格式文件 上传文件就行

熵权法+TOPSIS+灰色关联度综合算法(Matlab实现)

熵权法TOPSIS灰色关联度综合算法&#xff08;Matlab实现&#xff09; 代码获取私信回复&#xff1a;熵权法TOPSIS灰色关联度综合算法&#xff08;Matlab实现&#xff09; 摘要&#xff1a; 熵权法TOPSIS灰色关联度综合算法&#xff08;Matlab实现&#xff09;代码实现了一种…

利用deepseek+Mermaid画流程图

你是一个产品经理&#xff0c;请绘制一个流程图&#xff0c;要求生成符合Mermaid语法的代码&#xff0c;要求如下&#xff1a; 用户下载文件、上传文件、删除文件的流程过程符合安全规范细节具体到每一步要做什么 graph LRclassDef startend fill:#F5EBFF,stroke:#BE8FED,str…

leetcode0146. LRU 缓存-medium

1 题目&#xff1a;LRU 缓存 官方标定难度&#xff1a;中 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓…

SuperMap iClient3D for WebGL 如何加载WMTS服务

在 SuperMap iClient3D for WebGL 中加载WMTS服务时&#xff0c;参数配置很关键&#xff01;下面我们详细介绍如何正确填写参数&#xff0c;确保影像服务完美加载。 一、数据制作 对于上述视频中的地图制作&#xff0c;此处不做讲述&#xff0c;如有需要可访问&#xff1a;Onl…

组件自身如何向外暴露一个子组件

最近在开发是遇到一个问题,原本是在组件内的一个功能被ui设计稿给搞到了外面,产品也不同意放在子组件内。于是一个问题就来,抽出来放到外面的部分依赖的也是组件内部的数据和逻辑,所以如果外面再重写这一部分,显然浪费感情,并且又要把依赖关系挪出去,也不划算。 于是,…

《软件设计师》复习笔记(11.4)——处理流程设计、系统设计、人机界面设计

目录 一、业务流程建模 二、流程设计工具 三、业务流程重组&#xff08;BPR&#xff09; 四、业务流程管理&#xff08;BPM&#xff09; 真题示例&#xff1a; 五、系统设计 1. 主要目的 2. 设计方法 3. 主要内容 4. 设计原则 真题示例&#xff1a; 六、人机界面设…

深入解析B站androidApp接口:从bilibili.api.ticket.v1.Ticket/GetTicket到SendMsg的技术分析

前言 最近一段时间&#xff0c;我对B站的App接口进行了深入分析&#xff0c;特别是关注了认证机制和私信功能的实现。通过逆向工程和网络抓包&#xff0c;发现了B站移动端API的底层工作原理&#xff0c;包括设备标识生成机制、认证流程和消息传输协议。本文将分享这些研究成果…

UWP发展历程

通用Windows平台(UWP)发展历程 引言 通用Windows平台(Universal Windows Platform, UWP)是微软为实现"一次编写&#xff0c;处处运行"的愿景而打造的现代应用程序平台。作为微软统一Windows生态系统的核心战略组成部分&#xff0c;UWP代表了从传统Win32应用向现代应…

数据库相关概念,关系型数据库的核心要素,MySQL(特点,安装,环境变量配置,启动,停止,客户端连接),数据模型

目录 数据库相关概念 MySQL&#xff08;特点&#xff0c;安装&#xff0c;环境变量配置&#xff0c;启动和停止&#xff0c;客户端连接&#xff09; MySQL数据库的特点 Windows下安装MySQL MySQL 8.0.36&#xff08;安装版&#xff09; MySQL安装 配置Path环境变量 MySQ…