数据结构与算法全套精讲(python版) (四)链表

1. 链表

首先要明白一点:之所以做元素互换时,只有python里才可以直接用 a,b=b,a   ——> 就是因为做a=10时,a可以看做是一个输入待定的函数,10并不是直接放入到了a里面,二者只是一个指向关系

注:链表与之前的顺序表对比

(因为链表只能记录头节点,故要search到其中的元素,就要从头开始遍历往后找,故访问元素的时间复杂度是O(n); 而顺序表可以直接找到目标元素,故访问元素的时间复杂度是O(1),但除了在尾部的情况外,做删除或插入时,都要把操作点位置后的所有元素依次后移,故对应的为O(n))

(1). 单链表 

class Node(object):
    def __init__(self,data):
        self.data = data
        self.next = None#指向待定

class SingleLinkList(object):#表头只有data,表尾next为None
    def __init__(self,node=None):#待接节点待定
        self.__head = node#头节点为私有属性(必须双下划綫),限链表内使用

    def is_empty(self):
        return self.__head == None#头空,则链表空

    def length(self):
        cur = self.__head#cur游标,用来移动遍历节点
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count#当链表空,则头空,cur=None,不会循环,则count=0,符合

    def travel(self):#遍历
        cur = self.__head
        while cur != None:
            print(cur.data, end=' ')
            cur = cur.next
        print('')

    def add_front(self,item):#链首加元素,头不变
        node = Node(item)
        '链首本身没有next,等号代表指向,self.__head本身指向的是原head后的第一个节点,现在要把该节点接到新的节点的后面'
        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=self.head=None ,没cur.next,故需另做判断
                cur = cur.next
            cur.next = node

    def insert(self,pos,item):#指定位置加元素
        if pos <= 0:
            self.add_front(item)#链首
        elif pos > (self.length()-1):
            self.append(item)#链尾
        else:
            pre = self.__head#游标从头节点起步
            count = 0
            """
            此处有个难点:比如pos=2,即要插入到第3个位置(不算头结点),由于插入位置的前个节点的指向也需更改,
            故条件取到count < (pos-1) 而不是count < pos
            """
            while count < (pos-1):
                count += 1
                pre = pre.next
            #退出循环时,pre指向pos-1位置
            node = Node(item)
            node.next = pre.next#pre.next是被新节点占去位置的原节点,将其放到新节点的后面
            pre.next = node

    def remove(self,item):#删除节点
        '要考虑链首、尾、为空'
        cur = self.__head
        pre = None #游标cur的前个节点
        while cur != None:
            if cur.data == item:
                '先判断该节点是否为头结点'
                if cur == self.__head:
                    self.__head = cur.next#当只有头结点,将其删除后,self.__head就该是None,而cur.next就是None
                else:
                    """
                    注意:pre本身原是无节点意义的,是因为在判断条件下会先经历
                    pre = cur cur = cur.next 的位置后移,所以pre也有了和cur一样的节点属性!
                    """
                    pre.next = cur.next#把待删除节点的下个节点指给待删除节点的前个节点
                break#必须有!
            else:
                pre = cur
                cur = cur.next#pre与cur同时后移

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

if __name__ == '__main__':
    ll = SingleLinkList()
    print(ll.is_empty())
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.add_front(8)#8 1 2 3 4

    ll.insert(-1, 9)#9 8 1 2 3 4
    ll.travel()
    ll.insert(2, 100)#9 8 100 1 2 3 4
    ll.travel()
    ll.insert(10, 200)#9 8 100 1 2 3 4 200
    ll.travel()

    ll.remove(9)#删头
    ll.travel()#8 100 1 2 3 4 200
    ll.remove(200)#删尾
    ll.travel()#8 100 1 2 3 4
    ll.remove(1)
    ll.travel()#8 100 2 3 4

 

(2). 单向循环链表 

class Node(object):
    def __init__(self, data):
        self.data = data
        self.next = None  # 指向待定


'单向循环列表'
class SingleCycleLinkList(object):
    def __init__(self, node=None):  # 待接节点待定
        self.__head = node  # 头节点为私有属性(必须双下划綫),限链表内使用
        if node:
            node.next = node  # 若链表在初始时就已经传入了节点,则next指向自己

    def is_empty(self):
        return self.__head == None  # 头空,则链表空

    def length(self):
        if self.is_empty():
            return 0  # 函数遇return则执行结束
        cur = self.__head  # 游标
        count = 1  # 若非空,则至少有头节点,计数从1起
        while cur.next != self.__head:  # 至尾结束
            count += 1
            cur = cur.next
        return count

    def travel(self):  # 遍历
        if self.is_empty():  # 当空,cur = self.__head=None,但None不具备next属性,故空链要单独列出
            return
        cur = self.__head
        while cur.next != self.__head:
            print(cur.data, end=' ')
            cur = cur.next
        print(cur.data)  # 尾节点在循环退出时没打出,故补上

    def add_front(self, item):  # 链首加元素,头不变
        node = Node(item)
        '链首本身没有next,等号代表指向,self.__head本身指向的是原head后的第一个节点,现在要把该节点接到新的节点的后面'
        if self.is_empty():
            self.__head = node
            node.next = node
            return
        cur = self.__head
        while cur.next != self.__head:
            cur = cur.next
        # 退出循环,cur此时为尾节点
        node.next = self.__head  # node接到链首,此时self.__head更新后指向的是node了
        self.__head = node  # 头结点接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):  # 指定位置加元素
        if pos <= 0:
            self.add_front(item)  # 链首
        elif pos > (self.length() - 1):
            self.append(item)  # 链尾
        else:
            pre = self.__head  # 游标从头节点起步
            count = 0
            """
            此处有个难点:比如pos=2,即要插入到第3个位置(不算头结点),由于插入位置的前个节点的指向也需更改,
            故条件取到count < (pos-1) 而不是count < pos
            """
            while count < (pos - 1):
                count += 1
                pre = pre.next
            # 退出循环时,pre指向pos-1位置
            node = Node(item)
            node.next = pre.next  # pre.next是被新节点占去位置的原节点,将其放到新节点的后面
            pre.next = node

    def remove(self, item):  # 删除节点
        '要考虑链首、尾、为空'
        if self.is_empty():
            return
        cur = self.__head
        pre = None  # 游标cur的前个节点
        while cur.next != self.__head:
            if cur.data == item:
                '先判断该节点是否为头结点'
                if cur == self.__head:  # =是指向,==不是
                    # 当待删为头结点,则先找尾结点
                    rear = self.__head
                    while rear.next != self.__head:
                        rear = rear.next  # 后移, 直至找到尾节点
                    self.__head = cur.next  # cur此时就是头结点,把头指向链首cur.next,即链首成了新的头结点了
                    rear.next = self.__head  # 尾指向新的头

                else:  # 中间节点
                    """
                    注意:pre本身原是无节点意义的,是因为在判断条件下会先经历
                    pre = cur cur = cur.next 的位置后移,所以pre也有了和cur一样的节点属性!
                    """
                    pre.next = cur.next  # 把待删除节点的下个节点指给待删除节点的前个节点
                '此处不能像之前用break!因为break只是退出了while循环,而此处需要函数结束'
                return

            else:
                pre = cur
                cur = cur.next  # pre与cur同时后移
        # 退出循环,cur此时为尾节点
        if cur.data == item:
            if cur == self.__head:  # 只有头结点
                self.__head = None
            else:
                pre.next = cur.next  # cur的前节点直接指向cur的后节点

    def search(self, item):  # 查找某节点是否存在
        if self.is_empty():
            return False
        cur = self.__head
        while cur.next != self.__head:
            if cur.data == item:
                return True
            else:
                cur = cur.next
        # 退出循环,cur此时为尾节点
        if cur.data == item:
            return True
        return False


if __name__ == '__main__':
    ll = SingleCycleLinkList()
    print(ll.is_empty())
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.add_front(8)  # 8 1 2 3 4

    ll.insert(-1, 9)  # 9 8 1 2 3 4
    ll.travel()
    ll.insert(2, 100)  # 9 8 100 1 2 3 4
    ll.travel()
    ll.insert(10, 200)  # 9 8 100 1 2 3 4 200
    ll.travel()

    ll.remove(9)  # 删头
    ll.travel()  # 8 100 1 2 3 4 200
    ll.remove(200)  # 删尾
    ll.travel()  # 8 100 1 2 3 4
    ll.remove(1)
    ll.travel()  # 8 100 2 3 4

 

(3). 双向链表

class Node(object):
    def __init__(self,data):
        self.data = data
        self.prev = None#前驱节点
        self.next = None#后置节点

'双向链表'
class DoubleLinkList(object):
    def __init__(self,node=None):#待接节点待定
        self.__head = node#头节点为私有属性(必须双下划綫),限链表内使用

    def is_empty(self):
        return self.__head == None#头空,则链表空

    def length(self):
        cur = self.__head#cur游标,用来移动遍历节点
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count#当链表空,则头空,cur=None,不会循环,则count=0,符合

    def travel(self):#遍历
        cur = self.__head
        while cur != None:
            print(cur.data, end=' ')
            cur = cur.next
        print('')

    """
    由于双向链表的以上前4个属性及search()与单链表一样,所以可以直接用面向对象的继承方法,写作如下:
    class DoubleLinkList(SingleLinkList)
    则以上5个属性就无需再写了,因为直接继承过来了
    """

    def add_front(self,item):#链首加元素,头不变
        node = Node(item)
        '链首本身没有next,等号代表指向,self.__head本身指向的是原head后的第一个节点,现在要把该节点接到新的节点的后面'
        node.next = self.__head
        self.__head = node
        node.next.prev = node#比单链多一个node的下个节点对node的指向

    def append(self,item):#链表尾加元素
        node = Node(item)#待加节点
        if self.is_empty():
            self.__head=node
        else:
            cur = self.__head
            while cur.next != None:#找到表尾;当表为空,cur=self.head=None ,没cur.next,故需另做判断
                cur = cur.next
            cur.next = node
            node.prev = cur#比单链多一个cur的下个节点node对cur的指向

    def insert(self,pos,item):#指定位置加元素
        if pos <= 0:
            self.add_front(item)#链首
        elif pos > (self.length()-1):
            self.append(item)#链尾
        else:#此处与单链不同
            cur = self.__head#游标从头节点起步
            count = 0
            while count < pos:
                count += 1
                cur = cur.next
            #退出循环时,cur指向pos位置
            node = Node(item)
            """
            以下四字因操作先后顺序不同,也可改成如下:
            node.next = cur
            node.prev = cur.prev
            cur.prev = node
            node.prev.next = node
            """
            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.data == item:
                '先判断该节点是否为头结点'
                if cur == self.__head:
                    self.__head = cur.next#当只有头结点,将其删除后,self.__head就该是None,而cur.next就是None
                    if cur.next:#当只有cur这一个节点的时候,cur.next为None,是没有prev的
                        cur.next.prev = None
                else:
                    cur.prev.next = cur.next#把待删除节点的下个节点指给待删除节点的前个节点
                    if cur.next:
                        cur.next.prev = cur.prev
                break#必须有!
            else:
                cur = cur.next#pre与cur同时后移

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

if __name__ == '__main__':
    ll = DoubleLinkList()
    print(ll.is_empty())
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.add_front(8)#8 1 2 3 4

    ll.insert(-1, 9)#9 8 1 2 3 4
    ll.travel()
    ll.insert(2, 100)#9 8 100 1 2 3 4
    ll.travel()
    ll.insert(10, 200)#9 8 100 1 2 3 4 200
    ll.travel()

    ll.remove(9)#删头
    ll.travel()#8 100 1 2 3 4 200
    ll.remove(200)#删尾
    ll.travel()#8 100 1 2 3 4
    ll.remove(1)
    ll.travel()#8 100 2 3 4

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值