python列表是顺序表还是链表_Python数据结构与算法(链表使用详解)

链表

单向链表

e2b470b63f238a218b34f62c5a45bbaf.png

p是头节点,指向第一个值,最后一个是伪节点,因为不指向地址。

表元素域elem用来存放具体的数据

链接域next用来存放下一个节点的位置(python中的标识)

变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点

节点实现

C中*代表指向地址,但Python中没有表示地址的符号/运算,那么,如何实现链表结构呢?

首先,来看一下:

a = 10 b = 20 a,b = b,a

上面实现a,b值的互换的本质是:

a = 10, 实际上是创建了 a 和 10 两个块, 地址a指向10

b = 20, 同上,等号的含义其实就包含了地址操作的意思,如下图

b0b69383b7330164d0da07a55236f5d6.png

执行a,b = b,a 时, 先执行右边, 执行后为 a,b = 20,10

按照1、2步骤中所说的等号的含义,这个时候 a指向20 , b 指向10 ,完成了数据交换

同理,如果是:

def f(): pass a=f

这样也是可以的,如下图:

3d8738ca94adfbde64da3981b367378a.png

690a33fbbf662a16972ebde15916ee5a.png

所以在Python中实现时,没有一个特殊的类型叫做地址,使用下一个链接的时候,就专门用个属性,它等于谁,就预示着它的区域指向了哪个节点

然后,来实现:

首先,要把节点定义一下,节点包含ele(元素)和next(下一个元素的地址 )

定义一个节点的类(通用写法,不使用元组/列表):

class Node(object): """节点""" def _init_(self,elem): self.elem = elem

self.next = None pass pass #在使用时,又一个节点,就去构造一个node node = Node(100) #节点构造的时候,就保存100(元素值) node2 = Node() class SingleLinkList(object): """单链表""" def _init_(self,node = None): #设置node = None这个默认参数是为了让用户在不传node的时候可以构造空链表 self.__head = node # __表示私有属性。 这个构造函数表示我之前定义好了一个node后,我可以把node 传进来, 作为这个链表的头节点 #使用: node = Node(100) # SingleLinkList(node) def is_empty(self): #面对对象的方法 """链表是否为空""" pass def length(self): """链表长度""" pass def travel(self): """遍历整个链表""" pass def add(self,item): #item 指节点 """链表头部添加元素""" pass def insert(self,pos,item): #item 指节点 """指定位置添加元素""" pass def remove(self,item): #item 指节点 """删除节点""" pass def search(self,item): #item 指节点 """查找节点是否存在""" pass #使用时,先创建对象,思考:链表的构造函数写什么? 因为头节点需要指向第一个节点,不然构不成链表,所以 SingleLinkList()的构造函数中应该定义一下头节点指向第一个节点,并且,这是针对对象的定义,而不是针对属性的,定义见上面代码。 single_obj = SingleLinkList() #SingleLinkList相当于一种新的数据结构类型 single_obj.search

之后,来看具体这些方法如何实现:

针对上面的程序

01e211761ab8299dad10e74114b2fd53.png

538f3f443073f6278dac92f074be3547.png

理解上面两个类之间的关系后,便很好写函数了:

class SingleLinkList(object): """单链表""" def _init_(self,node = None): self.__head = node def is_empty(self): """链表是否为空""" return self.__head == None # return true or false def length(self): #链表长度即节点数目,理解分析见**附图一** """链表长度""" #cur 游标,用来遍历节点 理解见**附图二** cur = self.__head #count 记录数量 count = 0 whlie cur != None: count += 1 cur = cur.next return count def travel(self): """遍历整个链表""" cur = self._head

whlie cur != None: print(cur.elem) cur = cur.next def add(self,item): #理解见**附图三、四** 先改变新节点,不然会打破链表结构 """链表头部添加元素""" node = Node(item) node.next = self.__head

self.__head = node def append(self,item): """链表尾部添加元素""" node = Node(item) #先定义好要插入的节点,这里的item是数值 if self.__head == None: self.__head == node else: cur = self._head while cur.next != None: cur = cur.next cur.next = node def insert(self,pos,item): #理解见**附图四、五**, pre为了方便理解,因为要在下一个位置之前才方便操作 """指定位置添加元素""" if pos <= 0: self.add(item) elif pos > (self.length()-1): self.append(item) else: pre = self.__head

count =0 while count < (pos-1): count += 1 pre = pre.next node = Node(item) node.next = pre.next pre.next = node def remove(self,item): #略 """删除节点""" pass def search(self,item): #略 """查找节点是否存在""" pass if _name_ == "_main_": ll = SingleLinkList() print(ll.isempty())

118e49a9ecd137d48d9ff308b78a519b.png

附图1 用于理解length函数

6c3d7596e58fe4a6a3e25cc4eb729be7.png

附图2

204d77b152653bdfd9b4a294aae0fbcd.png

附图3 add之前

54b568cb880584ac934c8e1fdef40d53.png

附图4 add之后的效果

4a3cfc82d4b578eca095dc7c2e82d7ef.png

附图5 insert之前的效果

48535a14ed78f4138b5651686a2533d4.png

附图6 insert之后的效果

对比单链表和顺序表

操作

链表

顺序表

访问元素

O(n)

O(1)

在头部插入/删除

O(1)

O(n)

在尾部插入/删除

O(n)

O(1)

在中间插入/删除

O(n)

O(n)

注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。

链表在存数据时,可以是分散的数据区域,不需要一块连续的数据区域,可以更好地利用计算机资源,通过链接起来,但是因为还要储存地址,耗的空间也更多

顺序表在存、取元素的时候可以O(1),但缺点在于,要求的存储空间连续,当要保存的数据量很大时,没有那么多连续的存储空间,那么就不能用顺序表

本文地址:https://blog.csdn.net/Yulu_Gan/article/details/109038851

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值