數據結構學習——05—雙向鏈表

在這裏插入圖片描述
雙向鏈表的知識點很多都和單向鏈表相同,函數實現方法也差不多,只是需要注意每個節點的prev需要指向上一個節點。

雙向鏈表的節點實現:

'''雙向鏈表'''
# 節點實現
class Node(object):
    def __init__(self, item):
        self.item = item
        
        # 前後指向地址暫定爲None
        self.prev = None
        self.next = None

需要注意的就是地址指向有兩個prev和next,初始值都設爲None

雙向鏈表實現:

# 雙向鏈表實現
class DoubleLinkList(object):
    def __init__(self, node=None):
        self.__head = node          

因爲雙向鏈表中的首地址指向None,所以__init__函數中參量默認爲node=None,假如傳入參數node,不是空鏈表了,此時self.__head指向node。

雙向鏈表的增刪改查等函數實現:

1、判空函數:

 def is_empty(self):
        if self.__head == None:
            return True
        else:
            return False

2、鏈表長度函數:

# 鏈表長度
    def length(self):
        # 考慮空鏈表情況
        if self.is_empty():
            return 0
        
        # cur遊標  指向首節點,用來遍歷  相當於指針
        cur = self.__head  # 遊標指向首地址
        count = 0          # 注意此時的初始值和非空情況的判斷條件的關係

        # 非空情況下
        while cur != None:     # 初始值爲0 的判斷條件
            count += 1
            cur = cur.next
        return count
        
        # 另一種判空情況
        # while cur.next != None:         # 找到尾節點
        #     count += 1
        #     cur = cur.next              # 繼續循環
        # return count

這裏注意的是while虛表還條件的選擇,這個與count的初始值設定有關,還關乎尾節點是否在循環內的情況。
代碼中註釋掉的一部分代碼,就是另外一種的while循環條件,相對應的count=1,此時尾節點的數據沒有進入到循環內,需要單獨考慮。

3、遍歷鏈表

# 遍歷鏈表
    def travel(self):
        cur = self.__head
    
        # 考慮空鏈表情況
        if self.is_empty():
            return None
        
        # 非空鏈表的情況下:
        while cur != None:
            print(cur.item, end=' ')
            cur = cur.next           # 因爲判空條件的不同,尾節點已經在循環中
        # while cur.next != None:
        #     print(cur.item)
        #     cur = cur.next
        #
        # print(cur.next)  # 輸出結尾結點

和上面的函數實現相同,while循環條件的選擇,和輸出語句相關,註釋掉的代碼是另一種實現函數的方法。

4、鏈表頭部插入元素

    def add(self, item):
        node = Node(item)
        
        # 空鏈表
        if self.is_empty():
            self.__head = node
        else:
            # 非空鏈表
            # 第一步:將新的節點的地址指向初始鏈表的第一個元素
            node.next = self.__head
            
            # 第二步:將__head的頭節點的prev指向node
            # cur.prev = node
            self.__head.prev = node
            
            # 第三步:將__head指向node
            # node.prev = None
            self.__head = node

這裏需要注意的是:註釋掉的代碼是一種錯誤的思路,需要區分開。註釋掉的代碼是沒有明確cur,prev和next代表的含義是什麼,這些都是指向節點的數據地址的“指針”。如下圖所示:
圖中箭頭代表的是指向的地址,代碼編寫的順序應該是從右往左編寫代碼,否則會出現地址指向錯誤,順序很重要!!!
在這裏插入圖片描述

5、鏈表尾部插入數據

    # 尾部插入新節點
    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指向尾部結點
            cur.next = node
            node.prev = cur

這裏的while循環條件特殊一點:因爲是在尾部插入,找到的節點需要是最後一個節點,節點的next爲None。
鏈表尾部插入數據
圖中箭頭代表的是指向的地址,代碼編寫的順序應該是從右往左編寫代碼,否則會出現地址指向錯誤,順序很重要!!!

6、鏈表任意位置插值

# 任意位置插值
    def insert(self, pos, item):
        
        # pos負數,頭部插入
        if pos <= 0:
            self.add(item)
        # 尾部節點插入
        elif pos > self.length() - 1:
            self.append(item)
        
        # 中間位置插入
        else:
            node = Node(item)
            cur = self.__head
            count = 0
            
            # 在鏈表長度內循環
            while count < (pos - 1):
                count += 1
                cur = cur.next
            # 循環結束
            # 插入的節點next指向遊標所指的下一個節點
            node.next = cur.next
            # 新節點的prev指向遊標所指的節點
            node.prev = cur
            # 遊標所指下一個節點的prev指向新結點
            cur.next.prev = node
            # 遊標所在的節點的next指向新節點
            cur.next = node

示意圖如下:
圖中箭頭代表的是指向的地址,代碼編寫的順序應該是從右往左編寫代碼,這裏最主要的是兩個指向需要先寫:1、插入的節點next指向遊標所指的下一個節點;2、新節點的prev指向遊標所指的節點;這兩個順序需要在(遊標所指下一個節點的prev指向新結點)(遊標所在的節點的next指向新節點)之前
在這裏插入圖片描述

7、查找節點是否存在

# 查找結點是否存在
    def search(self, item):
        cur = self.__head
        # 循環查找
        while cur != None:
            if cur.item == item:
                return True
            else:
                # 遊標繼續執行
                cur = cur.next
        return False

8、刪除鏈表節點

# 刪除鏈表節點
    def remove(self, item):
        if self.is_empty():
            return None
        else:
            cur = self.__head
            # 首結點的元素就是要刪除的元素
            if cur.item == item:
                # 如果鏈表只有一個結點
                if cur.next == None:
                    self.__head = None          # 首地址指向None
                else:                           # 鏈表中不止一個節點數據
                    cur.next.prev = None        # 下一個節點的prev指向None
                    self.__head = cur.next      # 首地址只想下個節點
                return None

            while cur != None:    # 循環到尾部節點數據之前
                if cur.item == item:     # 循環過程中找到需要的數據
                    cur.prev.next = cur.next         # 當前要刪除節點的prev,即上一個節點,上一個節點的next指向刪除後的節點
                    if cur.next:                     # 如果刪除節點的下一個節點不是尾部節點
                        cur.next.prev = cur.prev     # 下一個節點的prev指向刪除節點的前一個節點
                    break                            # 跳出循環
                cur = cur.next                       # 繼續循環

示意圖如下:
在這裏插入圖片描述

代碼整體的實現:

# -*- encoding: utf-8 -*-
"""
@File    : double_link_list.py
@Time    : 2019/11/13 15:03
@Author  : chen

"""

'''雙向鏈表'''

# 節點實現
class Node(object):
    def __init__(self, item):
        self.item = item
        
        # 前後指向地址暫定爲None
        self.prev = None
        self.next = None
        

# 雙向鏈表實現
class DoubleLinkList(object):
    def __init__(self, node=None):
        self.__head = node              #
        
            
    def is_empty(self):
        if self.__head == None:
            return True
        else:
            return False
        
    # 鏈表長度
    def length(self):
        # 考慮空鏈表情況
        if self.is_empty():
            return 0
        
        # cur遊標  指向首節點,用來遍歷  相當於指針
        cur = self.__head  # 遊標指向首地址
        count = 0          # 注意此時的初始值和非空情況的判斷條件的關係

        # 非空情況下
        while cur != None:     # 初始值爲0 的判斷條件
            count += 1
            cur = cur.next
        return count
        
        # 另一種判空情況
        # while cur.next != None:         # 找到尾節點
        #     count += 1
        #     cur = cur.next              # 繼續循環
        # return count
    
    # 遍歷鏈表
    def travel(self):
        cur = self.__head
    
        # 考慮空鏈表情況
        if self.is_empty():
            return None
        
        # 非空鏈表的情況下:
        while cur != None:
            print(cur.item, end=' ')
            cur = cur.next           # 因爲判空條件的不同,尾節點已經在循環中
        # while cur.next != None:
        #     print(cur.item)
        #     cur = cur.next
        #
        # print(cur.next)  # 輸出結尾結點
    
    # 頭部插入元素
    def add(self, item):
        node = Node(item)
        
        # 空鏈表
        if self.is_empty():
            self.__head = node
        else:
            # 非空鏈表
            # 第一步:將新的節點的地址指向初始鏈表的第一個元素
            node.next = self.__head
            
            # 第二步:將__head的頭節點的prev指向node
            # cur.prev = node
            self.__head.prev = node
            
            # 第三步:將__head指向node
            # node.prev = None
            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指向尾部結點
            cur.next = node
            node.prev = cur
            
    # 任意位置插值
    def insert(self, pos, item):
        
        # pos負數,頭部插入
        if pos <= 0:
            self.add(item)
        # 尾部節點插入
        elif pos > self.length() - 1:
            self.append(item)
        
        # 中間位置插入
        else:
            node = Node(item)
            cur = self.__head
            count = 0
            
            # 在鏈表長度內循環
            while count < (pos - 1):
                count += 1
                cur = cur.next
            # 循環結束
            # 插入的節點next指向遊標所指的下一個節點
            node.next = cur.next
            # 新節點的prev指向遊標所指的節點
            node.prev = cur
            # 遊標所指下一個節點的prev指向新結點
            cur.next.prev = node
            # 遊標所在的節點的next指向新節點
            cur.next = node
    
    # 查找結點是否存在
    def search(self, item):
        cur = self.__head
        # 循環查找
        while cur != None:
            if cur.item == item:
                return True
            else:
                # 遊標繼續執行
                cur = cur.next
        return False
    
    # 刪除鏈表節點
    def remove(self, item):
        if self.is_empty():
            return None
        else:
            cur = self.__head
            # 首結點的元素就是要刪除的元素
            if cur.item == item:
                # 如果鏈表只有一個結點
                if cur.next == None:
                    self.__head = None          # 首地址指向None
                else:                           # 鏈表中不止一個節點數據
                    cur.next.prev = None        # 下一個節點的prev指向None
                    self.__head = cur.next      # 首地址只想下個節點
                return None

            while cur != None:    # 循環到尾部節點數據之前
                if cur.item == item:     # 循環過程中找到需要的數據
                    cur.prev.next = cur.next         # 當前要刪除節點的prev,即上一個節點,上一個節點的next指向刪除後的節點
                    if cur.next:                     # 如果刪除節點的下一個節點不是尾部節點
                        cur.next.prev = cur.prev     # 下一個節點的prev指向刪除節點的前一個節點
                    break                            # 跳出循環
                cur = cur.next                       # 繼續循環
    
  
    
if __name__ == '__main__':
    d = DoubleLinkList()
    print(d.is_empty())
    print(d.length())
    d.add(1)
    d.add(2)
    d.append(0)
    d.insert(2, 5)
    d.insert(-1, 9)
    d.insert(10,100)
    d.travel()
    print(d.is_empty())
    print(d.length())
        
        
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章