首先我們先了解順序表和鏈表的概念,爲什麼要使用鏈表。
順序表和鏈表
順序表
順序表的構建需要預先知道數據大小來申請連續的存儲空間,而在進行擴充時需要進行數據搬遷,所以使用起來不是很靈活。
鏈表
鏈表(Linked List) 是一種常見的基礎數據結構,是一隻種類線性表,但是不像順序表一樣連續存儲數據,而是在每一個節點(數據存儲單元)裏存放下一個節點的位置信息(即地址)。
鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。
順序表和鏈表的內存分佈
單向鏈表
單向鏈表也叫單鏈表,每個節點包含兩個域,一個信息域(元素域)和一個鏈接域。這個鏈接指向鏈表中的下一個節點,而最後一個節點的鏈接域則指向一個空值。
• 表元素域elem 用來存放具體的數據。
• 鏈接域next 用來存放下一個節點的位置(python中的標識)
• 變量p指向鏈表的頭節點(首節點)的位置,從p出發能找到表中的任意節點。
單鏈表的操作
鏈表實現之前,需要先實現節點,代碼如下:
class Node(object):
"""單鏈表節點的封裝"""
def __init__(self, element):
self.element = element
self.next = None
我們先實現判斷鏈表是否爲空,鏈表長度和遍歷鏈表的操作代碼:
class Node(object):
"""單鏈表節點的封裝"""
def __init__(self, element):
self.element = element
self.next = None
class SingleLink(object):
"""單鏈表的封裝"""
def __init__(self):
# 默認爲空
self._head = None
def is_empty(self):
"""是否爲空"""
return self._head == None
def __len__(self):
"""
求鏈表長度
1. 判斷是否爲空,爲空直接返回0
2. 不爲空時依次遍歷,長度加1之後將下一個節點賦值給當前
"""
if self.is_empty():
return 0
else:
cur = self._head
length = 0
while cur != None:
length += 1
cur = cur.next
return length
def travel(self):
"""遍歷鏈表"""
if self.is_empty():
print('空鏈表')
else:
cur = self._head
while cur.next != None:
print(cur.element, end=',')
cur = cur.next
print(cur.element)
單鏈表的頭部添加元素
class SingleLink(object):
"""單鏈表的封裝"""
def __init__(self):
# 默認爲空
self._head = None
def add(self, item):
"""
頭部添加元素:
1. 創建一個保存item值的節點
2. 將新節點的next指向頭結點,即_head指向的位置
3. 將鏈表的頭_head指向新節點
:param item:
:return:
"""
node = Node(item)
node.next = self._head
self._head = node
指定位置添加元素
class SingleLink(object):
"""單鏈表的封裝"""
def __init__(self):
# 默認爲空
self._head = None
def insert(self, index, item):
"""
指定位置添加元素
1. 指定位置爲頭部之前,則頭部添加元素
2. 指定位置爲尾部之後,則尾部添加元素
3. 中間位置:需要找出指定位置的前一個元素,得到其尾部,插入的節點的尾部指向前面得到的尾部,
前一個元素新的尾部指向插入的頭部
:param index:
:param item:
:return:
"""
if index <= 0:
self.add(item)
elif index >= len(self):
self.append(item)
else:
node = Node(item)
count = 0 # 當前節點的位置
cur = self._head
# 尋找插入節點的前一個節點
while count < index - 1:
count += 1
cur = cur.next
# 插入節點的前一個節點的尾部成爲了插入節點的尾部指向
# 然後將插入節點的前一個節點的尾部指向更新爲新插入節點的頭部
node.next = cur.next
cur.next = node
刪除節點
def remove(self, item):
"""
刪除指定元素的節點
1. 刪除頭部節點
2. 刪除其他位置的節點
:param item:
:return:
"""
"""
既然要刪除當前節點,首先想的就是遍歷鏈表找到對應節點並且刪除,
這裏直接省去了遍歷的操作,直接給出了對應的節點。
既然想刪除當前節點那麼將給定的節點的val和next全部轉換成當前節點的下一個節點所對應的值,
那麼當前節點在本鏈表中就相當於替換成了下一個節點。
"""
cur = self._head
pre = None
while cur != None:
if cur.element == item:
if not pre:
self._head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
尾部添加元素
def append(self, item):
"""
尾部添加元素
1. 先判斷鏈表是否爲空,若爲空,將_head指向新節點
2. 若不爲空,找到尾部,將尾節點next指向新節點
:param item:
:return:
"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
if cur.next == None:
cur.next = node
判斷元素在鏈表中是否存在
def search(self, item):
"""
判斷查找的元素在節點中是否存在,返回Bool類型
:param item:
:return:
"""
cur = self._head
while cur.next != None:
if cur.element == item:
return True
cur = cur.next
if cur.element == item:
return True
return False
鏈表與順序表的對比
操作 | 鏈表 | 順序表 |
---|---|---|
訪問元素 | O(n) | O(1) |
在頭部插入/刪除 | O(1) | O(n) |
在尾部插入/刪除 | O(n) | O(1) |
在中間插入/刪除 | O(n) | O(n) |
時間複雜度的不同主要是因爲鏈表和順序表在插入和刪除時進行的是完全不同的操作。鏈表的主要耗時操作是遍歷查找。順序表查找很快,主要耗時的操作是拷貝覆蓋。因爲除了目標元素在尾部的特殊情況,順序表進行插入和刪除時需要對操作點之後的所有元素進行前後移位操作,只能通過拷貝和覆蓋的方法進行。
python實現單向鏈表的整體代碼
class Node(object):
"""單鏈表節點的封裝"""
def __init__(self, element):
self.element = element
self.next = None
class SingleLink(object):
"""單鏈表的封裝"""
def __init__(self):
# 默認爲空
self._head = None
def is_empty(self):
"""是否爲空"""
return self._head == None
def __len__(self):
"""
求鏈表長度
1. 判斷是否爲空,爲空直接返回0
2. 不爲空時依次遍歷,長度加1之後將下一個節點賦值給當前
"""
if self.is_empty():
return 0
else:
cur = self._head
length = 0
while cur != None:
length += 1
cur = cur.next
return length
def travel(self):
"""遍歷鏈表"""
if self.is_empty():
print('空鏈表')
else:
cur = self._head
while cur.next != None:
print(cur.element, end=',')
cur = cur.next
print(cur.element)
def append(self, item):
"""
尾部添加元素
1. 先判斷鏈表是否爲空,若爲空,將_head指向新節點
2. 若不爲空,找到尾部,將尾節點next指向新節點
:param item:
:return:
"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
if cur.next == None:
cur.next = node
def add(self, item):
"""
頭部添加元素:
1. 創建一個保存item值的節點
2. 將新節點的next指向頭結點,即_head指向的位置
3. 將鏈表的頭_head指向新節點
:param item:
:return:
"""
node = Node(item)
node.next = self._head
self._head = node
def insert(self, index, item):
"""
指定位置添加元素
1. 指定位置爲頭部之前,則頭部添加元素
2. 指定位置爲尾部之後,則尾部添加元素
3. 中間位置:需要找出指定位置的前一個元素,得到其尾部,插入的節點的尾部指向前面得到的尾部,
前一個元素新的尾部指向插入的頭部
:param index:
:param item:
:return:
"""
if index <= 0:
self.add(item)
elif index >= len(self):
self.append(item)
else:
node = Node(item)
count = 0 # 當前節點的位置
cur = self._head
# 尋找插入節點的前一個節點
while count < index - 1:
count += 1
cur = cur.next
# 插入節點的前一個節點的尾部成爲了插入節點的尾部指向
# 然後將插入節點的前一個節點的尾部指向更新爲新插入節點的頭部
node.next = cur.next
cur.next = node
def remove(self, item):
"""
刪除指定元素的節點
1. 刪除頭部節點
2. 刪除其他位置的節點
:param item:
:return:
"""
"""
既然要刪除當前節點,首先想的就是遍歷鏈表找到對應節點並且刪除,
這裏直接省去了遍歷的操作,直接給出了對應的節點。
既然想刪除當前節點那麼將給定的節點的val和next全部轉換成當前節點的下一個節點所對應的值,
那麼當前節點在本鏈表中就相當於替換成了下一個節點。
"""
cur = self._head
pre = None
while cur != None:
if cur.element == item:
if not pre:
self._head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, item):
"""
判斷查找的元素在節點中是否存在,返回Bool類型
:param item:
:return:
"""
cur = self._head
while cur.next != None:
if cur.element == item:
return True
cur = cur.next
if cur.element == item:
return True
return False
if __name__ == '__main__':
# 實例化單鏈表對象
link = SingleLink()
# 長度獲取
# print("鏈表長度:", len(link))
# # 遍歷鏈表
# link.travel()
# print("鏈表是否爲空?", link.is_empty())
link.add(3)
print(link.travel())
link.append(1)
link.append(2)
print(link.travel())
print("鏈表是否爲空?", link.is_empty())
link.insert(1, 'hello')
print(link.travel())
print("鏈表長度:", len(link))
link.remove(3)
print(link.travel())
print(link.search(2))