一個連結串列的構造過程-單鏈表
對於有其它程式設計基礎的人來說,在初學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 = 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()