数据结构与算法(二)单链表(Singly linked list)

  • 链表(Linked list)
  • Python完整功能实现
  • LeetCode思想实践:

链这个东西大家一定都不陌生, 脖子上有项链, 自行车上有车链, 手表上有表链…

今天我们要研究的单链表的逻辑结构就和他们如出一辙

链表(Linked list)

是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

链表按照其结构可以分为

  • 单向链表
  • 双向链表
  • 循环链表
  • 块状列表
  • 拓展

链表中最简单的一种是单向链表,它包含两个域,一个信息域(data field)和一个指针域(‘next’ field)。指针域指向链表中的下一个节点。最后一个节点则指向一个空值(None)。可以在单链表上执行的操作包括插入,删除和遍历。

从引用单向链表的首节点的变量可以找到这个表的首节点(图中的data field == 12),从表中任一结点可以找到保存着该表下一个元素的结点(表中下一结点)。这样,从首节点出发就能找到这个表里的任一个结点。结点之间通过结点链接建立起单向的顺序联系。

我们把保存着首节点链接(引用或标识)的变量成为表头指针表头变量

如果一个表头指针的值是空链接,就说明‘它所引用的链接已经结束’,这是没有元素就已结束,说明该表为空表

我们照例还是用代码实现一遍方便理解

链是由结点构成的, 为了方便操作, 我们先定义一个简单的表结点类:

class LNode(object):                 # 定义一个节点类def __init__(self, value, next_n=None):self.value = valueself.next = next_n

前面已经讲过了, 一个结点包含两个域, data和next.

下面考虑单链表的操作,这里主要关注加入元素删除元素

  1. 加入元素

有顺序的东西, 当你想插入的时候, 自然要考虑插入的位置, 可以是首端插入、尾端插入或者定位插入。注意, 在插入时, 我们并不需要移动已有的数据, 只需要给新元素一个结点, 改变next的值, 连在正确的位置即可。

  • 首端插入

第一步, 创建我们要插入的结点, 并输入数据;
第二步, 将新结点的next域更新为原来旧表头的链接, 这一操作将原表的一串结点链接在刚创建的新结点之后;
第三部, 将新结点更新为表头指针。

new_node = LNode(new_value)
new_node.next = head
head = new_node
  • 一般位置插入

一般位置插入就要更新位置前后两个结点的next值, 前值指向新结点, 新结点指向后值

new_node = LNode(new_value)
new_node.next = pre.next #前结点原本保存的是后结点的链接,所以直接赋给新结点
pre.next = new_node      #新结点的链接赋值给前结点的next

如果是尾部就不需要更新新结点的next, 默认为None即可

  1. 删除元素

插入的逻辑明白之后, 删除自然也就明白了, 就是换next呗

删除首元素

直接将表头后一个元素的链接更新为表头即可, Python解释器会自动回收丢弃的结点

head = head.next

删除其他位置

将要删除的结点的前一个结点的next域修改为要删除的结点的后一个结点的链接即可

pre.next  = pre.next.next
#如果pre是0号结点,那么pre.next对应的是1号结点,pre.next.next就是2号

Python解释器同样会自动回收丢弃的结点

总结一下链表操作的复杂度

1)创建空表:O(1)
2)删除表:在python中是O(1)。当然,Python解释器做存储管理也需要时间
3)判断空表:O(1)
4)加入元素:
首端加入元素:O(1)
尾端加入元素:O(n),因为需要找到表的最后结点
定位加入元素:O(n),平均情况和最坏情况

5)删除元素:
首端删除元素:O(1)
尾端删除元素:O(n)
定位删除元素:O(n),平均情况和最坏情况\

6)扫描、定位和遍历操作都需要检查一批表结点,其复杂度受到表结点数的约束,都是O(n)操作。

Python完整功能实现

转载一个单链表的实现程序, 将上边的思路完整的捋顺一下

作者:Vico_Men 来源:CSDN
原文:https://blog.csdn.net/qq_28031525/article/details/53583857

class LNode(object):                 # 定义一个节点类def __init__(self, elem, next_=None):self.elem = elemself.next = next_class LinkListUnderflow(ValueError):       # 自定义一个异常passclass Llist(object):                   # 定义一个链表def __init__(self):self._head = Nonedef is_empty(self):                # 判断链表是否为空,通过self._head来判断return self._head is Nonedef prepend(self, elem):           # 在链表的头部加上元素self._head = LNode(elem, self._head)def pop(self):                     # 删除表头节点并返回其中的值if self._head is None:         # 无结点raise LinkListUnderflow('in pop')e = self._head.elemself._head = self._head.nextprint edef append(self, elem):if self._head is None:              # 在链表的结尾处添加元素self._head = LNode(elem)returnp = self._headwhile p.next is not None:p = p.nextp.next = LNode(elem)def pop_last(self):                      # 删除链表最后的一个元素if self._head is None:raise LinkListUnderflow('in pop_last')p = self._headif p.next is None:e = p.elemself._head = Nonereturn ewhile p.next.next is not None:           # 找到链表的倒数第二个节点p = p.nexte = p.next.elemp.next = Nonereturn edef printall(self):              # 自定义打印出来的结果p = self._headwhile p is not None:print p.elemp = p.nextdef rev(self):                   # 列表翻转的操作p = Nonewhile self._head is not None:q = self._headself._head = q.next                 # 摘下原来的首节点q.next = p                          # 将摘下来的节点放到p引用的节点序列中p = qself._head = p                          # 反转的结点序列做好后,重置表头链接def sort1(self):                           # 通过移动表中的元素进行排序if self._head is None:returncart = self._head.next                  # 从首节点之后的结点开始处理while cart is not None:p = self._headx = cart.elemwhile p is not cart and p.elem <= x:    # 跳过小的元素p = p.nextwhile p is not cart:                    # 置换大的元素和现在的值y = p.elemp.elem = xx = yp = p.nextcart.elem = x                           # 回填最后的一个元素cart = cart.next

LeetCode思想实践:

#142 Linked List Cycle II

https://leetcode.com/problems/linked-list-cycle-ii/

给定一个链表找循环起点的游戏

拿到题以后, 就没明白传入的变量是什么, 让人一头雾水

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution(object):def detectCycle(self, head):""":type head: ListNode:rtype: ListNode"""

看到example里的input, 本以为iuput是一个列表!?? 但是!!! 函数传入的类型是ListNode!!! 我一下就没明白这是怎样的一个对应关系, 本以为需要自己拿列表数据做链, 直到我验证了head的内容。

     print(head.val) #得到的结果竟然是3***再测试一下next***print(head.next.val) #得到的结果是2

这也就是说明, 提供给我们的是串好的链!!! 直接干就完了

昨天我们做了#202 Happy Number, 这和今天这题不就是一回事儿吗

昨天我们把平方和后得到的结果放在字典里, 如果 in dict 为 True 就return False

我们把结点放在字典里, 如果 in dict 为 True, 那这个交叉点不就找到了么, 是不是一回事儿

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution(object):def detectCycle(self, head):""":type head: ListNode:rtype: ListNode"""dict = {}while head and head.next:if head in dict:return headelse:dict[head] = 0head = head.nextreturn None

判断单链表中是否有环,找到环的入口节点, 我们可以使用双指针追逐法: 快指针每次移动2, 慢指针每次移动1

如图所示,链表头是X,环的第一个节点是Y,slow和fast第一次的交点是Z。各段的长度分别是a,b,c,如图所示。环的长度是L。

fast先走, 与slow在Z会和是已经走了 a+b+nL (#可能已经循环了n圈)
slow走了 a + b, 因为fast的速度是slow的二倍所以 fast走了 2a+2b

我们可以得到等式 a+b+nL = 2a+2b

所以a = nL -b = c

所以, 当快慢指针相遇时, 再有一指针从表头走, 因为路径长度相同, 所以再次会和时, 所在结点就是交点

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution(object):def detectCycle(self, head):""":type head: ListNode:rtype: ListNode"""slow = fast = headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextif slow == fast:slow = headwhile fast != slow:fast = fast.nextslow = slow.nextreturn fastreturn None

#206 Reverse Linked List

https://leetcode.com/problems/reverse-linked-list/

反转一个单链表, 迭代法和递归法都行

递归我们会在后面讨论

迭代解一下:

class Solution:def reverseList(self, head):""":type head: ListNode:rtype: ListNode"""if head is None:return Nonethis = Nonepre = Nonewhile head:this = head.nexthead.next = prepre = headhead = thisreturn pre

今天的学习到这里就要结束了

感谢那些为我们提供资料的大佬们:

  1. 数据结构与算法(单链表) https://blog.csdn.net/qq_28031525/article/details/53583857
  2. python编程(指针) https://blog.csdn.net/feixiaoxing/article/details/79504712
  3. [Leetcode-142] Linked List Cycle II(链表有环详细分析)https://blog.csdn.net/xy010902100449/article/details/48995255
  4. 维基百科链表 https://zh.wikipedia.org/wiki/链表

下次见

数据结构与算法(二)单链表(Singly linked list)相关推荐

  1. 数据结构与算法:单链表(利用万能指针实现对任意类型数据进行操作)

    前言 C语言的指针真的很强大,万能指针更强大,可以指向任意类型的数据.在上篇博客 数据结构与算法:单链表(超详细实现)中用C语言实现了单链表的相关算法,不过却有局限性 只能针对某一种数据类型还是不够强 ...

  2. 数据结构与算法之单链表

    数据结构与算法之单链表 //链表的实现/*实现单链表的 构建.数据添加.数据删除(返回元素所在位置).数据查找(返回元素所在的位置)的算法设计:*/ //链表的实现/*实现单链表的 构建.数据添加.数 ...

  3. 数据结构与算法:单链表(超详细实现)

    实现算法预览 这次博主写的单链表主要实现了以下算法.所有功能可进行循环运行测试.欢迎各位指正. LinkList.h #pragma once #ifndef __LINKLIST_H__ #defi ...

  4. 数据结构与算法之单链表(1)

    在说头插法建立单链表之前,先补充几个知识点: 用typedef定义类型:意思就是可以用typedef声明新的类型名来代替已有的类型名. 例如: typedef struct { int month; ...

  5. 数据结构与算法篇-单链表

    ????????关注后回复 "进群" ,拉你进程序员交流群???????? 作者丨写代码的牛顿 来源丨编程学习总站 01 - 单链表结点定义以及操作函数声明 我们知道在C语言里数组 ...

  6. (数据结构与算法)单链表与双链表增删改查的实现。

    文章目录 链表介绍 1. 单链表应用实例 1.1 实现思路 1.2 代码实现 2.单链表常见面试题 2.1 求单链表中有效节点的个数 2.2 查找单链表中倒数第K个节点 2.3 单链表的反转 2.4 ...

  7. 数据结构和算法之单链表

    package com.company;import java.util.Stack;/*** @author:抱着鱼睡觉的喵喵* @date:2021/2/4* @description:*/ pu ...

  8. java宋江,Java编程内功-数据结构与算法「单链表」,

    package com.structures.linkedlist; public class SingleLinkedListDemo { public static void main(Strin ...

  9. 单链表 Singly linked list

    Singly linked list with initialized value in All nodes. 从尾部插入一个节点 从头部插入一个节点

  10. 【数据结构与算法】单链表的Java实现

    链表 数据结构的逻辑表示有4种类型: 集合:元素之间无关 线性:元素之间1->1的关系 树:元素之间1->many的关系 图:元素之间many->many的关系 最基础的线性结构的简 ...

最新文章

  1. offsetof使用小结
  2. linux 命令下删除字符,【Linux基础】tr命令替换和删除字符
  3. 5、jeecg 笔记之 minidao 条件判断
  4. 猎豹浏览器插件无法加载怎么办 插件无法加载解决方法
  5. spring整合使用activemq
  6. 笨办法学 Python · 续 练习 27:`tr`
  7. BZOJ.3575.[HNOI2014]道路堵塞(最短路 动态SPFA)
  8. 深度学习自学(十一):Aborted at 1558257386 (unix time)
  9. java点歌系统代码_ktv 一个用java写的ktv点歌系统,用ACCESS数据库 Develop 238万源代码下载- www.pudn.com...
  10. 使用正则表达式把关键字替换加粗
  11. Gradle 4.8.1基本配置
  12. echarts如何获取后端的值_echarts图怎么用从后台获取的数据
  13. EXCEL集成工具箱
  14. 形式化验证和功能验证VC Formal-synopsys芯片验证基础篇(六)
  15. 谢烟客---------Linux之bash编程
  16. B站手机APP缓存视频(m4s)转换为mp4格式
  17. Python自动生成代码 - 通过tkinter图形化操作生成代码框架
  18. 做seo软文编辑撰写经验
  19. 仓库 “http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release”
  20. 短视频去水印(不需要下载任何app)

热门文章

  1. 国家绿色数据中心名单(第一批)
  2. 计算机软件系统三类,计算机的应用软件分为哪三类
  3. MFC+Halcon实现相机的实时采集+保存采集图片
  4. Android字节跳动一面,被面试官吊打
  5. Capto 1.2.19 屏幕截图录像工具
  6. js Number parseInt()
  7. 【Scratch考级99图】图21-等级考试scratch绘制复杂图形4个凹形图 少儿编程 scratch画图案例教学
  8. 直流电机闭环PID控制
  9. linux统计单词程序,linux统计单词数
  10. linux pcm接口调试,Linux的alsa接口播放pcm音频数据