3-链表(单向链表+单向循环链表+双向链表)

一、单向链表

1、链表的提出

(1)链表和顺序表的区别???

顺序表的特点:存储空间必须连续,而且一旦不够的情况下就涉及到动态的改变数据区。

思考:有没有一种数据结构,能够在进行扩充的时候,原有的数据完全不用变,你多一个,我就增加一个。====>链表

c4b2d78c79621f1ff1b9c5593ad3084d9ce.jpg【简单的指向】===>链表的操作:7e0a7ea4c70ba35de00f6ede58584d4e0cb.jpg

【所以:链表的实现方式,把数据分成两部分:数据区+链接区

(2)链表和顺序表统称为:线性表

  • 顺序表:将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示。

  • 链表:将元素存放在通过链表构造起来的一系列存储块中。

2、单链表的ADT模型

单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。

SouthEast

  • 表元素域elem用来存放具体的数据。
  • 链接域next用来存放下一个节点的位置(python中的标识)
  • 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。

3、单链表与顺序表的对比

链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。

链表与顺序表的各种操作复杂度如下所示:

操作链表顺序表
访问元素o(n)o(1)
在头部插入/删除o(1)o(n)
在尾部插入/删除o(n)o(1)
在中间插入/删除o(n)o(n)

注:

        虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作

        链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)

        顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。

分析:

(1)原有顺序表访问元素,在尾部插入/删除的时间复杂度都是O(1),但是换成链表时,时间复杂度是O(n),为什么还要用链表呢?

答:假设要存储一个巨大的数据,而操作系统没有一块连续的内存给你分配,这时候可以使用链表。

因为链表可以把整个内存中所有分散的、只要可用的,通过链表进行串起来,但是顺序表达不到这样的效果。

(2)顺序表的优点:存取元素时,可以通过O(1)的方式一次性定位。

缺点:顺序表的空间必须是连续的,如果一旦动态的改变,整个存储区都得改变。而且若是存储大数据时,没有相应的存储空间,则达不到要求。

(3)链表的特点:对于分散的、离散的内存空间可以达到充分的利用。但是利用的同时,额外的开销也是大的。且利用链表存取元素时的时间复杂度是O(n)。

重点:(4)在中间插入/删除,链表和顺序表的时间复杂度都是O(n),但是其中的n所代表的重复的操作是不一样的

                            对于链表来说,n花费在遍历上;对于顺序表,n花费在数据搬迁上。

4、单链表:节点的实现+单链表中的操作

【操作:is_empty()链表是否为空;length()链表长度;travel()遍历整个链表;add(item)链表头部添加元素;append(item)链表尾部添加元素;insert(pos,item)指定位置添加元素;remove(item)删除节点;search(item)查找节点】

代码如下:

class Node(object):  #链表中的节点
    def __init__(self,elem):
        self.elem=elem  #元素区
        self.next=None  #刚创建的节点,默认是在链表之外,所以它的指向是Null

class SingleLinkList(object):  #单向链表

    def __init__(self,node=None):  #定义的节点默认是None
        self.__head=node  #初始化的头结点   指向   定义的node节点

    def is_empty(self):
        '''链表是否为空'''
        return self.__head==None  #链表的头节点指向None,则返回True,为空;否则不为空!

    def length(self):
        '''链表长度'''
        cur = self.__head  # cur游标,用来移动遍历节点
        count = 0  # 记录数量
        while cur != None:
            count += 1
            cur = cur.next  # 每次往后移一个位置
        return count

    def travel(self):
        '''遍历整个链表'''
        cur=self.__head  #cur游标,用来移动遍历节点
        while cur!=None:
            print(cur.elem,end=' ')
            cur=cur.next  #每次往后移一个位置
        print("")  #相当于多次调用travel时换行

    def add(self,item):
        '''链表头部添加元素'''
        node=Node(item)
        node.next=self.__head
        self.__head=node

    def append(self,item):
        '''链表尾部添加元素'''
        node=Node(item)  #将要添加的元素转化为节点
        if self.is_empty():  #如果链表为空,则将头结点指向该节点
            self.__head=node
        else:
            cur=self.__head
            while cur.next!=None:
                cur=cur.next
            cur.next=node

    def insert(self,pos,item):
        '''指定位置添加元素  pos 从0开始'''
        if pos<=0:  #为头插法
            self.add(item)
        elif pos>self.length()-1:  #尾插法
            self.append(item)
        else:
            node=Node(item)
            pre=self.__head
            count=0
            while count<(pos-1):
                pre=pre.next
                count+=1
            #跳出while循环时,count=pos-1,此时pre指向pos-1位置,在插入节点位置的前一个
            node.next=pre.next
            pre.next=node

    def remove(self,item):
        '''删除节点'''
        cur=self.__head
        pre=None
        while cur!=None:
            if cur.elem==item:  #找到这个节点
                #先判断此节点是否是头结点
                if cur==self.__head: #头结点
                    self.__head=cur.next
                else: #不是头结点
                    pre.next=cur.next
                break
            else:
                pre=cur  #先移动pre游标
                cur=cur.next  #再移动cur游标

    def search(self,item):
        '''查找节点是否存在'''
        cur=self.__head
        while cur!=None:
            if cur.elem==item:
                return True
            else:
                cur=cur.next
        return False

if __name__ == '__main__':
    l1=SingleLinkList()
    print(l1.is_empty())  #True
    print(l1.length())  #0
    l1.append(1)
    print(l1.is_empty())  #False
    print(l1.length())  #1
    l1.append(2)
    l1.append(3)
    l1.append(4)
    l1.append(5)
    l1.add(8)
    l1.travel()  # 8 1 2 3 4 5
    l1.insert(-1,9)
    l1.travel()  #9 8 1 2 3 4 5
    l1.insert(2,100)
    l1.travel()  #9 8 100 1 2 3 4 5
    l1.insert(10,200)
    l1.travel()  #9 8 100 1 2 3 4 5 200
    l1.remove(100)
    l1.travel()  #9 8 1 2 3 4 5 200
    l1.remove(9)
    l1.travel()  #8 1 2 3 4 5 200
    l1.remove(200)
    l1.travel()  #8 1 2 3 4 5

二、单向循环链表

单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点。c96029daf11e852e6cfce0bba95c4e9191e.jpg

单向循环链表:节点的实现+单链表中的操作

【单向循环链表的操作:is_empty()链表是否为空;length()链表长度;travel()遍历整个链表;add(item)链表头部添加元素;append(item)链表尾部添加元素;insert(pos,item)指定位置添加元素;remove(item)删除节点;search(item)查找节点】

class Node(object):  #节点的构造
    def __init__(self,elem):
        self.elem=elem
        self.next=None

class SingleCycleLinkList(object):  #单向循环链表

    def __init__(self,node=None):  #定义的节点默认是None     #【与单项链表不同】
        self.__head=node  #初始化的头结点   指向   定义的node节点
        if node:  #如果传入不为None
            node.next=node #设置循环

    def is_empty(self):
        '''链表是否为空'''
        return self.__head==None  #链表的头节点指向None,怎返回True,为空;否则不为空!

    def length(self):          #【与单项链表不同】
        '''链表长度'''
        if self.is_empty():  #如果链表时空链表,返回0
            return 0
        #链表不为空
        cur = self.__head  # cur游标,用来移动遍历节点
        count = 1  # 记录数量
        while cur.next!=self.__head:
            count += 1
            cur = cur.next  # 每次往后移一个位置
        return count

    def travel(self):
        '''遍历整个链表'''
        cur=self.__head  #cur游标,用来移动遍历节点
        while cur.next!=self.__head:
            print(cur.elem,end=' ')
            cur=cur.next  #每次往后移一个位置
        print("")  #相当于多次调用travel时换行

    def add(self,item):    #【与单项链表不同】  (需要找到尾节点,然后尾节点指向头结点)   【先找到尾节点,然后插入头结点,最后尾节点指向头结点】
        '''链表头部添加元素'''
        node=Node(item)

        if self.is_empty():  #若是空链表
            self.__head=node
            node.next=node
        else:  #不是空链表
            cur=self.__head
            while cur.next!=self.__head:
                cur=cur.next
            #退出循环时,cur指向尾节点
            node.next=self.__head
            self.__head=node
            cur.next=self.__head

    def append(self,item):  #【与单项链表不同】
        '''链表尾部添加元素'''
        node=Node(item)  #将要添加的元素转化为节点
        if self.is_empty():  #如果链表为空
            self.__head=node
            node.next=node
        else:
            cur=self.__head
            while cur.next!=self.__head:
                cur=cur.next
            node.next=self.__head
            cur.next=node

    def insert(self,pos,item):   #【与单项链表相同,不修改】
        '''指定位置添加元素  pos 从0开始'''
        if pos<=0:  #为头插法
            self.add(item)
        elif pos>self.length()-1:  #尾插法
            self.append(item)
        else:
            node=Node(item)
            pre=self.__head
            count=0
            while count<(pos-1):
                pre=pre.next
                count+=1
            #跳出while循环时,count=pos-1,此时pre指向pos-1位置,在插入节点位置的前一个
            node.next=pre.next
            pre.next=node

    def remove(self,item):  #【与单项链表相同,小修改】
        '''删除节点'''
        cur=self.__head
        pre=None
        while cur.next!=self.__head:
            if cur.elem==item:  #找到这个节点
                #先判断此节点是否是头结点
                if cur==self.__head: #头结点,然后找尾节点
                    rear=self.__head
                    while rear.next!=self.__head:
                        rear=rear.next
                    self.__head=cur.next
                    rear.next=self.__head
                else: #中间节点
                    pre.next=cur.next
                return ''
            else:
                pre=cur  #先移动pre游标
                cur=cur.next  #再移动cur游标
        #退出循环,cur指向尾节点
        if cur.elem==item:
            if cur==self.__head: #链表只有一个节点
                self.__head=Node
            else:
                pre.next=cur.next

    def search(self,item):  #【与单项链表大致相同,小修改】
        '''查找节点是否存在'''
        if self.is_empty():  #如果是空链表,就直接返回 False
            return False
        cur=self.__head
        while cur.next!=self.__head:
            if cur.elem==item:
                return True
            else:
                cur=cur.next
        #退出循环时,cur指向尾节点   【需要判断尾节点是否指向item】
        if cur.elem==item:
            return True
        return False

if __name__ == '__main__':
    l1=SingleCycleLinkList()
    print(l1.is_empty())  #0
    print(l1.length())  #True

    l1.append(1)
    print(l1.is_empty())  #False
    print(l1.length())  #1
    l1.append(2)
    l1.append(3)
    l1.append(4)
    l1.append(5)
    l1.add(8)
    l1.travel()  # 8 1 2 3 4 5
    l1.insert(-1,9)
    l1.travel()  #9 8 1 2 3 4 5
    l1.insert(2,100)
    l1.travel()  #9 8 100 1 2 3 4 5
    l1.insert(10,200)
    l1.travel()  #9 8 100 1 2 3 4 5 200
    l1.remove(100)
    l1.travel()  #9 8 1 2 3 4 5 200
    l1.remove(9)
    l1.travel()  #8 1 2 3 4 5 200
    l1.remove(200)
    l1.travel()  #8 1 2 3 4 5

三、双向链表

每个节点有两个链接:一个指向前一个节点,当此节点为第一个节点时,指向空值;

                                而另一个指向下一个节点,当此节点为最后一个节点是,指向空值。

1719787a3454977e5e9136ffc864970e41b.jpg

双向链表:节点的实现+单链表中的操作

【单向循环链表的操作:is_empty()链表是否为空;length()链表长度;travel()遍历整个链表;add(item)链表头部添加元素;append(item)链表尾部添加元素;insert(pos,item)指定位置添加元素;remove(item)删除节点;search(item)查找节点】

class Node(object):  #节点
    def __init__(self,elem):
        self.elem=elem  #元素区
        self.next=None  #刚创建的节点,默认是在链表之外,所以它的后继节点为Null
        self.prev=None  #刚创建的节点,默认是在链表之外,所以它的前驱节点为Null

class DoubleLinkList(object):  #双向链表

    def __init__(self,node=None):  #定义的节点默认是None    【与单链表相同】
        self.__head=node  #初始化的头结点   指向   定义的node节点

    def is_empty(self):  #【与单链表相同】
        '''链表是否为空'''
        return self.__head==None  #链表的头节点指向None,则返回True,为空;否则不为空!

    def length(self):  #【与单链表相同】
        '''链表长度'''
        cur = self.__head  # cur游标,用来移动遍历节点
        count = 0  # 记录数量
        while cur != None:
            count += 1
            cur = cur.next  # 每次往后移一个位置
        return count

    def travel(self):  #【与单链表相同】
        '''遍历整个链表'''
        cur=self.__head  #cur游标,用来移动遍历节点
        while cur!=None:
            print(cur.elem,end=' ')
            cur=cur.next  #每次往后移一个位置
        print("")  #相当于多次调用travel时换行

    def add(self,item):     #【与单链表不同,只需要加入一行】
        '''链表头部添加元素'''
        node=Node(item)
        node.next=self.__head
        self.__head=node
        node.next.prev=node   #增加一行

    def append(self,item):  #【与单链表不同,只需要加入一行】
        '''链表尾部添加元素'''
        node=Node(item)  #将要添加的元素转化为节点
        if self.is_empty():  #如果链表为空,则将头结点指向该节点
            self.__head=node
        else:
            cur=self.__head
            while cur.next!=None:
                cur=cur.next
            cur.next=node
            node.prev=cur  # 增加一行

    def insert(self,pos,item):  #【与单链表不同】
        '''指定位置添加元素  pos 从0开始'''
        if pos<=0:  #为头插法
            self.add(item)
        elif pos>self.length()-1:  #尾插法
            self.append(item)
        else:
            cur=self.__head
            node=Node(item)
            count=0
            while count<(pos-1):
                cur=cur.next
                count+=1
            #跳出while循环时,cur指向pos位置
            node.next=cur
            node.prev=cur.prev
            cur.prev.next=node
            cur.prev=node

    def remove(self,item):  #【与单链表不同】
        '''删除节点'''
        cur=self.__head
        while cur!=None:
            if cur.elem==item:  #找到这个节点
                #先判断此节点是否是头结点
                if cur==self.__head: #头结点
                    self.__head=cur.next
                    if cur.next:  #判断链表是否只有一个节点
                        cur.next.prev=None
                else: #不是头结点
                    cur.prev.next=cur.next
                    if cur.next:
                        cur.next.prev=cur.prev
                break
            else:
                cur=cur.next  #移动cur游标

    def search(self,item):  #【与单链表相同】
        '''查找节点是否存在'''
        cur=self.__head
        while cur!=None:
            if cur.elem==item:
                return True
            else:
                cur=cur.next
        return False

if __name__ == '__main__':
    l1=DoubleLinkList()
    print(l1.is_empty())  #True
    print(l1.length())  #0
    l1.append(1)
    print(l1.is_empty())  #False
    print(l1.length())  #1
    l1.append(2)
    l1.append(3)
    l1.append(4)
    l1.append(5)
    l1.add(8)
    l1.travel()  # 8 1 2 3 4 5
    l1.insert(-1,9)
    l1.travel()  #9 8 1 2 3 4 5
    l1.insert(2,100)
    l1.travel()  #9 8 100 1 2 3 4 5
    l1.insert(10,200)
    l1.travel()  #9 8 100 1 2 3 4 5 200
    l1.remove(100)
    l1.travel()  #9 8 1 2 3 4 5 200
    l1.remove(9)
    l1.travel()  #8 1 2 3 4 5 200
    l1.remove(200)
    l1.travel()  #8 1 2 3 4 5

注:

    双向链表DoubleLinkList()中,可以使用继承,因为其部分操作(is_empty(),length(),travel(),search(item))与单链表SingleLinkList()中相应的操作相同。

转载于:https://my.oschina.net/pansy0425/blog/3070785

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值