2019/11/19 01-习题单双向链表实现

在这里插入图片描述
链表数据解构很重要,不亚于列表(有顺序连续的结构)
链表是有顺序但在内存中部连续的数据结构,每个元素离的比较远,习惯称为手拉手
链表和列表的区别
链表和列表的好处,链表的好处是增删比较方便,增删对于列表就比较麻烦
查找的时候,链表比列表慢,因为链表是手拉手,一个问下一个的,是比较耗时的,

linked list,链接的表,上一个结点知道下一个结点在哪里
对于双向的链表是,上一个知道下一个,下一个知道上一个,头和尾是两个特例,一个没有上一个,一个没有下一个

链表本身是个容器,容器里面装其他元素,其他元素自己知道下一个是谁,并不是知道链表下一个谁,每一个元素知道下一个是谁,链表是一个容器,元素里放的内容合法就行
在这里插入图片描述

加个头和尾
在这里插入图片描述
加元素append,首先需要把元素包装起来,node把item包装,就获得一个元素对象,不包装不行,直接放一个item不合适,每个元素放进来除了包装意外,还需要捆绑一个下一条是谁,next在这里插入图片描述
现在拿到一个结点把它放进去,对于单向链表,追加相对来说比较简单,只要知道尾巴就可以解决了,留尾巴是代表找尾巴比较方便,不然给个开头,不知道尾巴在哪里,不然要迭代一遍才知道尾巴是谁

假设现在放第一个元素,如果一个元素都没有,追加元素相当于self.head=node,self。tail=noe,就一个元素没有上一条和下一跳
对于单向链表,head=none,说明是一个元素都没有,准备添加第一个元素进去

在这里插入图片描述‘但凡head不是none,添加元素,就准备在tail上做文章 了’,当前尾巴的下一个,就是未来要加的,然后当前尾巴还要再修正一下

第一个元素,头尾都是记录5这个元素,tail和head是同一个东西,增加一个元素,tail修改了next在这里插入图片描述
再添加3,把中间的每一次的下一跳都要修改上,tail指向3
在这里插入图片描述
第一次添加元素是当前尾巴,要指向下一个要加入的节点,下一跳解决之后,下一跳尾巴更新下就好在这里插入图片描述
这两条语句顺序能否颠倒,颠倒了以后顺序就完全错了在这里插入图片描述
这两句都有就可以注释掉,是一样的,在外面写self。tail=node在这里插入图片描述
接下来要return,不要return就不太适合做链表,return self(这个时候已经调整完了,不遍历到最后怎么append)就代表把整个链表就返回了,就调整完了,在当前尾巴加元素,tail就少了很多遍历,链表追加还是经常使用的
self。head可以解决头部追加的问题,头部移除的问题,加个tail可以解决尾部追加和移除的问题,虽然链表可以找到某个元素之后把手断开重新拉手,但是链表虽然增删速度快,但是少不了要找一遍,找的时候挺慢的,所以链表也有不好的地方,但是加了tail属性之后,现在可以说,只要在头尾增删,效率是非常高的,链表头尾增删非常多
但是使用列表的时候如果头部增删还是少用,会引起整个链表的挪动
所以list使用的时候要特别小心增删的问题,只有尾部增删可以达到O(1)的效果
在这里插入图片描述
return self回去,就可以连续调用append
在这里插入图片描述

如何来迭代,for循环在python中是在有限次数场景使用的
就可以从头开始,current =self。head
当前头如果是none就没必要进去了
当前有一个元素就yield出去,yield是yield,差一步需要挪动

在这里插入图片描述
需要挪动,current=current.next
在这里插入图片描述
出现好几个说明递归了
在这里插入图片描述
如果下一跳不是none,则把item打印出来,下一跳是none,就没有item了,1下面是2,2下面是3
在这里插入图片描述
这么写更好的理解,1指向2,2指向3在这里插入图片描述
这样单向链表就结束了,写链表的时候只要想没有数据的情况下,添加第一个节点怎么做,再想想,有 一个节点,2个节点该怎么做在这里插入图片描述
在这里插入图片描述
链表和列表,在遍历的时候谁也快不了多少,因为要找下一个,再找下一个,任意找一个就要找半天了,这就是链表的问题

如果想要从里面拿出第5个元素,按照之前的,应该遍历此链表才能找到,但是能不能塞一张列表进来,列表找索引特别快,加一个这个是本身解决链表本身不太好做的事情
在这里插入图片描述
构建一个列表来辅助,遍历有两种方式看修改个写法,iter是给一个生成器,生成器直接包一下即可在这里插入图片描述
也可以这么写,但是遍历都不会太快的,所有元素都要从里面拿出来
在这里插入图片描述
现在是借用这个列表来辅助你为它提速,这个所谓的提速,仅限于查找,如果链表增删,列表是帮不了什么忙的
在这里插入图片描述
前面代码不修改,就可以进行追加
在这里插入图片描述这两个方法就提速了,这两个方法找起来就快多了,通过列表大大提升了链表
在这里插入图片描述
但是如果插入的话,列表不能嫌烦就不管了,链表和列表应该是一致的,仅当前来看,单向链表只满足append。iternodes方法,目前来看是没有问题的,因为列表的查询速度确实比链表快,链表要一个个问,这里用列表直接给索引就立马定位了,
单向链表负索引没有什么实际意义
在这里插入图片描述
这个列表看起来解决了给一个索引随机访问的问题,看似也给了迭代的方式。但是我们用链表的目的就是要与列表有区别,想在里面增删数据,,查询虽然有,但不是主要目的
在这里插入图片描述
所以这么修改,搞的链表不像链表,列表不像列表,一旦加入增删,发现这么设计就是失败的,看是加在一起,实际谁的优势都没发出来,这种情况下只能查多改少,查询特别多,改的特别少(直接用列表岂不更好)
所以经典的链表实现,就是纯粹的链表实现,没有人把字典,set,列表挪进去,进去之后就是四不像,链表不是链表,字典不是字典
每一个数据结构有自己的特点,混用只能在某一部分增强,为了保证列表,set,字典的一致性,就要增删的时候大家一起增删,会发现写的时候越来越麻烦,对于链表没有优化
在这里插入图片描述
这上面就删除,注释掉在这里插入图片描述在这里插入图片描述
一种是纯粹的链表实现,还有一个是本来想辅助的时候借用的列表在这里插入图片描述在这里插入图片描述
看似很好其实是有一些问题的,这样单向链表是完成了

双向链表

双向链表每一个节点指向前后两个地方
在这里插入图片描述
现在节点需要加一个,指向前一个节点
在这里插入图片描述
这样就解决打印的问题
在这里插入图片描述
关键是链表如何修改,第一个元素进来,头尾都是第一个元素,所以这两句不用动
在这里插入图片描述
加了元素,要提示上一个元素指向哪个,node元素的上一个,当前的节点是当尾巴的,当前尾巴的节点是准备要做尾巴的前一个,当前尾巴的下一个,就是node,这样相互手拉手
当前节点的左边,前一个修正了,下一个不用修正,因为是none,缺省值,当前节点的前一个不用管,当前尾巴的下一个要管,要添加 一个新的进来
self.tail=node 当前尾部要修正一下

在这里插入图片描述
双向迭代,加个reverse
如果不反转就是下一个next,反转就是上一个,prev,直到上一个或者下一个是none,就到头了,while current
在这里插入图片描述
下面代码不动,试试看
在这里插入图片描述
稍微做下改动就可以了,但是要把其他功能搞定
在这里插入图片描述
pop尾部移除,remove,任意位置删除,insert中间加入在这里插入图片描述
节点可以了
在这里插入图片描述
append也搞定了
在这里插入图片描述
迭代可以两头迭代在这里插入图片描述
这样基本算达到要求了

先只接受正索引
在这里插入图片描述
然后进行迭代,enumerate,迭代元素然后配个对

在这里插入图片描述
查看是否超界,超界代表迭代完了
如果index相同就找到了那个位置上了,就代表这里肯定有个元素

在这里插入图片描述

如果是空的,if self.head is none 说明现在没有元素,全空在这里插入图片描述
如果遍历完都没找到你插入的位置,else,说明要么是你索引超界了,要么是正好超界了,是在尾巴上
for循环如果没有元素是可以直接不迭代的,
进入else,append
在这里插入图片描述
吵了,就要在队尾,追加一个元素,插入简单,但是要把关系,也要更新
这是把当前节点的上一个下一个搞定了

在这里插入图片描述
**A和 B中间加入c,c要指向B,B要指向C,A是原来B的前一个,C的上一个是A,就可以用prev **在这里插入图片描述
假设你的插入点在0的位置,在头部插入,说明当前prev,也应该是none,头部插入不能说明下一个也是none,没有关系
空的是靠self.append(value)解决的
原来AB链接,加入C,就需要都重新链接
第一种和第二种的情况做的情况不一样

在这里插入图片描述
怎么知道是不是首部
第一种是prev is none
i==0,0是因为上面enumerate还是局部变量

在这里插入图片描述

上面用哪个都可以,这两个条件是等价的
在这里插入图片描述

node= Node(value)获得一个待加入结点对象
如果是第一个元素,index==0
node.next =current
新添入的节点左右就解决完了

在这里插入图片描述
self.head,就是头要改
c代表current,前一个是A(prev),后面的元素是next,加入一个 node,前面如果本身是none,就不用进行修改了,写一个修正成current,current的前一个修正尾node,current的下一个就不用管

在这里插入图片描述
在插入的时候有三个参与方,插入的算中间,中间的后一个是谁,
如果是在头部加入修正头,尾部加入修正尾
要是尾部加入,append(value)这步就做了,轮不到 下面了

在这里插入图片描述
中间加入试试,就需要把三个点的关系调整下
在这里插入图片描述
中间插入,修改的点,有4个需要考虑在这里插入图片描述
现在这么看好像有重复的
在这里插入图片描述
就可以拿出来,里面的注释掉在这里插入图片描述
上面是尾部追加,下面是非尾部追加,分为,头部,和中间加入,在头部追加的时候需要修正头,在这里插入图片描述
这样insert方法基本搞定
insert(0,0)0 的位置加个0
在0 的位置再加个abc

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在中间加一个
在这里插入图片描述
这就是insert,下面写pop,尾部移除
就是self.tail
如果return none,区分不出来是真正没有还是真正的none

在这里插入图片描述
拿到tail,里面一定封装一个数据,通过node.item拿出数据
如果pop尾部移除,那么前一个需要作为新的尾巴

在这里插入图片描述
但是如果把尾巴拿掉,前一个是none,说明里面只有一个元素,需要清空
self.head=none
self.tail=none
在这里插入图片描述
最后return item
在这里插入图片描述
还有其他的,假设2个元素,移除尾巴,最后剩一个
代表前一个元素的下一个原来有东西,现在要改成none, prev.next=none
前一个元素就要改成tail尾巴了。self.tail=prev

在这里插入图片描述
这就是pop
在这里插入图片描述
试试pop,现在end就不见了
在这里插入图片描述
pop完,空了,就没打印了在这里插入图片描述
抛出empty,就没用了在这里插入图片描述
pop先不用了在这里插入图片描述
pop就完成了,下一步的remove相对来讲比较难
如果负索引就报错,
self.head is none代表里面什么都没有
else:
超出界了就直接删除最后一个即可

在这里插入图片描述
或者这么写
抛出异常,raise indexerror
break,说明移除的就是当前current
在这里插入图片描述
走到这一步就找到了要移除的节点
在这里插入图片描述
然后看看各种情况
要盯着index,不然不知道移除谁
一般移除头想,只需要修改头即可,移除尾只需要修改尾即可
4种情况:
如果正好是1个,移除,头尾都需要修正,prev is none and next is none
elif(代表前面测试过,一定不是前面判断过的)
prev is none 不是一个元素的链表,从头部移除
elif
next is none 不是一个元素的链表,从尾部移除
else:
不是一个元素,且从中间移除

在这里插入图片描述如果正好是1个,移除,头尾都需要修正,
self.head=none
self.tail=none
移除头部,就需要改头部
self.head=current 头修正下
current.prev=none 当前节点做老大,原来有A,B,把原来老大A去掉,B作为新的老大,原来B指向A的就不见了,B就作为current了,下面一个就爱谁谁,不变了,就动头,当前
在这里插入图片描述
修改尾巴
当前节点移除 self.tail=prev,(如果是开头)
下一个就要做开头了,netx.prev=none

在这里插入图片描述
尾部移除,
current丢弃,self.tail=prev
前一个的下一个就是none了,prev.next=none
在这里插入图片描述
中间就不用修正头和尾
修改前后两个手拉手一起
prev.next=next
next.prev=prev

在这里插入图片描述
这就是写的双向链表
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
使用list是为了查询起来速度更好在这里插入图片描述
增加列表,只能为查多,删除少的链表增速
看似兼顾优势,其实没什么
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
正反迭代如何思考,iter开头就是为了写生成器
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值