数据结构 | 如何一文搞定链表问题?(附20本书获奖名单)
这是本公号的第127篇原创。
近期看到一个数据结构题目,翻转链表。动手写了下代码,手生了不少,发现好铁不用也会生锈,大脑也如此。
于是就整体回顾了一下链表的常见操作和数据结构题,整理下分享出来,万一对读者有帮助呢?想必那是极好的!目录如下~
链表基础知识
链表常见操作
链表常见题目
链表基础知识
链表和顺序表相对应,顺序表将表中的元素按照顺序放置于连续的存储区间,便于访问和查找。而链表则将元素放置于一系列结点中,各结点空间不要去连续,虽然给访问和查找带来了一定不便,但是在元素的删除和插入等操作上有着得天独厚的优势。(数组和链表结构的在复杂度方面的优缺点你get到了吗?)
链表的结点包括数据域和指针域两部分,顾名思义,数据域保存着元素的数据信息,指针域保存着下一个结点的指针标识。当然 Python 中没有指针这个概念,刷题中也是模拟指针的概念进行操作。
谈到链表一定不能避开的两个定义:头结点和头指针。
头结点的设立是为了便于操作,指的是放在第一个元素结点前的结点。不是链表结构必不可少的一部分。头指针则不一样,属于必不可少的一部分,指向链表结构的第一个结点(头结点存在的时候指向头结点)。通常我们对链表的访问和各操作都是基于头指针进行的。
链表分类则可以根据不同方式:
从内存角度出发: 链表可分为 静态链表、动态链表。
从链表存储方式的角度出发: 链表可分为 单链表、双链表、以及循环链表。
链表常见操作
链表的常见操作包括但不局限于求链表长度、结点的删除、插入等,但小詹之前文章说过许多次,Python 中由于不存在指针,所以指针和链表指的都是模拟指针和链表。所以这里的常见操作应当加上链表的构建操作。
1.首先是用 Python 构建链表
如上所述,链表由一系列结点组成,所以构建链表的第一步是定义一个结点类。
class Node(object): """ 定义一个节点类 """ def __init__(self,data): self.data = data self.next = None
有了结点,下一步就是构建链表了。如下的 InitList() 方法用一个非空 data 构建的链表,并定义了一个 PrintList() 方法打印出链表结果。
class LinkList(object): """ 创建一个链表类,包括判断是否为空、打印链表 """ def __init__(self): self.head = Node(None)
def InitList(self, data): if len(data) == 0: print('\nIt is a empty link list') return False
self.head = Node(data[0])#头结点,data的一个元素为数据域信息 p = self.head #头指针,指向第一个结点
for i in data[1:]: node = Node(i) p.next = node p = p.next
def IsEmpty(self): p = self.head #指向第一个结点
def IsEmpty(self): p = self.head #指向第一个结点 if p == None:# print('\nIt is a empty link list') return True #是空链表 # print('\nIt is not empty') return False #非空链表
def PrintList(self): p = self.head
while p: #注意不是p.next print(p.data, end = ' ') #end表示结束的标识,不添加说明时默认为换行符 p = p.next
这是一个简单的构建链表方法并将其打印出来,可以测试下结果。
def main(): """ 测试 """ data = [1, 2, 3, 4, 5] lst = LinkList() lst.InitList(data) lst.PrintList()
main()
对应的结果为:
1 2 3 4 5
2.其次是链表长度的求取
链表长度的求取在使用中经常遇到,所以构建了一个链表后,我们应当把一些常见的操作封装到一起,为此,可以创建一个求表长的方法,通过便历链表所有结点即可。
def Size(self): """ 求取链表长度 """ p = self.head size = 0
while p: size += 1 p = p.next return size
3.还有常见的结点删除与插入
开篇提到顺序表对元素的查找和访问比较便捷,但是涉及到元素删除和插入就比较鸡肋了,链表中就相反,非常的便捷!
需要注意情况分类,有大概 3 种情况。且删除位置 n 应当有区间限制。
当链表只有一个结点的时候
当删除的是第一个结点的时候
其他普适情况
对于第一种,只需要直接将头结点为 None 即可;第二种情况和普适情况类似,只是普适情况的前驱结点在第二种情况为头结点。重点还在普适情况的删除操作,可以简单的将前一个结点的指针指向待删除结点的下一个结点即可跳过该结点。代码如下所示:
def Del(self,n): """ 删除第n个结点,注意特殊边界情况 """ if self.IsEmpty() or n < 0 or n > self.Size(): print('error occured') return
p = self.head
if p.next is None: # 当只有一个节点的链表时 self.head = None return
if n == 1: # 当删除第一个节点时 self.head = p.next return
#普通情况 p = self.head index = 1 while index < n:#找到要删除的结点位置 pre = p index += 1 p = p.next pre.next = p.next#跳过该结点即可删除 p = None
以上是删除操作,考虑了各种删除情况。插入操作和删除有点类似倒转的感觉,只需要找到待插入位置,打断前后两个结点的指针,改变 3 者的指向关系即可。插入位置n范围应当在闭区间(0,链表长度)之间。值得注意的是这里也需要分情况讨论,许多教程忽略了边缘情况,这里着重讨论下。
插入的链表为空链表
插入在链表的头结点处
普适情况
前俩种情况类似,只用将插入结点做头结点即可,普适情况只需要将该结点的前后位置结点指针做些调整即可,代码给出如下,可以手动画图方便理解噢。
def Insert(self,n,data): """ 把data插入第n个结点之后的位置 """ if n < 0 or n > self.Size() + 1: #n最小值为0,且插入位置超过长度+1时实际只能插入到最后,即最大值为链表长【0,size】区间内有效 print('error occured') return
p = self.head
if self.IsEmpty():#空链表时插入头指针之后即可 node = Node(data) p.next = node return
if n == 0: node = Node(data) lat = self.head self.head = node #新插入的做头结点 node.next = lat #新插入结点后续指向之前的头结点 return
index = 1 while index < n: index += 1 p = p.next
#插入操作关键之处,注意指针变换顺序 node = Node(data) node.next = p.next p.next = node
链表常见题目
这里重要讲解 2 个题型。
1.链表翻转问题
简单的说就是将一个单链表翻转过来,这是 LeetCode 的第 206 题,在链表题目中很有分量。
输入:1->2->3->4->5输出:5->4->3->2->1
这个题目其实不算难,思路比较清晰,只需要从前往后依次将结点指向颠倒,例如:
原 始:1->2->3->4->5第1轮:5<-4 3->2->1第2轮:5<-4<-3 2->1第3轮:5<-4<-3<-2 1第4轮:5<-4<-3<-2<-1
关键在于指针的转换顺序不能颠倒变换,建议自己按照我上边给的示例进行绘图帮助理解,代码如下:
class Solution: """ leetcode题目中提交函数都只用放在此类下 """ def reverseList(self, head): """ 翻转操作leetcode题目206 """ cur, pre = head, None while cur: cur.next, pre, cur = pre, cur, cur.next #这一行等同于如下 4 行: #lat = cur.next #cur.next = pre #pre = cur #cur = lat
return pre
2.链表有环问题(LeetCode第141题)
链表有环问题也是一个常见的问题了,之前在总结双指针类型问题(见文末推荐阅读)时候讲过,可以去回顾下。思路如下:
定义两个指针 ,一快一慢 。比如慢指针一次移动 1 个位置 ,快指针移动 2 个 。
初始快慢指针放在一个位置 ,并开始循环移动 。
如果有环 ,那么随着移动的进行 ,终有快指针经过环遇到并超过慢指针的时候 ,那么这就可以用来判断是否存在环的依据啦 。
前几天送的 20 本签名书名单出来啦,恭喜这些 B 如此幸运,快来看看有没有你自己,如果没有,继续混脸熟,希望下一次就是你!记得添加微信领奖(python_jiang),24小时后视作弃权噢~
名单:FavoreiteCheese;tj;阿飘-冲锋版;明日之前;老孙9857;魏星;Nevermore阳;部落大圣;D;mascot;微笑;Cosmos;飞;心雨;杜明罗;,。;陈伟;梅破知春近;寂夜;LuoYe;
推荐阅读:
LeetCode 系列——双指针问题 。
LeetCode | 你不得不了解的哈希算法 !
数据结构 | 如何一文搞定链表问题?(附20本书获奖名单)相关推荐
- 最强绘图AI:一文搞定Midjourney(附送咒语)
最强绘图AI:一文搞定Midjourney(附送咒语) Midjourney官网:https://www.midjourney.com 简介 Midjourney是目前效果最棒的AI绘图工具.访问Mi ...
- 一文搞定CSS(附思维导图下载)
文章目录 思维导图 知识点概要 一.CSS选择的艺术 CSS基础语法 CSS选择器 类选择器 id选择器 群组选择器 全局选择器 后代选择器 伪类 CSS继承与层叠样式表 CSS选择器优先级 CSS命 ...
- 一文搞定数据结构(图解)
搞定顺序表: https://blog.csdn.net/qq_44443986/article/details/111387150 搞定链表: https://blog.csdn.net/qq_44 ...
- 一文搞定面试中的二叉树问题
一文搞定面试中的二叉树问题 版权所有,转载请注明出处,谢谢! http://blog.csdn.net/walkinginthewind/article/details/7518888 树是一种比较重 ...
- 一文搞定MAVLINK软件协议
转载:https://mp.weixin.qq.com/s/iGURlSS7V-5iBCEtgpzT7w 一文搞定MAVLINK软件协议 原创 L君 TBUS社区 2019-11-06 本文纯属资深程 ...
- php带参数单元测试_一文搞定单元测试核心概念
基础概念 单元测试(unittesting),是指对软件中的最小可测试单元进行检查和验证,这里的最小可测试单元通常是指函数或者类.单元测试是即所谓的白盒测试,一般由开发人员负责测试,因为开发人员知道被 ...
- 【Python基础】一文搞定pandas的数据合并
作者:来源于读者投稿 出品:Python数据之道 一文搞定pandas的数据合并 在实际处理数据业务需求中,我们经常会遇到这样的需求:将多个表连接起来再进行数据的处理和分析,类似SQL中的连接查询功能 ...
- 一文搞定Swing和Qt按钮和文本框的创建
一文搞定Swing和Qt按钮和文本框的创建 Qt的截图 java的 源码 package com.lujun;import java.awt.Container;import javax.swing. ...
- 一文搞定C#关于NPOI类库的使用读写Excel以及io流文件的写出
一文搞定C#关于NPOI类库的使用读写Excel以及io流文件的写出 今天我们使用NPOI类库读写xlsx文件, 最终实现的效果如图所示 从太平洋官网下载相应的类库,大概4~5MB,不要从github ...
最新文章
- Leetcode(18)-四数之和
- 解决vscode中golang插件依赖安装失败问题
- 任务一,转换学习思路
- 用Socket编写TCP程序(C/C++)(转)
- ThreadPoolExcutor 线程池 异常处理 (上篇)
- 在recovery模式下命令控制手机_安卓手机开机密码忘记,解锁教程
- Linux内存是怎么工作的
- Tcl Tutorial 笔记4 ·if
- Tomcat加载类机制 - 我们到底能走多远系列(14)
- Julia Rust : 看递归上的运行效率PK
- w ndows10更新后变成32位了,三种直接从 Win7 升级到 Win10 的方法
- 思科ASA防火墙接口区域基本信息
- 计算机睡眠状态网络是断开的吗,电脑进入睡眠模式会自动断网如何解决
- 接入Internet的技术有哪几种?各有什么特点?
- 如何与设计师更顺畅沟通
- Linux中的计划任务—Crontab调度一次性执行的任务at/batch
- 悟透JavaScript - 对象素描
- Ubuntu apt卸载
- 钉钉总裁叶军与智办事CEO胡志强就企业绩效数字化进行了深度对谈
- 音视频开发常用分析工具介绍
热门文章
- jQuery按键事件keyup
- 主流mes厂商_MES为什么可以成为企业核心
- 耳机不分主从是什么意思_“在网吧上网给玩家配置这样的耳机,是什么意思?”哈哈哈...
- android源码编译烧鸡,android4.0源码下载 编译 系统体验~图解
- java capacity_关于Java中StringBuffer的capacity问题
- 转:PHP 获取今日、昨日、上周、本月的起始时间戳和结束时间戳的方法
- php post 500 ajax,php – Laravel 5:Ajax Post 500(内部服务器错误)
- ios 修复 内存泄露_iOS 内存泄漏如何解决
- python批量执行linux命令并写入log日志
- python出现的次数最多的元素_Python cookbook(数据结构与算法)找出序列中出现次数最多的元素算...