1. 程式人生 > 實用技巧 >線性表(六):雙向迴圈連結串列的基本操作

線性表(六):雙向迴圈連結串列的基本操作

引言

本節將介紹最後一個連結串列的變形——雙向迴圈連結串列,這一結構在雙向連結串列的基礎上優化了對尾部節點的插入/刪除效率。

雙向迴圈連結串列

基本操作基本與雙向連結串列類似,但是要注意每個操作都要考慮到首尾節點的指標。

Python實現:

class DoublyListNode():
    def __init__(self, val, prev_=None, next_=None):
        self.val = val
        self.next_ = next_
        self.prev_ = prev_
        
class DoubleCircularLinkedList():
    
    def __init__(self):
        self.rear = None
        self.head = None
        
    # 判斷連結串列是否為空
    def is_empty(self):
        return self.rear is None
    
    # 向頭部插入元素
    def prepend(self, element):
        p = DoublyListNode(element, None, self.head)
        if self.is_empty():
            self.rear = p
        else:
            p.next_.prev_ = p
        self.head = p
        self.rear.next_ = self.head
        self.head.prev_ = self.rear
    
    # 向尾部插入元素
    def append(self, element):
        p = DoublyListNode(element, self.rear, None)
        if self.is_empty():
            self.head = p
        else:
            p.prev_.next_ = p
        self.rear = p
        self.rear.next_ = self.head
        self.head.prev_ = self.rear
        
    # 彈出頭部元素
    def pop_first(self):
        if self.is_empty():
            raise ValueError('連結串列為空,該操作無效!')
        output = self.head.val
        self.head = self.head.next_
        if self.head:
            self.head.prev_ = self.rear
            self.rear.next_ = self.head
        return output
    
    # 彈出尾部元素
    def pop_last(self):
        if self.is_empty():
            raise ValueError('連結串列為空,該操作無效!')
        output = self.rear.val
        self.rear = self.rear.prev_
        if self.rear:
            self.rear.next_ = self.head
            self.head.prev_ = self.rear
        return output
    
    # 獲取連結串列長度
    def get_length(self):
        if self.is_empty():
            return 0
        cur = self.head
        length = 1
        while cur.next_ != self.head:
            cur = cur.next_
            length += 1
        return length
    
    # 向指定位置插入任意元素
    def insert(self, position, element):
        l = self.get_length()
        if position >= l or position < 0:
            raise IndexError('下標越界!')
        elif position == 0:
            self.prepend(element)
        elif position == l-1:
            self.append(element)
        else:
            if self.is_empty():
                raise IndexError('下標越界!')
            if position <= l / 2:
                cur = self.head
                while position > 0:
                    cur = cur.next_
                    position -= 1
                p = DoublyListNode(element, None, cur)
                cur.prev_.next_ = p
            else:
                cur = self.rear
                while position < l-1:
                    cur = cur.prev_
                    position += 1
                p = DoublyListNode(element, None, cur)
                cur.prev_.next_ = p
    
    # 刪除指定元素
    def remove(self, element):
        if self.is_empty():
            raise IndexError('下標越界!')
        if self.head.val == element:
            self.head = self.head.next_
            if self.head:
                self.head.prev_ = self.rear
        elif self.rear.val == element:
            self.rear = self.rear.prev_
            if self.rear:
                self.rear.next_ = self.head
        else:
            cur = self.head
            flag = False
            while cur.val != element and cur.next_ != self.head:
                cur = cur.next_
                if cur and cur.val == element:
                    flag = True
            if flag:
                cur.prev_.next_ = cur.next_
            else:
                raise ValueError('連結串列中不存在該元素!')
    

if __name__ == '__main__':
    def testfunction(node):
        nums = []
        cur = node
        while cur.next_ != node:
            nums.append(cur.val)
            cur = cur.next_
        nums.append(cur.val)
        return nums
    
    sample = DoubleCircularLinkedList()
    for i in range(8):
        sample.prepend(i)
    print(testfunction(sample.head))
    
    sample.append(2)
    print(testfunction(sample.head))
    
    print(sample.get_length())
    
    print(sample.pop_last())
    print(testfunction(sample.head))
    
    sample.insert(1, 10)
    print(testfunction(sample.head))
    
    sample.remove(1)
    print(testfunction(sample.head)) 

總結

從但單鏈表到單向迴圈連結串列,通過新增尾部節點引用,提高了尾插法的效率,再到雙向連結串列實現了兩端高效插入,最後到雙向迴圈連結串列,彌補了雙向連結串列尾部元素插入低效的問題,這一系列改進操作似乎是強化了連結串列結構,但事實上如果非必要,我們還是優先考慮單鏈表,因為其他結構雖然操作上優化了不少,但同時也需要更多的空間去存放指標。此外,相比於順序表,雖然連結串列的儲存方式更加靈活,但連結串列定位查詢操作的複雜度較高,這也是這一結構最大的劣勢。