1. 程式人生 > >一個連結串列的構造過程-單鏈表

一個連結串列的構造過程-單鏈表

對於有其它程式設計基礎的人來說,在初學python時會發現有兩個很明顯的不同的地方,一個是縮排,另外一個就是在定義一個變數時並不會提前宣告這個變數的型別。出現第二個不同的主要原因是在於python在儲存資料的時候與其他語言(如C語言)的不同所導致。那麼python是如何做到一個變數可以隨時更改其型別的呢?
python中當我們編寫下面這句程式碼時:

a = 268
b = 300
a, b = b, a
print("a=%d, b=%d"%(a, b))

執行後我們會發現兩個變數的值交換了,python語言是如何做到這一點的呢?我們用一張圖來看一下這個過程:
變數定義的過程
當我們執行 a = 268時,先會在記憶體中獲取一片空間來存放 268 (整型)這個資料,它有一個地址,然後等號左邊,a 也會獲取一片空間(固定4byte),此時這片空間的名字就是a,而這個空間記憶體儲的是 268 (整型)的地址。這樣我們就可以做到當執行:
a = “268”
此時,變化的只有a獲取的空間所儲存的地址發生變化,這時候就指向了”268”所在的地址,完成了變數型別的改變。
變數型別的改變


這裡我們回到a,b值交換的問題上,
定義兩個變數a,b
首先,a,b 的地址空間記憶體儲了對應的資料的地址。執行”a, b = b, a”,等號右邊”0x628, 0x516”,這裡是生成了一個元祖來儲存這兩個地址,然後變為 a = 0x628 , b = 0x516。最後,看似是兩個變數的資料的交換,本質上確是兩個變數地址的交換。(左邊完成後元祖的引用計數變為0,被回收)
這裡寫圖片描述
理解了這個Python的這個不同之處後,我們就可以開始一個單鏈表的構造了。在單鏈表中每個元素的儲存是在結點中來儲存的。如下圖:
這裡寫圖片描述
我們把一個結點看做一個整體,這個結點有一個地址。結點內分為兩個區域,一個是元素域(資訊域),用來儲存我們真實資料的地址。而另外一個是連結域,用來儲存位於這個結點後面的結點地址。
我們把結點例項化為一個類:

class Node(object):
    def __init__(self, value, n_node = None):
        self.elem = value
        # 預設下一個結點為None,當然我們也支援在建立一個結點的時候指明下一個結點是什麼
        self.next = n_node

而單鏈表的作用就是將這些結點串起來,同時單鏈表要支援:

  • is_empty() 連結串列是否為空
  • length() 連結串列長度
  • travel() 遍歷整個連結串列
  • add(item) 連結串列頭部新增元素
  • append(item) 連結串列尾部新增元素
  • insert(pos, item) 指定位置新增元素
  • remove(item) 刪除結點
  • search(item) 查詢結點是否存在

既然單鏈表有這麼多方法,那麼就將他封裝到一個類中:

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

這時候我們就可以通過這個類來建立一個連結串列,但是他並沒有任何功能,我們現在依次進行新增:
1. is_empty() 連結串列是否為空

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def is_empty(self):
        # 只需要判斷是否有結點即可
        return self.__head is None

驗證:

sll =  SinglyLinkedList()
print(sll.is_empty())
sll_2 =  SinglyLinkedList(Node(225))
print(sll2.is_empty())
# 執行結果
True
False

2.append(value) 連結串列尾部新增元素

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def append(self, value):
        node = Node(value)
        if self.is_empty():
            self.__head = node
        else:
            p = self.__head
            while p.next is not None:
                 = p.next               
            p.next = node

對於向連結串列尾部新增結點,相當於要找到最後一個結點,然後將我們要新增的結點的地址新增到最後一個結點的連結域內。
當在尋找最後一個結點的時候,需要從第一個結點開始找起,然後依次來判斷接下來的結點的連結域是否為None,如果為None的話,那麼說明就是最後一個結點。這裡給append方法新增一個區域性變數p(遊標),p總是指向一個結點。當p.next為None時(迴圈結束),此時的p即為最後一個結點。然後將我們要新增的結點新增到最後一個結點的連結域內即可。
這裡寫圖片描述
2.length() 連結串列長度

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def length(self):
        if self.is_empty():
            return 0
        else:
            p = self.__head
            count = 0
            while p.next is not None:
                count += 1
                p = p.next
            count += 1
            return count

這裡針對為空連結串列時只需檢查是否為空返回0即可,對於有一個以上結點的連結串列,需要介入遊標判斷連結域是否為空來計數,因為最後一個結點的連結域定None,而最後一個結點並未進入迴圈計數,所以當迴圈結束時進行一次+1操作。
3.travel() 遍歷整個連結串列

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def travel(self):
        if self.is_empty():
            return
        else:
            p = self.__head
            while p.next is not None:
                print(p.elem, end=" ")
                p = p.next
            print(p.elem)

遍歷類似於判斷長度,對空進行判斷後,正常情況下依次列印直到最後一個結點,而最後一個結點的連結域為None,未進入迴圈,所以在迴圈結束後列印最後一個結點的elem。

4.add(item) 連結串列頭部新增元素

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def add(self, item):
        node = Node(item)
        if self.is_empty():
            self.__head = node
        else:
            node.next = self.__head
            self.__head = node

為空時直接指定連結串列頭結點為新結點,否則只需讓新節點的連結域指向舊連結串列的頭結點,然後使新結點成為頭結點。

5.insert(pos, item) 指定位置新增元素

class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def insert(self, pos, item):
        node = Node(item)
        if not isinstance(pos, int):
            print("TypeError:Must be int ")
            return
        elif 0 == pos and self.is_empty():
            self.__head = node
            return
        elif 0 == pos and (not self.is_empty()):
            self.add(item)
        elif ((pos < 0)
              or (pos > (self.length()-1))):
            print("position Erro")
            return
        else:
            p = self.__head
            count = 0
            while p.next is not None:
                count += 1
                if pos == count:
                    node.next = p.next
                    p.next = node
                    break
                p = p.next

剩餘的remove(item) 刪除節點和search(item) 查詢節點是否存在,前者與指定位置插入類似,後者與遍歷類似,只需進行相應修改即可。
附程式碼:

class Node(object):
    def __init__(self, value, n_node=None):
        self.elem = value
        # 預設下一個結點為None,當然我們也支援在建立一個結點的時候指明下一個結點是什麼
        self.next = n_node


class SinglyLinkedList(object):
    def __init__(self, node=None):
        self.__head = node

    def length(self):
        if self.is_empty():
            return 0
        else:
            p = self.__head
            count = 0
            while p.next is not None:
                count += 1
                p = p.next
            count += 1
            return count

    def is_empty(self):
        # 只需要判斷是否有結點即可
        return self.__head is None

    def add(self, item):
        node = Node(item)
        if self.is_empty():
            self.__head = node
        else:
            node.next = self.__head
            self.__head = node

    def insert(self, pos, item):
        node = Node(item)
        if not isinstance(pos, int):
            print("TypeError:Must be int ")
            return
        elif 0 == pos and self.is_empty():
            self.__head = node
            return
        elif 0 == pos and (not self.is_empty()):
            self.add(item)
        elif ((pos < 0)
              or (pos > (self.length()-1))):
            print("position Erro")
            return
        else:
            p = self.__head
            count = 0
            while p.next is not None:
                count += 1
                if pos == count:
                    node.next = p.next
                    p.next = node
                    break
                p = p.next

    def travel(self):
        if self.is_empty():
            return
        else:
            p = self.__head
            while p.next is not None:
                print(p.elem, end=" ")
                p = p.next
            print(p.elem)

if __name__ == '__main__':
    new_node = Node(12)
    sll = SinglyLinkedList(new_node)
    sll.travel()
    sll.add(13)
    sll.add(14)
    sll.add(15)
    sll.add(16)
    sll.travel()
    sll.insert(8, 11)
    sll.travel()