【python】Single / Single Cycle / Double Link List

https://www.bilibili.com/video/av53583801/?p=20
學習筆記


1 Single Link List

在這裏插入圖片描述
圖片來源:https://www.bilibili.com/video/av53583801/?p=19

class Node(object):
    def __init__(self,value,next=None):
        self.value = value
        self.next = next

class SingleLinkList(object):
    def __init__(self,node=None):
        self.__head = node # 初始化頭指針指向 None

    def is_enmpty(self):
        """鏈表是否爲空"""
        return self.__head==None

    def length(self):
        """鏈表長度"""
        cur = self.__head # 頭節點
        count = 0
        while(cur): # 當前指針不指向 None 時候
            count+=1
            cur = cur.next
        return count

    def travel(self):
        """遍歷鏈表"""
        cur = self.__head # 頭節點,私有變量
        while (cur):  # 當前指針不指向 None 時候
            print(cur.value,end=" ")
            cur = cur.next
        print("")

    def append(self,item):
        """鏈表尾部添加元素"""
        node = Node(item,None) # 創建一個新節點

        if self.is_enmpty(): # 如果是空鏈表,頭指針直接指向新的節點
            self.__head = node
        else:
            cur = self.__head
            while(cur.next):
                cur = cur.next
            cur.next = node

    def add(self,item):
        """鏈表頭部添加元素"""
        node = Node(item) # 新建節點
        node.next = self.__head # 新建結點指向原來的第一個節點
        self.__head = node # 頭部節點指向新建的節點(新的第一個節點)

    def insert(self,pos,item):
        """鏈表任意位置添加元素,位置從0開始"""
        if pos <= 0: # 插入的位置小於等於0,則等價於在鏈表頭部添加元素
            self.add(item)
        elif pos > self.length()-1: # 大於鏈表長度,等價於在鏈表尾部添加元素
            self.append(item)
        else:
            cur = self.__head
            for i in range(pos-1): # 遍歷到插入到位置的前一個位置
                cur = cur.next
            node = Node(item) # 新建一個節點
            node.next = cur.next
            cur.next = node

    def search(self,item):
        """查找元素是否在鏈表中,返回布爾值"""
        cur = self.__head
        while(cur):
            if cur.value == item:
                return True
            else:
                return False

    def remove(self,item):
        """移除第一個匹配的元素"""
        """單指針,cur.next = cur.next.next"""
        """雙指針,pre.next = cur.next"""
        pre = None
        cur = self.__head
        while(cur):
            if cur.value == item:
                if cur == self.__head: # 匹配上了第一個節點,此時 pre 爲空,沒有next,所以單獨討論
                    self.__head = cur.next
                else:
                    pre.next = cur.next # 刪除節點
                break # 刪完以後就應該退出
            else: # 向後走一步
                pre = cur
                cur = cur.next

if __name__ == "__main__":
    ll = SingleLinkList()
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(1)
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.insert(-1,9)
    ll.insert(3,100)
    ll.insert(10,200)
    ll.travel()

    result = ll.search(9)
    print(result)
    result = ll.search(300)
    print(result)

    ll.remove(9)
    ll.travel()
    ll.remove(200)
    ll.travel()
    ll.remove(100)
    ll.travel()

output

is_empty: True
length 0
is_empty: False
length 1
9 8 1 100 2 3 4 5 6 200 
True
False
8 1 100 2 3 4 5 6 200 
8 1 100 2 3 4 5 6 
8 1 2 3 4 5 6 

2 Double Link List

在這裏插入圖片描述
圖片來源:https://www.bilibili.com/video/av53583801/?p=23

is_enmptylengthtravelsearch 同 Single Link List,完全可以繼承 Single Link List 類!remove 改動較大,注意要 remove 的元素是最後一個節點的時候的情況

class Node(object):
    def __init__(self,value,pre=None,next=None):
        self.value = value
        self.pre = pre
        self.next = next

class DoubleLinkList(object):
    def __init__(self,node=None):
        self.__head = node # 初始化頭指針指向 None

    def is_enmpty(self): # 同 SingleLinkList
        """鏈表是否爲空"""
        return self.__head==None

    def length(self): # 同 SingleLinkList
        """鏈表長度"""
        cur = self.__head # 頭節點
        count = 0
        while(cur): # 當前指針不指向 None 時候
            count+=1
            cur = cur.next
        return count

    def travel(self): # 同 SingleLinkList
        """遍歷鏈表"""
        cur = self.__head # 頭節點,私有變量
        while (cur):  # 當前指針不指向 None 時候
            print(cur.value,end=" ")
            cur = cur.next
        print("")

    def append(self,item):
        """鏈表尾部添加元素"""
        node = Node(item,None) # 創建一個新節點

        if self.is_enmpty(): # 如果是空鏈表,頭指針直接指向新的節點
            self.__head = node # 注意只有一個node的話,pre 和 next 都是空,不要以爲 pre 是 head
        else:
            cur = self.__head
            while(cur.next):
                cur = cur.next
            cur.next = node
            node.pre = cur # 相比於 SingleLinkList 新增

    def add(self,item):
        """鏈表頭部添加元素"""
        node = Node(item) # 新建節點
        node.next = self.__head # 新建結點指向原來的第一個節點
        self.__head.pre = node # 相比於 SingleLinkList 新增
        self.__head = node # 頭部節點指向新建的節點(新的第一個節點)

    def insert(self,pos,item):
        """鏈表任意位置添加元素,位置從0開始"""
        if pos <= 0: # 插入的位置小於等於0,則等價於在鏈表頭部添加元素
            self.add(item)
        elif pos > self.length()-1: # 大於鏈表長度,等價於在鏈表尾部添加元素
            self.append(item)
        else: # 相比與 SingleLinkList
            cur = self.__head
            for i in range(pos): # 遍歷到插入的位置
                cur = cur.next
            node = Node(item) # 新建一個節點
            node.next = cur # 先讓新建的節點搭在原來的列表上
            node.pre = cur.pre
            cur.pre = node # 再斷開原有鏈表的鏈接,搭在新建列表上
            node.pre.next = node

    def search(self,item): # 同 SingleLinkList
        """查找元素是否在鏈表中"""
        cur = self.__head
        while(cur):
            if cur.value == item:
                return True
            else:
                return False

    def remove(self,item):
        """移除第一個匹配的元素"""
        """不同於 singleLinkList,這裏不需要定義兩個指針了"""
        cur = self.__head
        while(cur):
            if cur.value == item:
                if cur == self.__head: # 匹配上了第一個節點,此時 pre 爲空,沒有next,所以單獨討論
                    self.__head = cur.next
                else:
                    cur.pre.next = cur.next # 刪除節點
                    if cur.next: # 這裏判斷是否是最後一個節點(最後一個節點的next爲none,none沒有pre)
                        cur.next.pre = cur.pre
                break # 刪完以後就應該退出
            else: # 向後走一步
                if cur.next:
                    cur = cur.next

if __name__ == "__main__":
    ll = DoubleLinkList()
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(1)
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.insert(-1,9)
    ll.insert(3,100)
    ll.insert(10,200)
    ll.travel()

    result = ll.search(9)
    print(result)
    result = ll.search(300)
    print(result)

    ll.remove(9)
    ll.travel()
    ll.remove(200)
    ll.travel()
    ll.remove(100)
    ll.travel()

結果

is_empty: True
length 0
is_empty: False
length 1
9 8 1 100 2 3 4 5 6 200 
True
False
8 1 100 2 3 4 5 6 200 
8 1 100 2 3 4 5 6 
8 1 2 3 4 5 6 

3 Single Cycle Link List

在這裏插入圖片描述
圖片來源:https://www.bilibili.com/video/av53583801/?p=25

Single Cycle Link List 在 Single Link List 的基礎上改動還是比較大的,特別是 remove 的時候。search 同 Single Link List

class Node(object):
    def __init__(self,value,next=None):
        self.value = value
        self.next = next

class SingleCycleLinkList(object):
    def __init__(self,node=None):
        self.__head = node  # 初始化頭指針指向 None
        if node: # 新建一個不爲空的循環鏈表
            node.next = self.__head

    def is_enmpty(self): # 同 SingleLinkList
        """鏈表是否爲空"""
        return self.__head==None

    def length(self):
        """鏈表長度"""
        if self.is_enmpty():
            return 0
        else:
            cur = self.__head # 頭節點
            count = 1
            while(cur.next != self.__head):
                count+=1
                cur = cur.next
            return count

    def travel(self):
        """遍歷鏈表"""
        if self.is_enmpty():
            return 0
        else:
            cur = self.__head # 頭節點,私有變量
            while (cur.next != self.__head):  # 當前指針不指向 None 時候
                print(cur.value,end=" ")
                cur = cur.next
            print(cur.value,end=" ") # 退出循環的時候,cur 指向尾節點,但尾節點的元素並沒有打印
            print("")

    def append(self,item):
        """鏈表尾部添加元素"""
        node = Node(item,None) # 創建一個新節點
        if self.is_enmpty(): # 如果是空鏈表,頭指針直接指向新的節點
            self.__head = node
            node.next = node  # 新增節點自己指向自己形成cycle
        else:
            cur = self.__head
            while(cur.next != self.__head): # 遍歷讓cur指向尾節點
                cur = cur.next
            cur.next = node
            node.next = self.__head # 形成 cycle

    def add(self,item):
        """鏈表頭部添加元素"""
        node = Node(item)  # 新建節點
        if self.is_enmpty():
            self.__head = node # 頭指向新增的節點
            node.next = node # 新增節點自己指向自己形成cycle
        else:
            cur = self.__head
            while(cur.next!=self.__head): # 遍歷讓cur指向尾節點(因爲是循環鏈表,所以尾部要指向新增的頭)
                cur = cur.next
            node.next = self.__head # 新建結點指向原來的第一個節點
            self.__head = node # 頭部節點指向新建的節點(新的第一個節點)
            cur.next = node #相比於 SingleLinkList 新增,尾部指向頭部


    def insert(self,pos,item): # 同 SingleLinkList
        """鏈表任意位置添加元素,位置從0開始"""
        if pos <= 0: # 插入的位置小於等於0,則等價於在鏈表頭部添加元素
            self.add(item)
        elif pos > self.length()-1: # 大於鏈表長度,等價於在鏈表尾部添加元素
            self.append(item)
        else:
            cur = self.__head
            for i in range(pos-1): # 遍歷到插入到位置的前一個位置
                cur = cur.next
            node = Node(item) # 新建一個節點
            node.next = cur.next
            cur.next = node

    def search(self,item):
        """查找元素是否在鏈表中,返回布爾值"""
        if self.is_enmpty():
            return False
        else:
            cur = self.__head
            while(cur.next!=self.__head): # 遍歷 1-n-1
                if cur.value == item:
                    return True
                else:
                    return False
            if cur.value == item: # 同travel,退出循環的時候,cur 指向尾節點,但尾節點的元素並沒有遍歷
                return True
            else:
                return False

    def remove(self,item):
        """移除第一個匹配的元素"""
        if self.is_enmpty():
            return
        else:
            pre = None
            cur = self.__head
            while(cur.next!=self.__head):
                if cur.value == item: ### 匹配到了第一個
                    if cur == self.__head: # 匹配上了第一個節點,此時 pre 爲空,沒有next,所以單獨討論
                        end = self.__head
                        while(end.next!=self.__head): # 遍歷定位到尾部指針
                            end = end.next
                        self.__head = self.__head.next
                        end.next = self.__head
                    else: ### 匹配到了 2-n-1,刪除操作同單鏈表
                        pre.next = cur.next # 刪除節點
                    return # 刪完以後就應該退出
                else: # 向後走一步
                    pre = cur
                    cur = cur.next
            if cur.value == item: ### while 循環外表示遍歷到了最後一個節點(只有一個節點/不止一個節點),匹配到了第n個
                if cur.next == self.__head: # 這表示匹配的是鏈表中最後一個節點
                    pre.next = self.__head
                else: #cur == self.__head: # 鏈表只有一個節點,此時 pre 爲 none,不能用上面的一句話
                    self.__head = None



if __name__ == "__main__":
    ll = SingleCycleLinkList()
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(1)
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.insert(-1,9)
    ll.insert(3,100)
    ll.insert(10,200)
    ll.travel()

    result = ll.search(9)
    print(result)
    result = ll.search(300)
    print(result)

    ll.remove(9)
    ll.travel()
    ll.remove(200)
    ll.travel()
    ll.remove(100)
    ll.travel()

output

is_empty: True
length 0
is_empty: False
length 1
9 8 1 100 2 3 4 5 6 200 
True
False
8 1 100 2 3 4 5 6 200 
8 1 100 2 3 4 5 6 
8 1 2 3 4 5 6 

4 小結

Single Link List 是情況最簡單的,在這個的基礎上,我們改進實現了 Double Link List 和 Single Cycle Link List,三種鏈表的測試樣例是一樣的,所以如果 coding 沒有問題的話,結果是一樣的!
主要實現瞭如下功能:

  • is_empty():是否爲空
  • length():鏈表的長度
  • traval():遍歷鏈表,打印出來
  • search(item):查找元素是否在鏈表中,返回 boolean 值
  • add(item):在鏈表的第一個位置添加元素
  • append(item):在鏈表的最後一個位置添加元素
  • insert(pos,item):在鏈表的指定位置插入元素
  • remove(item):刪除掉匹配到的第一個元素

在 coding 的時候一定要注意以下的邊界情況是否要單獨討論:

  • 鏈表爲空
  • 鏈表只有一個元素
  • 要對鏈表的第一個元素進行操作
  • 要對鏈表的最後一個元素進行操作

然後插入的時候,最好不要先動原來的鏈表,先讓新節點搭上去,然後再改原來的鏈表。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章