数据结构之:链表

news2024/11/17 13:31:07
链表初体验之单链表
线性表
线性表 " 线性存储结构 " —— 一根线能串起来的数组 存储到物理空间之中
数据需要有相同的数据类型
元素之间的关系 需要是 一对一
两种存储方式 顺序 链式

 链表介绍

分为有头节点的链表和没有头节点的链表。
插入的时候,分为 头插法和尾插法。
节点的关系,称之为前置节点和后继节点。
节点的构成,包含数据本身,以及对后继节点的引用。

 

 

单链表之倒数第 k 个节点
链表中倒数第 k 个节点
https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
链表中倒数第 k 个节点
输入一个链表,输出该链表中倒数第 k 个节点。为了符合大多数人的习惯,本题从 1 开始计数,即
链表的尾节点是倒数第 1 个节点。例如,一个链表有 6 个节点,从头节点开始,它们的值依次是
1 2 3 4 5 6 。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表 : 1->2->3->4->5, k = 2.
返回链表 4->5

分析: 

解法1
先遍历出链表的总长度 n 倒数第 k 个节点 = 从头遍历的第 n-k+1 个节点
    public static ListNode getKthFromEnd(ListNode head, int k) {
        int n = 0;
// 先求出链表总长度
        ListNode tmp = head;
        while (tmp.next != null) {
            n++;
            tmp = tmp.next;
        }
// 再次遍历 找到第n-k+1个节点
        tmp = head;
        for (int i = 0; i < n - k + 1; i++) {
            tmp = tmp.next;
        }
        return tmp;
    }

解法2:

先定义额外指针 找到正数第 k 个节点
两个指针同时向后移动 当快的指针到达链表最后一个节点时
慢的指针 正好到达倒数第 k 个节点
( 相当于慢的指针没走的 k 由快的指针帮忙走完了 )

 

 

    public static ListNode getKthFromEnd1(ListNode head, int k) {
// 快慢指针
        ListNode slow = head;
        ListNode fast = head;
// 正数第k个节点
        for (int i = 1; i < k; i++) {
            fast = fast.next;
        }
        System.out.println(fast.val);
        while (fast.next != null) {
            slow = slow.next;
            fast = fast.next;
            System.out.println("slow移动到" + slow.val + ",fast移动到" +
                    fast.val);
        }
        return slow;
    }
单链表之反转链表
反转链表
https://leetcode-cn.com/problems/reverse-linked-list/
反转链表
反转一个单链表。
示例 :
输入 : 1->2->3->4->5->NULL
输出 : 5->4->3->2->1->NULL
进阶 :
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
迭代的方法分析:
让一个节点的 next 指向前置节点 (2->1)
让原 next 节点指向自身 (3->2)
也就是,记录前置节点,变更指向

 

每一次迭代的操作分析
在引用更改时,会丢失原 next 节点的引用,需要用临时指针 tmp 记录一下

 

    public static ListNode reverseList(ListNode head) {
// 记录前置节点 和当前节点
        ListNode pre = null;
        ListNode cur = head;
// 不断移动cur节点 向后遍历 同时更改其next
// 当cur 为空时 遍历完成
        while (cur != null) {
            ListNode tmp = cur.next;
// 更改指向
            cur.next = pre;
            if (cur != null && pre != null) {
                System.out.println("让" + cur.val + "的next指向" + pre.val);
            }
// pre和cur向后移动
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
双链表的那些事儿
双链表
1 )链表和数组的区别
优点,链表实现了真正的动态,不需要处理固定容量带来的系列问题
缺点,失去随机访问的能力
链表增删快,查询慢;数组查询快,增删慢。
2 )结构
单链表 数据 + 下一个节点的引用
双链表
数据 + 下一个节点的引用 + 对上一个节点的引用
数据 + 后置指针 + 前置指针

添加元素

 

 删除元素

 

java 的经典实现 —— LinkedList
查询方式
根据元素索引位置 找到元素
如果索引在前半段,从头往后遍历;如果索引在后半段,从后往前遍历。
环形链表的兜兜转转
环形链表
给链表加环的逻辑
    // pos 代表 尾节点指向 链表中某个节点的索引位置 (环的入口)
    public static void toCycle(ListNode node, int pos) {
// 遍历 通过pos找到 入口对应的节点 记录下来
// 遍历到尾节点时 设置为其next引用
// 记录遍历的位置
        int cnt = 0;
        ListNode cycleNode = null;
        while (true) {
// 判断是否是环的入口节点
            if (cnt == pos) {
                cycleNode = node;
            }
// 判断是否是尾节点
            if (node.next == null) {
                node.next = cycleNode;
                return;
            }
            node = node.next;
            cnt++;
        }
    }
1 )判断是否有环
https://leetcode-cn.com/problems/linked-list-cycle/
141. 环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给
定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果
pos -1 ,则在该链表中没有环。注意: pos 不作为参数进行传递,仅仅是为了标识链表的实际
情况。
如果链表中存在环,则返回 true 。 否则,返回 false
进阶:
你能用 O(1) (即,常量)内存解决此问题吗?
示例 1
输入: head = [3,2,0,-4], pos = 1
输出: true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2
输入: head = [1,2], pos = 0
输出: true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3
输入: head = [1], pos = -1
输出: false
解释:链表中没有环。
分析:
环形链表 其实是形成了一个圈
跑步时 快的人和慢的人在某一点相遇 这能倒推出跑道是环形的
快慢指针 构造一个遍历时 走一步的指针 为慢指针
走两步的指针 为快指针 如果两者在某节点相遇 可以断定链表为环形
   public static boolean hasCycle(ListNode head) {
// 链表本身不能为空 或者只有一个节点 也是无环的
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (true) {
// 没有环时 fast会先走到链表尾部
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
            if(slow != null) {
                System.out.print("slow走到" + slow.val);
            }
            if(fast != null) {
                System.out.println(",fast走到" + fast.val);
            }
            if (slow == fast) return true;
        }
    }
2) 求环的入口节点
https://leetcode-cn.com/problems/linked-list-cycle-ii/
142. 环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0
始)。 如果 pos -1 ,则在该链表中没有环。
说明:不允许修改给定的链表。
示例 1
输入: head = [3,2,0,-4], pos = 1
输出: tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
分析:
快慢指针 第一次相遇后,让 fast 重新指向头结点 head,slow 保持不变。
fast slow 按照相同速度移动,第二次相遇后,此节点即为入口节点。
[3,2,0,-4]
slow 3 2 0 -4 走了三步
fast 3 0 2 -4 走了六步 (是 slow 2 倍)
fast - slow 的步数 其实是环的长度 6-3=3
相遇点为 -4 距离环的入口节点 步数为两步
(指的是 入口节点 通往相遇点的方向距离 2->0->-4
因为 头结点 途经的路程 是从头结点出发 经过入口节点 到达相遇点的
头结点到入口节点的距离 为一步
因为环的长度已知
所以 从相遇点再行进多少步 能回到入口节点 = 环的长度 - 相遇点距离入口节点的步数
slow -4 2
fast 3 2
    public static ListNode detectCycle(ListNode head) {
// 链表本身不能为空 或者只有一个节点 也是无环的
        if (head == null || head.next == null) {
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (true) {
// 没有环时 fast会先走到链表尾部
            if (fast == null || fast.next == null) {
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
            if(slow != null) {
                System.out.print("slow走到" + slow.val);
            }
            if(fast != null) {
                System.out.println(",fast走到" + fast.val);
            }
            if (slow == fast) break;
        }
// 第一次相遇后,让fast重新指向头结点head,slow保持不变。
// fast和slow按照相同速度移动,第二次相遇后,此节点即为入口节点。
        fast = head;
        while (true){
            slow = slow.next;
            fast = fast.next;
            if(slow != null) {
                System.out.print("slow走到" + slow.val);
            }
            if(fast != null) {
                System.out.println(",fast走到" + fast.val);
            }
            if(slow == fast) return slow;
        }
    }
经典应用之约瑟夫环问题
约瑟夫环
N 个人围成一圈,从第一个人开始报数,报到 M 被杀,然后下一个人继续从 1 开始报数,循环往复,直
到剩最后一个,最后一个人的初始位置在哪里?
f(n,m)
f(3,3) -> 1
0 1 2
A B C1
A2 B
f(4,3) -> 0
0 1 2 3
A B C1 D
D A B2
D3 A
分析:
A )环形链表法
将每个人视作链表中的一个节点,当报数到 m 的时候,删除节点,直到剩下最后一个节点
当找到报数 m-1 的节点 node node.next = node.next.next
当只剩最后一个节点时 node.next = node
    public static int josephus(int n, int m) {
// 初始化环形链表
        int[] arr = new int[n];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i + 1;
        }
        ListNode node = ListNode.arrayToListNode(arr);
        ListNode.toCycle(node, 0);
// 将每个人视作链表中的一个节点,当报数到m的时候,删除节点,直到剩下最后一个节点
// 当找到报数m-1的节点 node node.next = node.next.next
// 当只剩最后一个节点时 node.next = node
        int cnt = 1;
        while (true) {
            if (cnt == m - 1) {
                System.out.println("删除节点" + node.next.val);
                node.next = node.next.next;
                cnt = 0;
            }
            node = node.next;
            cnt++;
            if (node.next == node) return node.val;
        }
    }
B )数组遍历法
将每个人视作数组中的一个元素,当报数到 m 的时候,使用占位符代表此人死掉了
当剩余人数 只有一人时 停止循环
    public static int josephus1(int n, int m) {
// 数组记录 初始值是0 使用-1代表当前元素 死掉
        int[] people = new int[n];
// 人的索引
        int index = -1;
// 报数 1 2 ... m
        int cnt = 0;
// 剩余人数
        int remain = n;
        while (remain > 0) {
            index++;
// 到达数组末端 重新从头遍历
            if (index >= n) index = 0;
// 如果此人死掉 跳过 继续报数
            if (people[index] == -1) {
                continue;
            }
// 报数到m 将index对应位置的元素 置为-1 (尸体)
            if (cnt == m) {
                people[index] = -1;
                cnt = 0;
                remain--;
            }
            cnt++;
        }
        return index;
    }


 

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

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

相关文章

化合物在高通量筛选中的作用

在 1985 年之前&#xff0c;先导物的筛选主要是通过人工进行的&#xff0c;每周处理的样本数量不过几百个&#xff0c;组合化学的出现使得科学家们获取化合物的方式发生了显著变化&#xff0c;他们可以在短时间内合成大量化合物。更重要的是&#xff0c;随着分子生物学和功能基…

【构建ML驱动的应用程序】第 1 章 :从产品目标到 ML 框架

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

SpringBoot框架详细教学保姆级说明

目录 文章目录1.1 简介1.2 特性1.3 四大核心2 springboot入门案例2.1 SpringBoot 项目开发步骤2.2 创建一个 Spring MVC 的 Spring BootController2.3 分析2.4 核心配置文件格式2.5 Spring Boot 前端使用 JSP3 SpringBoot框架Web开发3.1 Spring Boot 集成 MyBatis3.2 DAO 的其它…

D. Make It Round(math)

Problem - D - Codeforces 在Berlandia发生了通货膨胀&#xff0c;所以商店需要改变商品的价格。 商品n的当前价格已经给出。允许将该商品的价格提高k倍&#xff0c;1≤k≤m&#xff0c;k为整数。输出商品的最圆的可能的新价格。也就是在最后有最大数量的零的那个。 例如&…

开发工程师的面经

目录1. static关键字2. 多态是什么&#xff1f;3. ArrayList和LinkList的区别区别ArrayList的扩容机制4. Java是编译型还是解释型&#xff1f;5. 什么是编译&#xff1f;什么是解释&#xff1f;6. String str“abc” 和 String str new String(“abc”)的区别&#xff1f;7. i…

C\C++刷题ADY3

题目来源&#xff1a;力扣 1.第一题 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 思路分析:&#xff08;不带哨兵位的头节点&#xff09; 每次去分析一个节点&#xff0c; 如果节点不是存的是6&#xff0c;就拿节点来尾插 如果节点存的不是6&#xff0c;就把节…

计算机毕业设计ssm+vue基本微信小程序的“香草屋”饮料奶茶点单小程序

项目介绍 随着社会的发展,互联网的迅速发展,5G时代的到来,智能手机的普及,人们的生活方式更加智能一体化,衣食住行也越来越简单快捷,人们的生活也更加趋向于智能化,一台智能手机可以解决生活中的各种难题。为了使人们的生活更加方便,于是各种生活小程序普遍产生, 随着微信小程序…

记一次 .NET 某自动化采集软件 崩溃分析

一&#xff1a;背景 1.讲故事 前段时间有位朋友找到我&#xff0c;说他的程序在客户的机器上跑着跑着会出现偶发卡死&#xff0c;然后就崩掉了&#xff0c;但在本地怎么也没复现&#xff0c;dump也抓到了&#xff0c;让我帮忙看下到底怎么回事&#xff0c;其实崩溃类的dump也…

java项目-第155期ssm班级同学录网站-java毕业设计_计算机毕业设计

java项目-第155期ssm班级同学录网站-java毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm班级同学录网站》 该项目分为2个角色&#xff0c;管理员、用户。 用户可以浏览前台,包含功能有&#xff1a; 首页、公告信息、校友风采、论坛信息&#xff0c;用…

vue项目中使用 NProgress 进度加载插件

场景&#xff1a;每次进入页面时&#xff0c;页面顶部都有一个加载条 下来说下这个效果怎么实现的 第一步&#xff1a;下载 NProgress 插件 npm install --save nprogress 第二步&#xff1a;导入 nprogress 并配置 切记&#xff1a;NP 都必须是大写 我是在路由页面中导入的 im…

搜索引擎项目开发过程以及重难点整理

目录认识搜索引擎搜索的核心思路倒排索引介绍项目简介模块管理构建索引模块数据库设计正排索引倒排索引程序入口类Indexer类扫描文档子模块FileScanner类构建文档子模块构建标题parseTitle方法构建urlparseUrl方法构建内容parseContent方法构建正排索引子模块正排索引实体类map…

Nginz静态资源缓存

缓存 缓存&#xff08;cache&#xff09;&#xff0c;原始意义是指访问速度比一般随机存取存储器&#xff08;RAM&#xff09;快的一种高速存储器&#xff0c;通常它不像系统主存那样使用DRAM技术&#xff0c;而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发…

【前端-TypeScript】TypeScript学习思维导图-一图看完《TypeScript编程》

目录前言文章已收录至https://lichong.work&#xff0c;转载请注明原文链接。 ps&#xff1a;欢迎关注公众号“Fun肆编程”或添加我的私人微信交流经验&#x1f91d; 前言 现在&#xff0c;TypeScript 正在逐渐成为与前端框架以及 ES6 语法同一地位的基础工具&#xff0c;更多…

计算机毕业设计node+vue基于微信小程序的西餐外卖系统 uniapp 小程序

项目介绍 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代,西餐外卖系统就是信息时代变革中的产物之一。 任何系统都要遵循系统…

力扣每日一题:808. 分汤 【dp动态规划】

有 A 和 B 两种类型 的汤。一开始每种类型的汤有 n 毫升。有四种分配操作&#xff1a; 提供 100ml 的 汤A 和 0ml 的 汤B 。提供 75ml 的 汤A 和 25ml 的 汤B 。提供 50ml 的 汤A 和 50ml 的 汤B 。提供 25ml 的 汤A 和 75ml 的 汤B 。 当我们把汤分配给某人之后&#xff0c;汤…

CentOS8 安装 erlang 和 RabbitMQ

哈喽大家好&#xff0c;我是阿Q&#xff01; 最近正好用到了消息队列中的 RabbitMQ ,今天就先来个前味菜&#xff0c;总结一下它在 centos 内的安装。 环境&#xff1a;CentOS 8.0 64位 安装erlang 由于 rabbitmq 是基于 erlang 语言开发的&#xff0c;所以必须先安装 erlang…

大数据项目之电商数仓、日志采集Flume、source、channel、 sink、Kafka的三个架构

文章目录4. 用户行为数据采集模块4.3 日志采集Flume4.3.1 Kafka的三个架构4.3.1.1 source4.3.1.2 channel4.3.1.3 sink4.3.1.4 kafka source4.3.1.5 kafka sink4.3.1.6 kafka channel4.3.1.6.1 第一个结构4.3.1.6.2 第二个结构4.3.1.6.3 第三个结构4. 用户行为数据采集模块 4.…

CentOS7 离线部署 Python 项目

1.前言 主要过程如下&#xff1a; &#xff08;1&#xff09;创建项目环境&#xff0c;生成requirements.txt文件。&#xff08;如果已有可跳出&#xff09; &#xff08;2&#xff09;新建一个跟目标机器一样的操作系统&#xff0c;python环境的测试服务器&#xff0c;并下载…

【考研英语语法】名词性从句

0 导言 名词性从句&#xff0c;是指一个句子相当于名词来使用&#xff0c;放到另外一个句子中。通常情况下&#xff0c;名词在句子中主要作四种成分&#xff1a;宾语、表语、主语、同位语。因此&#xff0c;名词性从句就分成四种&#xff1a;宾语从句、表语从句、主语从句、同…

迅为iTOP3568开发板Android11获取root权限关闭selinux

本文档所需资料在网盘资料“iTOP-3568 开发板\02_【iTOP-RK3568 开发板】开发资料\ 06_Android 系统开发配套资料\02_Android11 获取 root 权限配套资料”目录下。本文档参考瑞 芯微官方文档&#xff0c;在源码“Android11/rk_android11.0_sdk/RKDocs/android/patches/root”目…