?1.順序表
?2.鏈表
?3.棧
?4.隊列
??線性結構是一種有序數據項的集合,其中每個數據項都有唯一的前驅和后繼(除第一個沒有前驅,最后一個沒有后繼)。不同線性結構的關鍵區別在于數據項增減的方式,有的結構只允許數據從一端添加,而有的結構則允許數據從兩端移除。不同的方式形成了不同的線性結構,如棧(Stack)、隊列(Queue)、雙端隊列(Deque)和列表(List)。
1. 順序表
??(1)連續存儲與順序表
??計算機內部存儲的本質是將是將所需要存儲的數據類型轉換為二進制表示,并存儲在基本存儲單元中(字節),1字節=8位,不同的數據類型所占用的內存大小不同,如32位計算機中,基本int占4個字節,char占1個字節。存儲在基本單元中的數據都是0、1,需要通過存儲時聲明的數據類型,確認在取出時是當作int或char處理。
??連續存儲的概念是,定義一個有順序的數據集合,如List = [2,54,13,31],在存儲時(假設一個數字占4個字節),將各個數字轉換為二進制數據,在創建List集合時,集合指向的內存地址為起始位置0x17,對于第0個元素則以0x17標記,由于一個數字占4個字節,那么下一個數字存儲的內存地址為0x17+41=0x21,此后依次類推(起始地址+下標數據類型所占字節數)。
??(2)順序表的結構與實現*
??順序表在實現時,除了數據存儲空間,一般還需要添加一個表頭來存儲順序表的信息(順序表內存多大、現在存儲了多少)。順序表實現的兩種基本方式是一體式結構(表頭和存儲空間連續)和分離式結構(表頭和存儲空間分離,表頭最后一個內存存儲了指向存儲空間的地址):
??(3)順序表的操作
??①增加元素:
??②刪除元素時間復雜度同上
2. 鏈表
?2.1 單鏈表
??很明顯,順序表存在一個問題,在進行擴充時,如果當前內存不足,需要重新申請內存,將原內存存儲遷移過去,這樣會同時造成時間(復制)和空間(再次申請內存時預留)的浪費。那么能否實現這樣一個表,保留順序表索引的特性,同時,在擴充時,動態的申請內存直接添加呢?
??(1)鏈表的結構
??鏈表就能夠實現這樣的功能,存儲一個元素的區域不再是一個元素的大小,還需要包含一個內存區域來存儲指向下一個元素的地址:
??(2)python中地址的指向
??在python中,創建一個變量a = 10,a并不像是C一樣是指存儲10的內存的別名,也無法像C一樣創建一個指針變量來指向一個地址。python中的變量名實際上保存的是指向保存10的內存的地址。所以在python中很方便的可以實現鏈表結構,通過直接創建一個變量即可作為指向一塊內存的地址。
??(3)鏈表的實現
??通過python實現List,鏈表結構如下:
??(4)單鏈表與順序表的對比
??①鏈表失去了順序表隨機讀取的優點,同時由于鏈表還需要存儲地址,空間開銷大,但存儲空間靈活;
??②在刪除和插入操作中,鏈表主要是遍歷耗時,刪除和插入都是O(1)的;而順序表遍歷查找很快,但拷貝覆蓋耗時。
單向鏈表代碼:
# coding:utf-8
class Node(object):
def __init__(self,elem = None):
self.elem = elem
self.next = None
class SingleLinkList(object):
"""single Link list"""
def __init__(self):
self.__head = None
def is_empty(self):
"""confirm whether the list is empty"""
return self.__head == None
def length(self):
"""the length of list"""
cur = self.__head
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
"""travel the list,and print the every value of the list"""
print("[",end=" ")
cur = self.__head
while cur != None:
print(cur.elem,end = ", ")
cur = cur.next
print("]",end=" ")
def add(self,elem):
"""add element into the head of list"""
node = Node(elem)
node.next = self.__head
self.__head = node
def append(self,elem):
"""add element into the tail of list"""
node = Node(elem)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
def insert(self,pos,elem):
"""insert element into the specific index position of list"""
if pos <= 0:
self.add(elem)
elif pos > self.length() - 1:
self.append(elem)
else:
node = Node(elem)
pre = self.__head
count = 0
while count < pos - 1:
count += 1
pre = pre.next
node.next = pre.next
pre.next = node
def remove(self,elem):
"""remove a element that is found first"""
if self.is_empty():
return "empty list"
else:
cur = self.__head
pre = None
while cur != None:
if cur.elem == elem:
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
cur.next = None
return
else:
pre = cur
cur = cur.next
def search(self,elem):
"""confirm whether the element is exist"""
if self.is_empty():
return "empty list"
else:
cur = self.__head
while cur != None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
if __name__ == "__main__":
ll = SingleLinkList()
print("the list is empty:",ll.is_empty())
print("the length of empty list:",ll.length())
ll.append(1)
print("the list is empty:",ll.is_empty())
print("the length of list:",ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
print("---1 2 3 4 5 6---------------------")
print("---88 7 1 16 2 3 4 5 6---------------------")
ll.add(7)
ll.insert(-1,88)
ll.insert(3,16)
ll.travel()
print()
print("------------------------------")
print(ll.search(3))
ll.remove(16)
ll.travel()
print()
ll.remove(88)
ll.travel()
print()
ll.remove(6)
ll.travel()
?2.2 單向循環鏈表
??單向循環鏈表與單鏈表結構上的唯一區別就是尾結點不再指向None,而是指向頭節點,形成一個閉環。
代碼實現:
# coding:utf-8
class Node(object):
def __init__(self,elem = None):
self.elem = elem
self.next = None
class SingleLinkList(object):
"""single Link list"""
def __init__(self,node = None):
self.__head = node
if node:
node.next = node
def is_empty(self):
"""confirm whether the list is empty"""
return self.__head == None
def length(self):
"""the length of list"""
if self.is_empty():
return 0
cur = self.__head
count = 1
while cur.next != self.__head:
count += 1
cur = cur.next
return count
def travel(self):
"""travel the list,and print the every value of the list"""
print("[",end=" ")
if self.is_empty():
print("",end = "")
else:
cur = self.__head
while cur.next != self.__head:
print(cur.elem,end = ", ")
cur = cur.next
print(cur.elem,end = " ")
print("]",end = " ")
def add(self,elem):
"""add element into the head of list"""
node = Node(elem)
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
self.__head = node
cur.next = self.__head
def append(self,elem):
"""add element into the tail of list"""
node = Node(elem)
if self.is_empty():
self.__head = node
node.next = self.__head
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
cur.next = node
node.next = self.__head
def insert(self,pos,elem):
"""insert element into the specific index position of list"""
if pos <= 0:
self.add(elem)
elif pos > self.length() - 1:
self.append(elem)
else:
node = Node(elem)
pre = self.__head
count = 0
while count < pos - 1:
count += 1
pre = pre.next
node.next = pre.next
pre.next = node
def remove(self,elem):
"""remove a element that is found first"""
if self.is_empty():
return "empty list"
else:
cur = self.__head
pre = None
while cur.next != self.__head:
if cur.elem == elem:
#head node
if cur == self.__head:
tail = self.__head
while tail != self.__head:
tail = tail.next
self.__head = cur.next
tail.next = self.__head
else:
#median node
pre.next = cur.next
cur.next = None
return
else:
pre = cur
cur = cur.next
#tail node
if cur.elem == elem:
if cur == self.__head:
self.__head = None
else:
pre.next = self.__head
cur.next = None
def search(self,elem):
"""confirm whether the element is exist"""
if self.is_empty():
return "empty list"
else:
cur = self.__head
while cur.next != self.__head:
if cur.elem == elem:
return True
else:
cur = cur.next
if cur.elem == elem:
return True
return False
if __name__ == "__main__":
ll = SingleLinkList()
print("the list is empty:",ll.is_empty())
print("the length of empty list:",ll.length())
ll.append(1)
print("the list is empty:",ll.is_empty())
print("the length of list:",ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
print("---1 2 3 4 5 6---------------------")
print("---88 7 1 16 2 3 4 5 6---------------------")
ll.add(7)
ll.insert(-1,88)
ll.insert(3,16)
ll.travel()
print()
print("------------------------------")
print(ll.search(3))
ll.remove(16)
ll.travel()
print()
ll.remove(88)
ll.travel()
print()
ll.remove(6)
ll.travel()
?2.3 雙向鏈表
??雙向鏈表的節點,除頭節點和尾節點,每個節點都會指向前驅節點和后繼節點:代碼實現
# coding:utf-8
class Node(object):
def __init__(self,elem = None):
self.elem = elem
self.next = None
self.prev = None
class DoubleLinkList(object):
"""single Link list"""
def __init__(self):
self.__head = None
def is_empty(self):
"""confirm whether the list is empty"""
return self.__head == None
def length(self):
"""the length of list"""
cur = self.__head
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
"""travel the list,and print the every value of the list"""
print("[",end=" ")
cur = self.__head
while cur != None:
print(cur.elem,end = ", ")
cur = cur.next
print("]",end=" ")
def add(self,elem):
"""add element into the head of list"""
node = Node(elem)
node.next = self.__head
self.__head.prev = node
self.__head = node
def append(self,elem):
"""add element into the tail of list"""
node = Node(elem)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
node.prev = cur
def insert(self,pos,elem):
"""insert element into the specific index position of list"""
if pos <= 0:
self.add(elem)
elif pos > self.length() - 1:
self.append(elem)
else:
node = Node(elem)
cur = self.__head
count = 0
while count < pos:
count += 1
cur = cur.next
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
def remove(self,elem):
"""remove a element that is found first"""
if self.is_empty():
return "empty list"
else:
cur = self.__head
while cur != None:
if cur.elem == elem:
if cur == self.__head:
self.__head = cur.next
if cur.next:
cur.next.prev = None
cur.next = None
else:
if cur.next is None:
cur.prev.next = None
cur.prev = None
else:
cur.prev.next = cur.next
cur.next.prev = cur.prev
cur.next = None
cur.prev = None
return
else:
cur = cur.next
def search(self,elem):
"""confirm whether the element is exist"""
if self.is_empty():
return "empty list"
else:
cur = self.__head
while cur != None:
if cur.elem == elem:
return True
else:
cur = cur.next
return False
if __name__ == "__main__":
ll = DoubleLinkList()
print("the list is empty:",ll.is_empty())
print("the length of empty list:",ll.length())
ll.append(1)
print("the list is empty:",ll.is_empty())
print("the length of list:",ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
print("---1 2 3 4 5 6---------------------")
print("---88 7 1 16 2 3 4 5 6---------------------")
ll.add(7)
ll.insert(-1,88)
ll.insert(3,16)
ll.travel()
print()
print("------------------------------")
print(ll.search(3))
ll.remove(16)
ll.travel()
print()
ll.remove(88)
ll.travel()
print()
ll.remove(6)
ll.travel()
2. 棧
??棧(Stack)是一種有序的數據項集合,在棧中,數據項的加入和移除都發生在同一端(操作端叫棧頂)。距離棧底越近的數據,停留在棧中的時間越長,新加入的數據則會被最先移除,即后進先出
。棧相當于對鏈表(順序表)進行了限制,不能任意刪除、插入,而只能在一端刪除添加。
??通過python內置List實現棧功能:
# coding:utf-8
class My_stack(object):
def __init__(self):
self.__list = []
def push(self,item):
self.__list.append(item)
def pop(self):
return self.__list.pop()
def peek(self):
if self.__list:
return self.__list[-1]
else:
return None
def is_empty(self):
return self.__list == []
def size(self):
return len(self.__list)
if __name__ == "__main__":
s = My_stack()
print(s.is_empty())
s.push(1)
s.push(2)
s.push(3)
s.push(4)
s.push(5)
s.push(6)
print(s.size())
print(s.peek())
print(s.pop())
print(s.size())
(1)棧的應用:簡單括號匹配
??在數值計算或者代碼中括號((
、{
、]
)都是成對出現的,且嵌套出現,如如果出現非成對或非嵌套形式將會導致出錯,如()())
、(([)])
都是錯誤的括號匹配方式。那么如何實現括號的正確匹配呢?算法的流程是,從左往右掃描,遇到左括號就壓入棧,遇到右括號則判斷棧是否為空,不為空則移除棧頂數據,為空則匹配失敗。當掃描完成后,判斷棧空,棧空則匹配成功,棧不空則匹配失敗。
# coding:utf-8
import sys
sys.path.append('./')
import my_stack as Stack
def par_checker(symbol_str):
s = Stack.My_stack()
balanced = True
index = 0
while index < len(symbol_str) and balanced:
symbol = symbol_str[index]
if symbol in "([{":
s.push(symbol)
elif symbol in ")]}" :
if s.is_empty():
balanced = False
else:
temp = s.peek()
if matches(temp,symbol):
s.pop()
else:
balanced = False
index += 1
if balanced and s.is_empty():
return True
else:
return False
def matches(open_s,close_s):
opens = list("([{")
closes = list(")]}")
return opens.index(open_s) == closes.index(close_s)
if __name__ == "__main__":
print(par_checker('(([({})]))'))
print(par_checker('((([)]))'))
print(par_checker('(5+3)*2+(2-(3+1))'))
(2)棧的應用:十進制轉化為二進制
??進制之間的轉換就是將數字除以目標進制數,循環求余數,所有余數排列即為轉化后的數字,先求出的余數在低位排列,類似棧的先進后出,所以通過棧來實現:
# coding:utf-8
import sys
sys.path.append('./')
import my_stack as Stack
def divide_by2(d_num):
remstack = Stack.My_stack()
while d_num > 0:
rem = d_num % 2
remstack.push(rem)
d_num = d_num // 2
binary_str = ""
while not remstack.is_empty():
binary_str = binary_str + str(remstack.pop())
return binary_str
if __name__ == "__main__":
print(divide_by2(42))
(3)棧的應用:表達式轉換
??通常我們看到的計算表達式是中綴
表示法,中綴表示法對于人來說比較好理解,但是如果不加括號、算符優先級問題,極其容易引起混淆。但對于計算機而言,最好是能明確規定所有計算順序,而不用處理優先級問題。
??所以,引入了前綴
和后綴
表示法,這兩種方法只有一個規則:操作符只與其最近的兩個操作數進行計算(前綴為符號及其鄰近后兩位操作數,后綴為前兩位)。這兩種方法不用考慮優先級問題。實現中綴表達式轉化為前綴(后綴)表達式算法:
??①首先將中綴表達式轉換為全括號形式(如5+2*3+4
→ (5+(2*3)+4)
)
??②按照下述方法移動操作符,并去除括號
轉換為后綴表達式:
①創建一個棧來存儲符號,從左向右讀取每個字符(數字、字母、符號、括號)
②如果讀取到字母或數字,直接追加到輸出列表中
③如果讀取到左括號,壓入棧中
④如果讀取到右括號:讀取棧頂符號,如果不是左括號,就一直彈出棧中元素,直到棧頂為左括號
⑤如果是操作符:判斷棧不為空時,棧頂操作符優先級是否大于等于當前讀取到符號,如果大于等于,循環彈出棧頂優先級高的操作符;再將當前符號壓入棧
⑥最后判斷棧是否為空,不空則依次彈出棧,追加至輸出列表
# coding:utf-8
import sys
sys.path.append('./')
import my_stack as Stack
def infix2postfix(infixexpr):
#define priorities
prec = {}
prec["*"] = 3
prec["/"] = 3
prec["+"] = 2
prec["-"] = 2
prec["("] = 1
op_stack = Stack.My_stack()
postfix_list = []
token_list = infixexpr.split()
# travel expression
for token in token_list:
if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
postfix_list.append(token)
elif token == "(":
op_stack.push(token)
elif token == ")":
top_token = op_stack.pop()
while top_token != "(":
postfix_list.append(top_token)
top_token = op_stack.pop()
else:
while (not op_stack.is_empty()) and (prec[op_stack.peek()] >= prec[token]):
postfix_list.append(op_stack.pop())
op_stack.push(token)
while not op_stack.is_empty():
postfix_list.append(op_stack.pop())
return "".join(postfix_list)
if __name__ == "__main__":
s = "( ( ( A + 3 ) * B ) + ( 6 / C ) )"
ss = "A + 3 * C + 6 / C"
print(s)
print(ss)
print("---------")
print(infix2postfix(s))
print(infix2postfix(ss))
(4)棧的應用:后綴表達式求值
??算法:從左向右掃描,如果是數字則壓入棧,遇到符號則取出兩個數字進行運算(先取出來的數字在算符右邊),運算結果壓回棧。
# coding:utf-8
import sys
sys.path.append("./")
import my_stack as Stack
def post_eval(postfix_expr):
op_num_stack = Stack.My_stack()
token_list = postfix_expr.split()
for token in token_list:
if token not in "*/+-":
token = int(token)
op_num_stack.push(token)
else:
op2 = op_num_stack.pop()
op1 = op_num_stack.pop()
result = math_op(token,op1,op2)
op_num_stack.push(result)
return op_num_stack.pop()
def math_op(op,op1,op2):
if op == "*":
return op1 * op2
elif op == "/":
return op1 / op2
elif op == "+":
return op1 + op2
elif op == "-":
return op1 - op2
if __name__ == "__main__":
ops = "32 4 + 6 * 7 8 / +"
print(post_eval(ops))
print("---------")
print("(((32+4)*6)+(7/8) = ",(((32+4)*6)+(7/8)))
3. 隊列
??隊列(Queue)也是一種線性表,但只允許在一段插入,另一端進行刪除的操作,即先進先出
。
(1)隊列實現
# coding:utf-8
class Queue(object):
def __init__(self):
self.__list = []
def enqueue(self,item):
self.__list.append(item)
def dequeue(self):
return self.__list.pop(0)
def is_empty(self):
return self.__list == []
def size(self):
return len(self.__list)
if __name__ == "__main__":
q = Queue()
print(q.is_empty())
print(q.size())
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.enqueue(4)
q.enqueue(10)
print(q.dequeue())
print(q.size())
(2)雙端隊列實現
??雙端隊列即允許兩端插入和取出
# coding:utf-8
class Deque(object):
def __init__(self):
self.__list = []
def add_head(self,item):
self.__list.insert(0,item)
def add_tail(self,item):
self.__list.append(item)
def remove_head(self):
return self.__list.pop(0)
def remove_tail(self):
return self.__list.pop()
def is_empty(self):
return self.__list == []
def size(self):
return len(self.__list)
if __name__ == "__main__":
d = Deque()
print(d.is_empty())
print(d.size())
d.add_head(1)
d.add_tail(2)
d.add_head(3)
d.add_head(4)
d.add_tail(5)
d.add_tail(6)
print(d.remove_head())
print(d.size())
print(d.remove_tail())
print(d.size())
(3)隊列的應用:熱土豆
??熱土豆問題也叫約瑟夫問題:傳燙手的熱土豆,鼓聲停止時,手里有土豆的人出列;如果去掉鼓,改為傳過固定人數,就出列,即約瑟夫問題。也就是一圈人,某個人開始數數,假設數到7的人出列。通過隊列的可以實現這樣的功能,每次從隊首取出一個人,并計數,沒有計數為7的人從隊尾入隊列,計數為7則不入隊列,一直循環,直到只剩下一個人。
# coding:utf-8
import sys
sys.path.append('./')
import Queue
def hot_patato(namelist,num):
name_queue = Queue.Queue()
for name in namelist:
name_queue.enqueue(name)
while name_queue.size() > 1:
for i in range(num):
name_queue.enqueue(name_queue.dequeue())
name_queue.dequeue()
return name_queue.dequeue()
if __name__ == "__main__":
ll = ["A","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S"]
print(hot_patato(ll,7))
(4)隊列的應用:打印任務
??當多人共享一臺打印機時,采取先到先服務的隊列策略來執行打印任務。在這種設定下,首要的問題是:打印作業系統的容量有多大?在能夠接受的等待時間內,系統能容納多少用戶以多高頻率提交多少打印任務?
??問題背景設置:
??對問題進行抽象:
①打印機屬性:
??i、私有字段:打印速度、當前是否有打印任務、當前任務所需剩余時間
??ii、打印機功能(方法):執行打印1秒、打印機是否空閑、是否開始下個任務
②打印任務屬性:
??i、私有字段:生成打印任務的時間、打印任務頁數
??ii、任務方法:獲取生成時間、獲取打印頁數、計算生成時間至開始打印本任務的等待時間
③打印流程模擬(設置打印時間范圍和打印速度):
??i、初始化打印機、打印隊列、等待時間表
??ii、在打印時間內,每1秒需要執行:
????a、是否生成了打印任務,如果生成了,加入打印隊列
????b、打印機是否忙且隊列是否為空,若打印機空閑且隊列不空,則開始打印下一任務,同時計算這個任務在開始打印時間與生成這個任務時的時間差
????c、執行1秒打印
注:
?①打印任務的生成:一共10人,每小時每次提交2次任務,則每一秒提交任務的概率為20/3600=1/180;
?②打印任務頁數隨機1-20,所以每次生成任務時,隨機生成頁數。
# coding:utf-8
import sys
sys.path.append('./')
import random
import Queue
class Printer:
def __init__(self,ppm):
self.__pagerate = ppm #print speed
self.__current_task = None
self.__time_remaining = 0
def tick(self):
if self.__current_task != None:
self.__time_remaining = self.__time_remaining - 1
if self.__time_remaining <= 0:
self.__current_task = None
def busy(self):
if self.__current_task != None:
return True
else:
return False
def start_next(self,new_task):
self.__current_task = new_task
self.__time_remaining = new_task.get_pages() * 60 / self.__pagerate
class Task:
def __init__(self,time):
self.__timestamp = time
self.__pages = random.randrange(1,21)
def get_stamp(self):
return self.__time_stamp
def get_pages(self):
return self.__pages
def wait_time(self,current_time):
return current_time - self.__timestamp
def new_print_task():
num = random.randrange(1,181)
if num == 111:
return True
else:
return False
def simulation_print(num_seconds,pages_per_minute):
lab_printer = Printer(pages_per_minute)
print_queue = Queue.Queue()
waiting_times = []
for current_time in range(num_seconds):
if new_print_task():
task = Task(current_time)
print_queue.enqueue(task)
if (not lab_printer.busy()) and (not print_queue.is_empty()):
next_task = print_queue.dequeue()
waiting_times.append(next_task.wait_time(current_time))
lab_printer.start_next(next_task)
lab_printer.tick()
ave_waiting_time = sum(waiting_times)/len(waiting_times)
print("Print task within %d hours,averge wait %6.2f secs,%3d tasks remaining"\
%(num_seconds/3600,ave_waiting_time,print_queue.size()))
if __name__ == "__main__":
print("ppm 10:")
for i in range(10):
simulation_print(3600,10)
print("ppm 5:")
for i in range(10):
simulation_print(3600,5)