python线性表_Python3数据结构与算法之线性表

在此开始记录数据结构与算法(基于Python的)学习,主要参考视频https://www.bilibili.com/video/av21540971/?p=1

线性表

线性表有两种存储结构:顺序存储结构与链式存储结构。

1 顺序表

比如 int 类型的数据1,2,3,4要以顺序表格式存储,首先每个整型数据占4个字节(每个字节8位),计算机会分配4*4个连续的字节空间给它们,让它们依次存储。python中 list 可以放置不同类型的元素,比如 a = [1, 'adaf' , 2.6],不同元素每个所占内存又不同,它是怎么存储的尼?它其实是元素外置的顺序表。虽然每个元素所占的空间大小不同,可是储存元素的起始地址所占大小一样。比如1,占四个字节,它可能是从0x100(地址)开始存储;‘adaf\o’占五个字节,它可能从0x11开始存储。注意要存储地址编号,所需要的空间大小是一样的。所以 list 是顺序存储的 0x100,0x11...。其实python中已经分装好了,不需要考虑。

其他语言中构建一个顺序表的完整信息应该包括两个部分:表头(告知应该分配多大空间,里面包含多少元素)eg, li[8],真实信息。但是Python是动态语言,其实不需要考虑表头。表头与数据区的存储又分为:一体式与分离式两种。分离式好

2 链表

2.1 单链表

单链表就是元素+节点(节点指向下一个)

classSingleNode(object):'''单链表的节点'''

def __init__(self,item):#_item存放数据元素

self.item =item#_next存放下一个节点的标识

self.next = None

单链表的操作:

1)is_empty()  链表是否为空

2)length()  链表长度

3)travel()  遍历整个链表

4) add(item) 链表头部添加元素

5)append(item) 链表尾部添加元素

6)insert(pos, item) 指定位置添加元素

7)remove(item) 删除节点,注:相同元素时,删除第一个,查找也是一样

8)search(item) 查找节点是否存在

编程前先理解一下python中的等于,方便编程。

为什么,a = 1, b =2 , a,b = b,a 可以。为什么python中变量不用声明,int a = 1。可以理解为引用。

f73d4037e282b85eeaede57f140d82f1.png

如图,a中存放的是一个地址,地址指向1这个数,同理b;然后箭头交叉就可以了。所以python中的等于有点像->

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#!/usr/bin/env python#-*-coding:utf-8 -*-

classNode(object):#节点

def __init__(self, elem):#_item存放数据元素

self.elem =elem#_next存放下一个节点的标识

self.next =NoneclassSingleLinkList(object):"""单链表"""

def __init__(self, node=None):

self.__head =nodedefis_empty(self):"""链表是否为空"""

return self.__head==Nonedeflength(self):"""链表长度"""

#定义cur游标,用来移动遍历节点

cur = self.__head

#count记录数量

count =0while cur !=None:

count= count + 1cur=cur.next#退出循环时,cur指向的是尾节点的下一个(None)

returncountdeftravel(self):"""遍历整个链表"""

#定义cur游标,用来移动遍历节点

cur = self.__head

while cur !=None:print(cur.elem, end=" ")

cur=cur.nextprint("")defadd(self, item):"""单链表头部添加元素"""

#对于任意一种插入,一般都是先动插入那个节点的指针,最后再打破原来的链表

node =Node(item)

node.next= self.__headself.__head =nodedefappend(self, item):"""链表尾部添加,尾插法"""node=Node(item)#处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了

ifself.is_empty():

self.__head =nodeelse:

cur= self.__head

while cur.next !=None:

cur=cur.next#退出循环时,cur指向的是尾节点

cur.next =nodedefinsert(self, pos, item):'''指定位置添加,单项循环与单链表一样

:param pos: 从0开始

:param item:

:return:'''

#考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插

if pos<=0:

self.add(item)elif pos > self.length()-1:

self.append(item)else:

pre= self.__head #这时游标指向的是第一个节点,不是head

count =0while count < (pos-1):

count= count+1pre=pre.next#pre指向的是插入位置的前一个节点,比如插入位置是2,这时指向的是位置1的节点

#插入一般都是先设置插入节点的指针(游标),保证链表不断,然后再改变原有链表哪个节点的next

node =Node(item)

node.next=pre.next

pre.next=nodedefremove(self,item):"""删除指定元素的节点(多个相同删除第一个)"""cur= self.__headpre=Nonewhile cur !=None:if cur.elem ==item:#特殊情况,看是否是头节点

if cur == self.__head:#头结点的情况

self.__head =cur.nextelse:#找到删除节点

pre.next =cur.nextreturn

else:#同步移动两个游标,先移动前一个

pre =cur

cur=cur.nextdefsearch(self, item):"""查找节点是否存在"""cur= self.__head

while cur !=None:if cur.elem ==item:returnTrueelse:

cur=cur.nextreturnFalseif __name__ == '__main__':'''测试'''

#先创建单链表对象

ll =SingleLinkList()print(ll.is_empty())print(ll.length())

ll.append(1) #尾插

ll.append(2)

ll.append(3)

ll.add(8) #头插

ll.insert(1,0.5) #位置插

ll.insert(0, 7.5)

ll.insert(10, 4)

ll.travel()#假如是print(ll.travel()),输出会多一个None

ll.remove(7.5) #删除头

ll.remove(2)

ll.remove(4)

ll.travel()print(ll.search(1))

单链表

2.2 单向循环链表

与单链表的区别就在于它的尾节点指向链表的第一个元素。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#!/usr/bin/env python#-*-coding:utf-8 -*-

classNode(object):#节点

def __init__(self, elem):#_item存放数据元素

self.elem =elem#_next存放下一个节点的标识

self.next =NoneclassSingleCycleLinkList(object):"""单向循环链表"""

def __init__(self, node=None):

self.__head =node#单项循环,有节点时得指向自己

ifnode:

node.next=nodedefis_empty(self):"""链表是否为空"""

return self.__head==Nonedeflength(self):"""链表长度"""

ifself.is_empty():return0#定义cur游标,用来移动遍历节点

cur = self.__head

#count记录数量

count = 1 #必须从1开始,与单链表不同

while cur.next != self.__head:

count= count + 1cur=cur.nextreturncountdeftravel(self):"""遍历整个链表"""

ifself.is_empty():return

#定义cur游标,用来移动遍历节点

cur = self.__head

while cur.next != self.__head:print(cur.elem, end=" ")

cur=cur.next#退出循环,cur指向尾节点,但是尾节点的元素没有打印

print(cur.elem)defadd(self, item):"""单向循环链表头部添加元素"""node=Node(item)ifself.is_empty():

self.__head =node

node.next=nodeelse:

cur= self.__head

while cur.next != self.__head:

cur=cur.next#退出循环,cur指向尾节点

node.next = self.__headself.__head =node#cur.next = node # 与下面那行等价

cur.next = self.__head

defappend(self, item):"""链表尾部添加,尾插法"""node=Node(item)#处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了

ifself.is_empty():

self.__head =node

node.next=nodeelse:

cur= self.__head

while cur.next != self.__head:

cur=cur.next

cur.next=node

node.next= self.__head

definsert(self, pos, item):'''指定位置添加,单项循环与单链表一样

:param pos: 从0开始

:param item:

:return:'''

#考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插

if pos<=0:

self.add(item)elif pos > self.length()-1:

self.append(item)else:

pre= self.__head #这时游标指向的是第一个节点,不是head

count =0while count < (pos-1):

count= count+1pre=pre.next#pre指向的是插入位置的前一个节点,比如插入位置是2,这时指向的是位置1的节点

#插入一般都是先设置插入节点的指针(游标),保证链表不断,然后再改变原有链表哪个节点的next

node =Node(item)

node.next=pre.next

pre.next=nodedefremove(self,item):"""删除指定元素的节点(多个相同删除第一个)"""

ifself.is_empty():returncur= self.__headpre=Nonewhile cur.next != self.__head:if cur.elem ==item:#特殊情况,看是否是头节点

if cur == self.__head:#头结点的情况

rear_cur = self.__head

while rear_cur.next != self.__head:

rear_cur=rear_cur.next

self.__head =cur.next#退出循环时,rear_cur指向的是尾节点

rear_cur.next = self.__head

else:#找到删除节点

pre.next =cur.nextreturn

else:#同步移动两个游标,先移动前一个

pre =cur

cur=cur.next#退出循环,cur指向尾节点,需要判断一下当前的item

if cur.elem ==item:if cur == self.__head:#链表只有一个节点,此时pre为None

self.__head =Noneelse:

pre.next=cur.nextdefsearch(self, item):"""查找节点是否存在"""cur= self.__head

while cur.next != self.__head:if cur.elem ==item:returnTrueelse:

cur=cur.next#退出循环cur指向尾节点

if cur.elem ==item:returnTruereturnFalseif __name__ == '__main__':'''测试'''

#先创建单链表对象

ll =SingleCycleLinkList()print(ll.is_empty())print(ll.length())

ll.append(1) #尾插

ll.append(2)

ll.append(3)

ll.add(8) #头插

ll.insert(1,0.5) #位置插

ll.insert(0, 7.5)

ll.insert(10, 4)

ll.travel()#假如是print(ll.travel()),输出会多一个None

ll.remove(7.5) #删除头

ll.remove(2)

ll.remove(4)

ll.travel()print(ll.search(1))

单向循环链表

2.3 双向链表

“双向链表”又称为“双面链表”,每一个节点有两个链接:一个指向前一个,一个指向后一个。对于首节点,指向前一个的指空,同理尾节点。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#!/usr/bin/env python#-*-coding:utf-8 -*-

classNode(object):#双向节点

def __init__(self, elem):

self.elem=elem

self.prev= None #前驱

self.next = None #后驱

classDoubleLinkList(object):"""双链表,其__init__,is_empty,length,travel,search,单链表一样,故可以利用多态的概念,直接传入单链表代替object"""

def __init__(self, node=None):

self.__head =nodedefis_empty(self):"""链表是否为空"""

return self.__head isNonedeflength(self):"""链表长度"""

#定义cur游标,用来移动遍历节点

cur = self.__headcount=0while cur !=None:

count= count + 1cur=cur.next#退出循环时,cur指向的是尾节点的下一个(None)

returncountdeftravel(self):"""遍历整个链表"""

#定义cur游标,用来移动遍历节点

cur = self.__head

while cur !=None:print(cur.elem, end=" ")

cur=cur.nextprint("")defadd(self, item):"""双链表头部添加元素"""

#对于任意一种插入,一般都是先动插入那个节点的指针,最后再打破原来的链表

node =Node(item)ifself.is_empty():

self.__head =nodeelse:#插入节点的后驱指向原首节点

node.next = self.__head

#原首节点的前驱指向插入节点

self.__head.prev =node#头指向新的首节点

self.__head =nodedefappend(self, item):"""链表尾部添加,尾插法"""node=Node(item)#处理特殊情况,单链表一开始是空,cur没有.next这个属性,就出错了

ifself.is_empty():

self.__head =nodeelse:

cur= self.__head

while cur.next !=None:

cur=cur.next#退出循环时,cur指向的是尾节点

cur.next =node

node.prev=curdefinsert(self, pos, item):'''指定位置添加,单项循环与单链表一样

:param pos: 从0开始

:param item:

:return:'''

#考虑特殊情况,位置小于0,默认为是头插,大于长度,默认为是尾插

if pos<=0:

self.add(item)elif pos > self.length()-1:

self.append(item)else:

cur= self.__headcount=0while count

count= count+1cur=cur.next#退出循环时cur指向pos位置

node =Node(item)

node.next=cur

node.prev=cur.prev

cur.prev.next=node

cur.prev=nodedefremove(self,item):"""删除指定元素的节点(多个相同删除第一个)"""cur= self.__head

while cur !=None:if cur.elem ==item:#特殊情况,看是否是头节点

if cur == self.__head:#头结点的情况

self.__head =cur.nextif cur.next: #else的情况是只有一个节点,cur.next无prev

cur.next.prev =Nodeelse:#找到删除节点,cur指向要删除的节点

cur.prev.next =cur.nextifcur.next:#尾节点

cur.next.prev =cur.prevreturn

else:

cur=cur.nextdefsearch(self, item):"""查找节点是否存在"""cur= self.__head

while cur !=None:if cur.elem ==item:returnTrueelse:

cur=cur.nextreturnFalseif __name__ == '__main__':'''测试'''

#先创建单链表对象

ll =DoubleLinkList()print(ll.is_empty())print(ll.length())

ll.add(100)

ll.append(1) #尾插

ll.append(2)

ll.append(3)

ll.add(8) #头插

ll.insert(1,0.5) #位置插

ll.insert(0, 7.5)

ll.insert(10, 4)

ll.travel()#假如是print(ll.travel()),输出会多一个None

ll.remove(7.5) #删除头

ll.remove(2)

ll.remove(4)

ll.travel()print(ll.search(1))

双链表

3 有关链表的题目

3.1 单链表反转

b043ae6235e55bb2e1fc18ffe31c3d6a.png

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值