Python与数据结构[0] -> 链表[0] -> 单链表与带表头单链表的 Python 实现

单链表 / Linked List



目录

  1. 单链表
  2. 带表头单链表

链表是一种基本的线性数据结构,在C语言中,这种数据结构通过指针实现,由于存储空间不要求连续性,因此插入和删除操作将变得十分快速。下面将利用Python来完成单链表的实现。

1 单链表

不带表头的单链表通常形式如下,

node_1 -> node_2 -> node_3 -> node_4

完整代码

  1 class Node:
  2     def __init__(self, val=None, nxt=None):
  3         self.value = val
  4         self.next = nxt
  5
  6     def __str__(self):
  7         return str(self.value)
  8
  9
 10 class LinkedList:
 11     """
 12     Linked list:
 13             node_1 -> node_2 -> node_3 -> node_4
 14     """
 15     def __init__(self, iterable=()):
 16         self.header = None
 17         if iterable:
 18             self.init(iterable)
 19
 20     def __str__(self):
 21         def _traversal(self):
 22             node = self.header
 23             while node and node.next:
 24                 yield node
 25                 node = node.next
 26             yield node
 27         return ‘->‘.join(map(lambda x: str(x), _traversal(self)))
 28
 29     def init(self, iterable=()):
 30         # Note: use empty tuple rather than list to init iterable
 31         if not iterable:
 32             return
 33         self.header = Node(iterable[0])     # header value
 34         node = self.header
 35         for i in iterable[1:]:              # add all node
 36             node.next = Node(i)
 37             node = node.next
 38
 39     def show(self):
 40         print(self)
 41
 42     @property
 43     def length(self):
 44         if self.header is None:
 45             return 0
 46         node = self.header      # node pointer points to header
 47         i = 1
 48         while node.next:
 49             node = node.next    # node pointer move to next
 50             i += 1
 51         return i
 52
 53     @property
 54     def is_empty(self):
 55         return self.header is None
 56
 57     def clear(self):
 58         self.__init__()
 59         # self.header = None
 60
 61     def append(self, item):
 62         self.insert(item, self.length)
 63
 64     def find(self, item):
 65         node = self.header
 66         while node.next and node.value != item:
 67             node = node.next
 68         if node.value == item:
 69             return node
 70         return None
 71
 72     def find_previous(self, item):
 73         node = self.header
 74         while node.next and node.next.value != item:
 75             node = node.next
 76         if node.next and node.next.value == item:
 77             return node
 78         return None
 79
 80     def delete(self, item):
 81         ‘‘‘
 82         node_1 -- X --> node_2 -----> node_3
 83             \                    /
 84              \                  /
 85               ------------------
 86         ‘‘‘
 87         prev = self.find_previous(item)
 88         if prev:
 89             prev.next = prev.next.next
 90
 91     def insert(self, item, index):
 92         ‘‘‘
 93                 ----> node_2 ---
 94                /                 95               /                   96         node_1 -------  X  ---------> node_3
 97
 98         ‘‘‘
 99         if abs(index) > self.length:
100             return
101         if index < 0:
102             self.insert(item, self.length+index+1)
103             return
104         elif index == 0:
105             self.insert(self.header.value, 1)
106             self.header.value = item
107             return
108         node = self.header
109         i = 0
110         while i < index-1:
111             node = node.next
112             i += 1
113         n = node.next
114         node.next = Node(item, n)
115
116
117 def test(li):
118     print(‘Show linked list:‘)
119     li.show()
120
121     print(‘\nInit linked list:‘)
122     li.init([1, 2, 3, 4, 5, 6, ‘xd‘, 8, 9])
123     li.show()
124
125     print(‘\nInsert element:‘)
126     li.insert(‘xxd‘, -3)
127     li.show()
128
129     print(‘\nAppend element:‘)
130     li.append(‘10‘)
131     li.show()
132
133     e = ‘xd‘
134     print(‘\nFind element:‘)
135     x = li.find(e)
136     print(x.value if x else x)
137     print(‘\nFind previous element:‘)
138     x = li.find_previous(e)
139     print(x.value if x else x)
140
141     print(‘\nDelete element:‘)
142     li.delete(‘xd‘)
143     li.show()
144
145     print(‘\nFind element not exist:‘)
146     x = li.find(e)
147     print(x.value if x else x)
148
149     print(‘\nInsert element to header:‘)
150     li.insert(‘cc‘, 0)
151     li.show()
152
153     print(‘\nClear linked list:‘)
154     li.clear()
155     li.show()
156
157     print(‘\nCurrent length: %s‘ % li.length)
158     print(‘\nIs empty: %s‘ % li.is_empty)
159
160
161 if __name__ == ‘__main__‘:
162     test(LinkedList())

分段解释

首先,需要定义一个结点类(在C语言中则使用结构体进行定义),其中包括两个基本属性,当前值和下个结点。由于Python中无法使用类似C语言中的指针,因此只能通过变量的引用来实现类似指针操作的功能。为了更好的显示结点,这里重载了结点的内置函数__str__。

1 class Node:
2     def __init__(self, val=None, nxt=None):
3         self.value = val
4         self.next = nxt
5
6     def __str__(self):
7         return str(self.value)

接着,我们需要。定义一个链表类,并利用前面的结点来构建这个链表。链表类中包含了一个头结点属性,在初始化函数中,首先将头结点赋值None,类似于在C语言中将头结点指针指向NULL。初始化方法中定义了一个默认参数,使得链表可以在实例化的时候利用传入的可迭代对象进行链表生成。

1 class LinkedList:
2     """
3     Linked list:
4             node_1 -> node_2 -> node_3 -> node_4
5     """
6     def __init__(self, iterable=()):
7         self.header = None
8         if iterable:
9             self.init(iterable)

重载链表的__str__方法,在显示链表的时候会对链表进行遍历,然后以更加清晰的方式显示在输出终端上。

1     def __str__(self):
2         def _traversal(self):
3             node = self.header
4             while node and node.next:
5                 yield node
6                 node = node.next
7             yield node
8         return ‘->‘.join(map(lambda x: str(x), _traversal(self)))

定义链表的生成函数,函数中接收可迭代对象并生成新的链表。注意这里最好使用不可变的元组而不是可变的列表。

在生成时,首先将第一个元素作为头结点,然后依次将每个结点的next属性引用指向下一个结点。

1     def init(self, iterable=()):
2         # Note: use empty tuple rather than list to init iterable
3         if not iterable:
4             return
5         self.header = Node(iterable[0])     # header value
6         node = self.header
7         for i in iterable[1:]:              # add all node
8             node.next = Node(i)
9             node = node.next

链表的show方法,由于已经重载了__str__方法,这里只需要print自身即可。

1     def show(self):
2         print(self)

链表的length属性方法,用于返回链表的长度,利用装饰器定义成属性方法,调用时遍历链表计算长度最后返回即可。

 1     @property
 2     def length(self):
 3         if self.header is None:
 4             return 0
 5         node = self.header      # node pointer points to header
 6         i = 1
 7         while node.next:
 8             node = node.next    # node pointer move to next
 9             i += 1
10         return i

链表的is_empty属性方法,用于检测链表是否为空,只需要查看头结点是否为None即可。

1     @property
2     def is_empty(self):
3         return self.header is None

链表的clear方法,用于清空链表,可以选择直接初始化链表或将链表的头结点指向None。

1     def clear(self):
2         self.__init__()
3         # self.header = None

链表的append方法,用于在链表尾部添加一个元素结点,这里只要直接调用后面的insert方法并插入到链表长度的那个位置即可。

1     def append(self, item):
2         self.insert(item, self.length)

链表的find方法,用于在链表中查找并返回某个结点,查找时会从链表的头部开始,遍历查找,直到找到查找的值或遇到None,这里的find方法有两点需要注意:

  1. 查找是依据元素的值进行查找,返回找到的第一个结点;
  2. 这里的查找方法如果遇到有环链表将无法自行检测退出

最后返回查找到的结点或None。

1     def find(self, item):
2         node = self.header
3         while node.next and node.value != item:
4             node = node.next
5         if node.value == item:
6             return node
7         return None

find_previous与find类似,返回查找结点的前一个结点。

1     def find_previous(self, item):
2         node = self.header
3         while node.next and node.next.value != item:
4             node = node.next
5         if node.next and node.next.value == item:
6             return node
7         return None

delete方法用于删除结点,基本思路在于,找到需要删除结点node_2的前一个结点node_1,将前一个结点node_1的next指针指向删除结点的下一个结点node_3,从而惰性删除了这个结点。

查找前一个结点可以使用前面定义的find_previous方法。

 1     def delete(self, item):
 2         ‘‘‘
 3         node_1 -- X --> node_2 -----> node_3
 4             \                    /
 5              \                  /
 6               ------------------
 7         ‘‘‘
 8         prev = self.find_previous(item)
 9         if prev:
10             prev.next = prev.next.next

insert方法用于向链表中插入一个结点,

基本思路在于,找到需要插入的位置前后的两个结点node_1和node_3,将node_1.next指向需要插入的结点node_2,再将node_2.next指向node_3,便完成了插入操作。

若插入的位置为头结点之前,则会替换原来的头结点为新插入的结点。

同时,这里的插入函数还支持index为负数的情况,类似于列表的负数索引。

 1     def insert(self, item, index):
 2         ‘‘‘
 3                 ----> node_2 ---
 4                /                 5               /                   6         node_1 -------  X  ---------> node_3
 7
 8         ‘‘‘
 9         if abs(index) > self.length:
10             return
11         if index < 0:
12             self.insert(item, self.length+index+1)
13             return
14         elif index == 0:
15             self.insert(self.header.value, 1)
16             self.header.value = item
17             return
18         node = self.header
19         i = 0
20         while i < index-1:
21             node = node.next
22             i += 1
23         n = node.next
24         node.next = Node(item, n)

最后,定义一个测试函数用于测试链表的各种操作。

开始时显示空表

1 def test(li):
2     print(‘Show linked list:‘)
3     li.show()

得到结果

Show linked list:
None

接着初始化链表元素

1     print(‘\nInit linked list:‘)
2     li.init([1, 2, 3, 4, 5, 6, ‘xd‘, 8, 9])
3     li.show()

得到结果

Init linked list:
1->2->3->4->5->6->xd->8->9

再向链表中插入元素

1     print(‘\nInsert element:‘)
2     li.insert(‘xxd‘, -3)
3     li.show()

得到结果

Insert element:
1->2->3->4->5->6->xd->xxd->8->9

向链表末尾添加元素

1     print(‘\nAppend element:‘)
2     li.append(‘10‘)
3     li.show()

得到结果

Append element:
1->2->3->4->5->6->xd->xxd->8->9->10

查找元素

1     e = ‘xd‘
2     print(‘\nFind element:‘)
3     x = li.find(e)
4     print(x.value if x else x)
5     print(‘\nFind previous element:‘)
6     x = li.find_previous(e)
7     print(x.value if x else x)

得到结果

Find element:
xd

Find previous element:
6

删除元素

1     print(‘\nDelete element:‘)
2     li.delete(‘xd‘)
3     li.show()

得到结果

Delete element:
1->2->3->4->5->6->xxd->8->9->10

查找不存在的元素

1     print(‘\nFind element not exist:‘)
2     x = li.find(e)
3     print(x.value if x else x)

得到结果

Find element not exist:
None

替换头结点

1     print(‘\nInsert element to header:‘)
2     li.insert(‘cc‘, 0)
3     li.show()

得到结果

Insert element to header:
cc->1->2->3->4->5->6->xxd->8->9->10

清空链表并查看链表信息

1     print(‘\nClear linked list:‘)
2     li.clear()
3     li.show()
4
5     print(‘\nCurrent length: %s‘ % li.length)
6     print(‘\nIs empty: %s‘ % li.is_empty)

得到结果

Clear linked list:
None

Current length: 0

Is empty: True

2 带表头单链表

这种单链表带有一个标志结点,通常被称为表头或哑结点,表头通常位于位置0处,通常不会被改变。

Linked list with dummy header node:
       Header -> node_1 -> node_2 -> node_3

带表头的单链表在实现时与不带表头的单链表类似,因此可以继承前面的单链表类来派生出一个带表头单链表类,其中需要进行修改和重载的方法主要有生成链表、判断链表为空和插入链表这几个方法。

 1 from linked_list import Node, LinkedList, test
 2
 3
 4 class LinkedListDummyHeader(LinkedList):
 5     """
 6     Linked list with dummy header node:
 7             Header -> node_1 -> node_2 -> node_3
 8     """
 9     def __init__(self, iterable=()):
10         self.header = Node(‘Header‘, None)
11         if iterable:
12             self.init(iterable)
13
14     def init(self, iterable=()):
15         if not iterable:
16             return
17         node = self.header
18         for i in iterable:
19             node.next = Node(i)
20             node = node.next
21
22     @property
23     def is_empty(self):
24         return self.header.next is None
25         # if self.length == 1:
26         #     return True
27         # return False
28
29     def insert(self, item, index):
30         if index == 0:
31             return
32         super(LinkedListDummyHeader, self).insert(item, index)
33
34 if __name__ == ‘__main__‘:
35     test(LinkedListDummyHeader())

__init__方法:

初始化时头结点不再指向None,而是指向表头。

init方法:

由于有表头的存在,因此创建初始链表的方法首先需要建立一个表头,再将所有元素依次加入链表中。

is_empty方法:

由于表头的存在,此时判断是否为空则可以根据表头指向结点是否为空,或链表长度是否为1来进行。

insert方法:

此处需要修改的地方在于当插入点为表头时,则应该直接返回或引发异常,无法修改表头。

最后利用单链表的测试函数进行测试得到结果

Show linked list:
Header

Init linked list:
Header->1->2->3->4->5->6->xd->8->9

Insert element:
Header->1->2->3->4->5->6->xd->xxd->8->9

Append element:
Header->1->2->3->4->5->6->xd->xxd->8->9->10

Find element:
xd

Find previous element:
6

Delete element:
Header->1->2->3->4->5->6->xxd->8->9->10

Find element not exist:
None

Insert element to header:
Header->1->2->3->4->5->6->xxd->8->9->10

Clear linked list:
Header

Current length: 1

Is empty: True

原文地址:https://www.cnblogs.com/stacklike/p/8283988.html

时间: 2024-10-10 08:34:50

Python与数据结构[0] -> 链表[0] -> 单链表与带表头单链表的 Python 实现的相关文章

带表头单向链表的创建

c语言实现 #include <stdio.h> #include <malloc.h> typedef struct node{ int data; struct node*next; } ElemSN; ElemSN*createLink (int*a,int n) { int i; ElemSN*h,*t; h=t=(ElemSN*)malloc(sizeof(ElemSN)); t->next=NULL; for(i=0;i<n;i++) { t=t->n

链表(四)——带表头的单向链表

1.带表头的单向链表 (1)不带表头的单向链表在实现插入和删除时必须区分头结点和其他节点的处理. (2)使用带表头的单向链表的好处:不用考虑头结点的单独处理. 表头节点:数据域没有值,指针域指向单向链表中数据域含值的第一个结点. 2.代表头的单向链表的基本操作 #include <stdio.h> #include <malloc.h> #define NULL 0 typedef struct node { int data; struct node *next; }ElemSN

Python与数据结构[0] -&gt; 链表[1] -&gt; 双链表与循环双链表的 Python 实现

双链表 / Doubly Linked List 目录 双链表 循环双链表 1 双链表 双链表和单链表的不同之处在于,双链表需要多增加一个域(C语言),即在Python中需要多增加一个属性,用于存储指向前一个结点的信息. Doubly linked list: node_1 <---> node_2 <---> node_3 完整代码 1 from linked_list import LinkedList, test 2 3 4 class NodeDual: 5 def __i

python实现数据结构单链表

#python实现数据结构单链表 # -*- coding: utf-8 -*- class Node(object): """节点""" def __init__(self, elem): self.elem = elem self.next = None # 节点一开始初始化的时候并不知道下一个元素的地址,所以先设置为空 class SingLinkList(object): """单链表""

小猪的数据结构辅助教程——2.2 线性表中的单链表

小猪的数据结构辅助教程--2.2 线性表中的单链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.理解顺序表以及单链表各自的有点以及缺点! 2.熟悉单链表的形式,对于头指针,头结点,尾结点,数据域和指针域这些名词要知道是什么! 3.熟悉单链表的结点结构 4.区分头指针与头结点! 5.熟悉创建单链表的两种方式:头插法和尾插法 6.了解单链表12个基本操作的逻辑 7.有趣的算法题:查找单链表的中间结点~ 1.单链表的引入(顺序表与单链表的PK) 2.单链表的结构图以及一些名

13输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。

转载请注明出处:http://www.cnblogs.com/wuzetiandaren/p/4250795.html 声明:现大部分文章为寻找问题时在网上相互转载,此博是为自己做个记录记录,方便自己也方便有类似问题的朋友,本文的思想也许有所借鉴,但源码均为本人实现,如有侵权,请发邮件表明文章和原出处地址,我一定在文章中注明.谢谢. 题目:输入一个单向链表,输出该链表中倒数第k个结点.链表的倒数第0个结点为链表的尾指针. 题目分析: 1.链表的倒数第0个结点为链表的尾指针,设为r,则r指向最后一

【 C# 数据结构】(一) -------------------------- 泛型带头节点的单链表,双向链表实现

在编程领域,数据结构与算法向来都是提升编程能力的重点.而一般常见的数据结构是链表,栈,队列,树等.事实上C#也已经封装好了这些数据结构,在头文件 System.Collections.Generic 中,直接创建并调用其成员方法就行.不过我们学习当然要知其然,亦知其所以然. 本文实现的是链表中的单链表和双向链表,并且实现了一些基本方法 一. 定义一个链表接口 MyList 接口里声明了我们要实现的方法: interface MyList<T> { int GetLength(); //获取链表

C/C++,数据结构实现两个链表的合并(尾插法建立单链表,求链表长度,直接插入排序)

1题目 实现两个链表的合并 2基本功能要求: 1.建立两个链表A和B,链表元素个数分别为m和n个. 2.假设元素分别为(x1,x2,-xm),和(y1,y2, -yn).把它们合并成一个线性表C,使得: 当m>=n时,C=x1,y1,x2,y2,-xn,yn,-,xm 当n>m时,C=y1,x1,y2,x2,-ym,xm,-,yn 3.输出线性表C: 用直接插入排序法对C进行升序排序,生成链表D,并输出链表D. 3测试数据: (1)A表(30,41,15,12,56,80) B表(23,56,

【数据结构复习】线性表的链式存储--单链表

链表的重点概念: 数据域和指针域 头指针和头结点 下面是单链表的实现源码: // // main.c // DataStructure // // Created by SuooL on 15/5/22. // Copyright (c) 2015年 SuooL. All rights reserved. // #include "stdio.h" #include "string.h" #include "ctype.h" #include &