python實現各種常用算法之數據結構(2)

python實現鏈表的操作


鏈表的介紹

鏈表(linked_list)是物理存儲單元上非連續的、非順序的存儲結構,數據元素的邏輯順序是通過鏈表的指針地址實現,每個元素包含兩個結點,一個是存儲元素的數據域 (內存空間),另一個是指向下一個結點地址的指針域。根據指針的指向,鏈表能形成不同的結構,例如單鏈表,雙向鏈表,循環鏈表等。
在這裏插入圖片描述
鏈表通過將鏈點 i 與其鄰居鏈點 i+1 通過指針相關聯,從索引 0 到索引 N-1 對鏈點進行排序。
複雜度分析
鏈表屬於常見的一種線性結構,對於插入和移除而言,時間複雜度都爲 O(1)
但是對於搜索操作而言,不管從鏈表的頭部還是尾部,都需要遍歷 O(n),所以最好複雜度爲 O(1),最壞的情況就是從頭部遍歷到尾部才搜索出對應的元素,所以最壞複雜度爲 O(n),平均複雜度爲 O(n)。
歸納如下:
最好複雜度爲 O(1)
最壞複雜度爲 O(n)
平均複雜度爲 O(n)

單鏈表的功能是實現
  • 創建 Node 類

創建一個 Node 的類,作爲基礎數據結構:鏈點,並初始化對應的內參。

class Node:  
    def __init__(self, data):
        self.data = data  # 表示對應的元素值
        self.next = None  # 表示下一個鏈接的鏈點
  • 創建 Linked_List 類

創建一個 Linked_List 的類,並初始化對應的內參。

class Linked_List:
    def __init__(self, head=None):  # 鏈表初始化函數
        self.head = head  # 表示鏈表的頭部元素
  • 添加 append 函數

添加一個 append 的函數,功能是向鏈表添加新的結點

    def append(self, new_element):
        # 將頭部結點指向臨時變量 current
        current = self.head
        # 當頭部結點存在時
        if self.head:
            # 循環遍歷到鏈表的最後一個元素
            while current.next:
                current = current.next
            current.next = new_element
        # 當頭部結點不存在時
        else:
            self.head = new_element
  • 添加 is_empty 函數

添加一個 is_empty 的函數,功能是判斷鏈表是否爲空

def is_empty(self):
    """
    判斷鏈表是否爲空
    """
    # bool() 函數只返回 True 和 False
    return not self.head
  • 添加 insert 函數

insert(new_element) 往鏈表中任意位置添加一個 new_element 元素
流程如下:
1 先判斷要插入的位置是否在鏈表的索引範圍內。
2 當插入的位置是頭結點(即索引爲 0)時,做特殊情況處理。
3 當要插入結點的位置不在 0 時,找到要插入的位置,插入新結點

 def insert(self, position, new_element):
        """
        在鏈表中指定索引處插入元素
        """
        if position < 0 or position > self.get_length():
            raise IndexError('insert 插入時,key 的值超出了範圍')
        temp = self.head
        # 將插入元素的 next 屬性指向老的頭結點,並將新的元素賦值給頭結點
        if position == 0:
            new_element.next = temp
            self.head = new_element
            # new_element.next, self.head = temp, new_element
            return
        i = 0
        # 遍歷找到索引值爲 position 的結點後, 在其後面插入結點
        while i < position:
            pre = temp
            temp = temp.next
            # pre, temp = temp, temp.next
            i += 1
        pre.next = new_element
        new_element.next = temp
        # pre.next, new_element.next = new_element, temp
  • 添加 remove 函數

remove() 從鏈表中任意位置刪除一個元素
流程如下:
1.先判斷要刪除的元素索引是否存在,如果不存在拋出錯誤
2 接着判斷當存在鏈表元素時才能執行刪除操作。
3 當要刪除的是頭結點時(即索引爲 0),做特殊情況處理。
4 其他情況時,通過循環找到要刪除的結點。
5 最後要做的就是把這個結點刪除掉。

 def remove(self, position):
        """
        刪除指定索引的鏈表元素
        """
        if position < 0 or position > self.get_length()-1:
            # print("insert error")
            raise IndexError('刪除元素的索引超出範圍')

        i = 0
        temp = self.head
        # 當存在鏈表元素時才能執行刪除操作
        while temp != None:
            # 將頭結點的後一個結點賦值給新的頭結點,再將之前的頭結點指向 `None`
            if position == 0:
                self.head = temp.next
                temp.next = None
                return

            pre = temp
            # 以此來遍歷鏈表
            temp = temp.next
            # pre, temp = temp, temp.next
            i += 1
            if i == position:
                # 將 pre 的 next 屬性指向 temp 的下一個結點
                pre.next = temp.next
                temp.next = None
                # pre.next, temp.next = temp.next, None
                return
  • 添加其他函數

get_length:獲取鏈表的長度
print_list:遍歷鏈表,並將元素依次打印出來
reverse:將鏈表反轉,可以參考鏈接https://blog.csdn.net/blioo/article/details/62050967
initlist: 將列表轉換爲鏈表

 def get_length(self):
        """
        返回鏈表的長度
        """
        頭部結點賦值給頭部結點
        temp = self.head
        # 計算鏈表的長度變量
        length = 0
        while temp != None:
            length = length+1
            temp = temp.next
        # 返回鏈表的長度
        return length

    def print_list(self):
        """
        遍歷鏈表,並將元素依次打印出來
        """
        print("linked_list:")
        # 頭部結點賦值給臨時變量 temp
        temp = self.head
        while temp is not None:
            print(temp.data)
            temp = temp.next

    def reverse(self):
        """
        將鏈表反轉
        """
        prev = None
        current = self.head
        while current:
            next_node = current.next
            current.next = prev
            prev = current
            current = next_node
        self.head = prev

    def initlist(self,data_list):
        """
        將列表轉換爲鏈表
        """
        # 創建頭結點
        self.head = Node(data_list[0])
        temp = self.head
        # 逐個爲 data 內的數據創建結點, 建立鏈表
        for i in data_list[1:]:
            node = Node(i)
            temp.next = node
            temp = temp.next

完整代碼如下

class Node:
    def __init__(self,data):
        #表示對應的元素值
        self.data=data
        #表示下一個鏈接的鏈點
        self.next=None

class Linked_List:
    #鏈表初始化函數
    def __init__(self,head=None):
        #表示鏈表的頭部元素
        self.head=head

    def append(self,new_element):
        '''
        在鏈表後面增加一個元素
        :param new_element: 增加的新元素
        :return: 返回值
        '''
        #將頭部結點指向臨時變量current
        current=self.head
        #當頭部結點存在時
        if self.head:
            #循環遍歷到鏈表的最後一個元素
            while current.next:
                #指針指向下一個元素
                current=current.next
            #在最後一個結點添加新元素
            current.next=new_element
        #當頭部結點不存在時
        else:
            #新元素賦值頭結點
            self.head=new_element

    def is_empty(self):
        #判斷鏈表是否爲空
        return not self.head
    def get_length(self):
        '''獲取鏈表的長度'''
        #臨時變量指向隊列頭部
        temp=self.head
        #計算鏈表的長度變量
        length=0
        #循環,直至爲空終止
        while temp!=None:
            #累加計數
            length=length+1
            #臨時變量指向下一元素
            temp=temp.next
        #返回鏈表的長度
        return length


    def insert(self,position,new_element):
        '''
        在鏈表中的指定位置插入新元素
        :param position: 指定位置
        :param new_element: 新元素
        :return: 返回
        '''

        #判斷要插入的位置是否在鏈表的索引範圍
        if position<0 or position > self.get_length():
            raise IndexError("insert 插入時,key的值超出了範圍")
        #將頭部結點指向臨時變量temp
        temp=self.head
        #當插入的位置是頭結點是,做特殊情況處理
        if position==0:
            #新元素的next指向頭結點
            new_element.next=temp
            #新元素即新的頭結點
            self.head=new_element
            return
        #當插入的結點位置不在0時,找到要插入的位置,插入新節點
        i=0
        #遍歷找到索引值爲position的節點後,在其後插入結點
        while i<position:
            #臨時變量pre,暫存上一個結點,用於保存要插入的前一個結點
            pre=temp
            #指向下一結點
            temp=temp.next
            #pre,temp=temp,temp.next 上面兩式的簡寫,效果一樣
            #計數,找位置
            i+=1
        #前一結點的後面插入新元素
        pre.next=new_element
        #新元素後面鏈接temp即原來pre的下一個結點
        new_element.next=temp
        #pre.next,new_element=new_element,temp  上面兩式的簡寫,效果一樣
    def print_list(self):
        '''遍歷鏈表,並將元素打印出來'''
        print("linked_list:")
        # 將頭部結點指向臨時變量temp
        temp=self.head
        # 建立新列表
        new_list=[]
        #循環添加鏈表的元素到列表中,直到鏈表爲空終止
        while temp is not None:
            new_list.append(temp,data)
            temp=temp.next
        print(new_list)

    def remove(self,position):
        '''刪除指定索引的鏈表元素'''
        #判斷要刪除的元素索引是否存在,如果不存在拋出錯誤
        if position<0 or position>self.get_length()-1:
            raise IndexError("刪除的元素索引超出範圍")
        i=0
        # 將頭部結點指向臨時變量temp
        temp=self.head
        #遍歷找到索引值爲position的結點
        while temp!=None:
            #要刪除的是頭結點是,做特殊情況處理
            if position==0:
                #將頭結點的後一個結點賦值給新的頭結點
                self.head=temp.next
                #再將之前的頭結點指向None
                temp.next=None
                return True
            #pre暫存目標結點的前一個結點
            pre=temp
            temp=temp.next
            #位置計數
            i+=1
            #找到目標元素,處理
            if i==position:
                #前一結點指向當前結點的下一個結點
                pre.next=temp.next
                #當前結點下一鏈接爲空
                temp.next=None
                return
    def reverse(self):
        '''將鏈表反轉'''
        prev=None
        current=self.head
        while current:
            next_node=current.next
            current.next=prev
            prev=current
            current=next_node
        self.head=prev

    def initllist(self,data_list):
        '''將列表轉化爲鏈表'''
        #創建頭結點
        self.head=Node(data_list[0])
        #臨時變量指向頭結點
        temp=self.head
        #爲data內的數據創建結點,建立鏈表
        for i in data_list[1:]:
            #將列表的值賦值結點node
            node=Node(i)
            #當前結點鏈接到鏈表
            temp.next=node
            #指針指向下一個
            temp=temp.next


雙鏈表

雙向鏈表(Double_linked_list)也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。

完整代碼如下
class Node(object):
    # 雙向鏈表節點
    def __init__(self, item):
        # 表示對應的元素值
        self.item = item
        # 表示後驅結點
        self.next = None
        # 表示前驅結點
        self.prev = None


class DLinkList(object):
    # 雙向鏈表
    #鏈表初始化函數
    def __init__(self):
        #初始頭結點爲空
        self._head = None

    def is_empty(self):
        # 判斷鏈表是否爲空
        return self._head == None

    def get_length(self):
        # 返回鏈表的長度
        #臨時變量cur指向頭結點
        cur = self._head
        #計數鏈表長度
        count = 0
        #循環到鏈表爲空
        while cur != None:
            #計數器加1
            count = count+1
            #指針指向後驅結點
            cur = cur.next
        #返回鏈表長度
        return count

    def travel(self):
        # 遍歷鏈表
        #臨時變量指向頭結點
        cur = self._head
        #循環到鏈表爲空終止
        while cur != None:
            #輸出當前元素
            print(cur.item)
            #指針指向後驅結點
            cur = cur.next
        print("")

    def add(self, item):
        # 頭部插入元素即頭插法
        node = Node(item)
        if self.is_empty():
            # 如果是空鏈表,將 node 賦值給 _head
            self._head = node
        else:
            # 將 node 的 next 屬性指向頭節點 _head
            node.next = self._head
            # 將頭節點 _head 的 prev 屬性指向 node
            self._head.prev = node
            # 將 node 賦值給 _head
            self._head = node

    def append(self, item):
        # 尾部插入元素即尾插法
        node = Node(item)
        if self.is_empty():
            # 如果是空鏈表,將 node 賦值給 _head
            self._head = node
        else:
            # 循環移動到鏈表尾部結點的位置
            cur = self._head
            while cur.next != None:
                cur = cur.next
            # 將尾結點 cur 的 next 屬性指向 node
            cur.next = node
            # 將 node 的 prev 屬性指向 cur
            node.prev = cur

    def search(self, item):
        # 查找元素是否存在
        #臨時變量指向鏈表頭結點
        cur = self._head
        #遍歷查找元素,視情況返回true或false
        while cur != None:
            if cur.item == item:
                return True
            cur = cur.next
        return False

    def insert(self, pos, item):
        # 在指定位置添加節點
        #在頭結點前面插入,頭插法
        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
            # 將 node 的 prev 屬性指向 cur
            node.prev = cur
            # 將 node 的 next 屬性指向 cur 的下一個節點
            node.next = cur.next
            # 將 cur 的下一個節點的 prev 屬性指向 node
            cur.next.prev = node
            # 將 cur 的 next 指向 node
            cur.next = node

    def remove(self, item):
        # 刪除元素
        if self.is_empty():
            return
        else:
            cur = self._head
            if cur.item == item:
                # 如果首節點的元素即是要刪除的元素
                if cur.next == None:
                    # 如果鏈表只有這一個節點
                    self._head = None
                else:
                    # 將第二個節點的 prev 屬性設置爲 None
                    cur.next.prev = None
                    # 將 _head 指向第二個節點
                    self._head = cur.next
                return
            while cur != None:
                if cur.item == item:
                    # 將 cur 的前一個節點的 next 指向 cur 的後一個節點
                    cur.prev.next = cur.next
                    # 將 cur 的後一個節點的 prev 指向 cur 的前一個節點
                    cur.next.prev = cur.prev
                    break
                cur = cur.next

實例應用

目標:
交換兩個鏈點在鏈表中的位置
不改變其他鏈點在鏈表中的位置
思路:
採用 insert 的思想,對於要交換的兩個鏈點 d1 d2,各自聲明新的 D1 D2 ,使 D1=d1, D2=d2
然後把 然後再根據 d1 d2 的位置,改變索引的位置,即完成交換的全部操作

交換單鏈表裏兩個鏈點
在這個要求中,我們要給定兩個值,如果這兩個值都在單鏈表的鏈點中,即交換這兩個鏈點在單鏈表的位置。

舉例:

1->2->3->4->5

input:1 4 output:4->2->3->1->5

完整代碼如下

class Node:
    #初始化結點
    def __init__(self, data):
        #初始化結點的數據位傳入的值
        self.data = data
        #初始結點的下一結點鏈接爲空
        self.next = None


class Linkedlist:
    #初始化鏈表
    def __init__(self):
        #初始頭結點爲空
        self.head = None

    def print_list(self):  # 遍歷鏈表,並將元素依次打印出來
        print("linked_list:")
        #指針指向單鏈表頭結點
        temp = self.head
        #初始化列表
        new_list = []
        #遍歷添加鏈表元素到列表
        while temp is not None:
            new_list.append(temp.data)
            temp = temp.next
        #打印列表
        print(new_list)
    #添加新元素到鏈表
    def insert(self, new_data):
        #初始化新節點
        new_node = Node(new_data)
        #頭插法插入新節點
        new_node.next = self.head
        self.head = new_node
    #交換指定的元素的位置
    def swapNodes(self, d1, d2):
        #暫存目標元素的前一個結點,初始爲空
        prevD1 = None
        prevD2 = None
        #如果交換元素相等,返回不執行操作
        if d1 == d2:
            return
        else:
            #D1指向單鏈表頭結點
            D1 = self.head
            #遍歷鏈表查找目標元素
            while D1 is not None and D1.data != d1:
                #暫存前一個結點
                prevD1 = D1
                #指向下一結點
                D1 = D1.next

            D2 = self.head
            while D2 is not None and D2.data != d2:
                prevD2 = D2
                D2 = D2.next
            #如何兩個目標元素均不存在,返回
            if D1 is None or D2 is None:
                return
            # 如果D1存在前一結點,則D1存在已找到,並把前一結點的後驅指向D2
            if prevD1 is not None:
                prevD1.next = D2
            #如果D1沒有前一結點,則D1爲頭結點,賦值D2新的頭結點
            else:
                self.head = D2

            if prevD2 is not None:
                prevD2.next = D1
            else:
                self.head = D1
            #完成鏈表的鏈接處理
            temp = D1.next
            D1.next = D2.next
            D2.next = temp


if __name__ == '__main__':
    #初始化鏈表
    list = Linkedlist()
    #鏈表添加結點
    list.insert(5)
    list.insert(4)
    list.insert(3)
    list.insert(2)
    list.insert(1)
    #打印初始鏈表
    list.print_list()
    #交換指定鏈表值的位置
    list.swapNodes(1, 5)
    #打印交換後的鏈表
    print("After swapping")
    list.print_list()

運行結果如下
交換1,5的位置

思路分析
1 初始化鏈表,並添加數據
2 數據交換
第一步:判斷要交換的兩個元素,是否相等,相等返回,不交換
第二步:遍歷目標元素,並保存其前一結點
考慮幾種情況
@1:兩個均未找到或者只找到一個,不操作,原鏈表輸出
@2:兩者均找到,執行,交換後輸出
第三步:打印前後鏈表進行比對’

發佈了129 篇原創文章 · 獲贊 347 · 訪問量 60萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章