我們學過計算機的童鞋們都知道算法與數據結構一直是大家逃不掉的噩夢,那麼今天小編就帶大家來看看用python來解讀這些數據結構是否會變得簡單一點呢?
數據結構,顧名思義就是存放數據的結構,結構的不同會導致我們增刪改查數據的效率也大不相同,所以為了能夠高效的操作數據,我們需要了解數據結構,並且在適當的情況下使用特定的數據結構。
舉個簡單的例子,我現在期中考試的成績出來了,我需要登記大家期中考試的成績,這個時候我們需要使用什麼數據類型?
大家都知道python裡面有list和tuple這兩種數據類型。現在我們需要一份名單,並且需要在這份名單上做更新和修改處理,那對應的我們需要選擇什麼數據結構呢?因為需要做修改的操作,所以我們選擇list作為我們存儲數據的主要方式。當登記完所有的成績,我們需要把成績發放到各位同學手中,這個時候為了保證每個人的真實成績都是不可被修改的,我們又需要改變數據結構,選擇tuple類型,來確保每個同學的成績都是不可被修改的!
上面這個例子就是告訴我們,在不同的時候,我們往往需要選擇不同的數據結構來存儲對應的數據,以確保我們能夠高效的訪問或者修改數據,這也就是我們需要學習數據結構的初衷了~
首先,關於數據結構,我們主要分為兩類,線性結構和非線性結構,線性結構中的元素都是一一對應的關係,而非線性結構可能存在一對多,多對多的關係。舉個例子,一次函數:y=kx(一條直線),x和y的值都是一一對應的,這種關係就是線性結構。
但是像y=kx^2,這種結構就屬於非線性結構了,一個y對應兩個x的值。
線性結構裡面主要有數組(Array),棧(Stack),隊列(Queue),鍊表(Linked List)
非線性結構主要是:樹(Tree),圖(Graph),堆(Heap),散列表(Hash)
今天我們主要來看看線性結構。
數組(Array)
數組,將具有相同類型的若干變量有序地組織在一起的集合就是數組。在python裡面,list就是數組。
array = [1, 2, 3, 4, 5, 6, 7]
使用中括號包圍,將裡面的多個元素用逗號隔開,這就是數組了。對於數組我們如何進行它的增刪改查操作呢?
數組是一個有序的集合,所以在數組中,每個元素都有一個與之對應的索引,通過這個索引我們就能取到這個值。
print(array[0])
獲取索引為0的元素的值
關於數組的修改操作:
修改元素值
array[0] = 5
中間插入新的值
array.insert(0, 5)
尾部插入新的值
array.append(5)
刪除值
array.remove(5)
鍊表(Linked List)
說了數組就不得不說和數組相似的鍊表,鍊表的定義是不連續(這個不連續是針對於物理存儲而言),沒有順序的數據結構。是由多個節點組成的。
從下圖中我們可以看出,鍊表好像就是一個單向傳遞的過程,總是由上一個傳向下一個(這就是單鍊表),並且每個節點都是由兩部分組成,一部分存放數據,一部分指向下一個節點。這就像接力賽跑一樣,每個隊員都需要將自己的接力棒傳向下一個隊員。
(鍊表和數組的區別)
鍊表分為單鍊表,雙鍊表和循環鍊表,我們這邊以簡單常見的單鍊表為例。談談,簡單鍊表的python實現
鍊表是由多個不同的節點構成的,所以我們需要定義一個節點,一個節點主要包含兩部分,一部分是指針,指向下一組數據,另一部分是存放數據的信息。
class Node(object):
#初始化,需要傳入節點的數據
作為節點類,要具備一些常用的方法,獲取節點數據,獲取下個節點,更新節點的數據,更新下個節點,這些都可以定義在node類裡面。
#返回節點的數據
def get_data(self):
return self.data
#更新節點的數據
def set_data(self, new_data):
self.data = new_data
#返回後繼節點
def get_next(self):
return self.next
#變更後繼節點
def set_next(self, new_next):
self.next = new_next
定義完節點後,我們就可以來實現簡單的鍊表了。
首先來說一下鍊表的添加:添加操作,如果是插入操作,需要刪除原有的指針,將被刪除的指針重新指向新的節點,新的節點指向原來節點指向的那個節點。如果是添加在頭部,只需要將新的節點指向頭節點即可。
刪除操作,需要將被刪節點的前一個節點直接指向被刪節點的後一個節點即可。
# Reference codes:https://www.jianshu.com/p/e4000619232bclass Linked_list:
# 初始化,頭結點為空以上我們便實現了一個簡單的鍊表。而關於雙鍊表和循環鍊表呢,與單鍊表不同的是,雙鍊表多了一個頭指針,每個節點既可以指向上個節點也可以指向下個節點。循環鍊表的特點就是最後一個節點的指針指向頭節點。
棧(Stack)
棧,也是一種線性的數據結構,他有個最經典的特徵就是FILO(先進後出原則)。
先進後出是什麼意思呢?打個比方,你現在有個羽毛球球桶,當你需要打球的時候,通常會在球桶頂端拿球,打完了放回去的時候,卻不能放在頂端,只能從底部塞入,再次去拿球的時候,只能從頂部去拿新的球,這就是這就是棧。(下圖有誤,應該是從哪放就從哪拿)
如何用python實現棧呢?我們使用python中的list來實現十分簡單。因為棧的特性,它的刪除操作只能從棧頂開始刪,所以移除的元素都是棧頂元素,添加元素也只能從棧底添加。
class Stack:
# 初始化棧為空列表
def __init__(self):
self.items = []
# 判斷棧是否為空
def is_empty(self):
return self.items == []
# 返回棧頂
def peek(self):
return self.items[len(self.items) - 1]
# 返回棧的大小
def size(self):
return len(self.items)
# 入棧
def push(self, item):
self.items.append(item)
# 出棧
def pop(self, item):
return self.items.pop()
隊列(Queue)
隊列,也是一種線性的數據結構,它和棧正好相反,它的特徵是FIFO(先進先出原則)。
先進先出的意思是:打個比方,一個n節的火車進入了隧道,最先出隧道的那節車廂肯定就是最先入隧道的那節車廂了,這就是隊列。
和上面一樣,我們也可以使用list來實現一個隊列:
class Queue:
def __init__(self):
self.items = []
def enqueue(self, item):
self.items.append(item)
def dequeue(self):
return self.items.pop(0)
def empty(self):
return self.size() == 0
def size(self):
return len(self.items)
python裡面也有自帶queue(隊列)這樣一個模塊,可以直接使用。(這裡我們就不提python裡面其他的隊列了,例如LifoQueue,PriorityQueue)
import queue
q = queue.Queue(3) # 調用構造函數,初始化一個大小為3的隊列
print(q.empty()) # 判斷隊列是否為空,也就是隊列中是否有數據
# 入隊,在隊列尾增加數據, block參數,可以是True和False 意思是如果隊列已經滿了則阻塞在這裡,
# timeout 參數 是指超時時間,如果被阻塞了那最多阻塞的時間,如果時間超過了則報錯。
q.put(13, block=True, timeout=5)
print(q.full()) # 判斷隊列是否滿了,這裡我們隊列初始化的大小為3
print(q.qsize()) # 獲取隊列當前數據的個數
# block參數的功能是 如果這個隊列為空則阻塞,
# timeout和上面一樣,如果阻塞超過了這個時間就報錯,如果想一隻等待這就傳遞None
print(q.get(block=True, timeout=None))
# queue模塊還提供了兩個二次封裝了的函數,
q.put_nowait(23) # 相當於q.put(23, block=False)
q.get_nowait() # 相當於q.get(block=False)
以上,便是線性數據結構的python版本的介紹,下期我們將聊聊:非線性的數據結構,歡迎「轉發」或者點擊「在看」支持!
「掃一掃,關注我吧」