1 概念node
1.1 時間複雜度python
假設存在函數g,使得算法A處理規模爲n的問題示例所用時間爲T(n)=O(g(n))則稱T(n)爲算法A的漸近時間複雜度,簡稱時間複雜度。算法
g(n)則稱爲一個時間複雜度的大O表示法。數組
漸近函數定義:考慮一個函數 ,咱們須要瞭解當
變得很是大的時候
的性質。令
,在
特別大的時候,第二項 數據結構
比起第一項 要小不少。因而對於這個函數,有以下斷言:
在
的狀況下與
漸近等價」,記做
。app
最壞時間複雜度:算法完成工做最多須要多少基本操做。函數
時間複雜度的基本計算規則:post
常見時間複雜度:性能
執行次數函數舉例 | 階 | 非正式術語 |
---|---|---|
12 | O(1) | 常數階 |
2n+3 | O(n) | 線性階 |
3n^2+2n+1 | O(n^2) | 平方階 |
5log2n+20 | O(logn) | 對數階 |
2n+3nlog2n+19 | O(nlogn) | nlogn階 |
6n^3+2n^2+3n+4 | O(n^3) | 立方階 |
2^n | O(2^n) | 指數階 |
常見時間複雜度之間的關係:測試
所消耗的時間從小到大:O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
1.2 timeit模塊(python內置模塊分析性能)
def cal(): for a in range(1001): for b in range(1001-a): c = 1000-a-b if a**2 + b**2 == c**2 : return a,b,c t = timeit.Timer('cal()','from __main__ import cal') print(t.timeit(number=1000))
1.3 數據結構
數據結構只是靜態的描述了數據元素之間的關係。Python的內置數據結構:列表、元組、字典。
程序 = 數據結構 + 算法
抽象數據類型(ADT):一個數學模型以及定義在此數學模型上的一組操做。
經常使用數據運算:插入、刪除、修改、查找、排序。
2 線性表
2.1 順序表
eg.python中list爲線性表。
eg.int類型在32位計算機中,存儲空間爲4個字節(8位),在內存中按照連續存儲單元(1個字節)進行存儲。
一、連續存儲元素信息 二、連續存儲地址信息
2.1.1 線性表結構
線性表的空間是固定的,若要進行擴充:一、增長固定數目 二、每次擴充容量加倍
頭部插入數據時間複雜度爲O(1),中間插入和尾部插入時間複雜度爲O(n)。
list內置操做的時間複雜度:
dict內置操做的時間複雜度:
2.2 鏈表
在每個節點(數據存儲單元)裏存放下一個節點的位置信息(即地址)。鏈表須要額外開銷存儲位置信息,可是在內存中是分散存儲的,因此能夠充分離散的內存空間。
2.2.1 單向鏈表
class SingleNode(object): '''節點''' def __init__(self,item): self.elem = item self.next = None class SingleLink(object): def __init__(self): self.__head =None def is_empty(self): '''鏈表是否爲空''' return self.__head ==None def length(self): '''鏈表長度''' #cur遊標,用來移動遍歷節點 cur = self.__head count = 0 while cur != None: count +=1 cur = cur.next return count def travel(self): '''遍歷整個鏈表''' cur = self.__head while cur != None: print(cur.elem,end= '') cur = cur.next def add(self,item): '''鏈表頭部添加元素''' # 先建立一個保存item值的節點 node = SingleNode(item) node.next = self.__head self.__head = node def append(self,item): '''鏈表尾部添加元素''' node = SingleNode(item) if self.__head == None: self.__head = node else: cur = self.__head while cur.next != None: cur = cur.next cur.next = node def insert(self,pos,item): '''指定位置添加元素''' node = SingleNode(item) cur = self.__head if pos ==0: node.next = cur self.__head = node elif self.length()-1< pos: while cur != None: cur = cur.next cur.next =item else: count = 0 while count != pos-1: count +=1 cur = cur.next node.next = cur.next cur.next = node def remove(self,item): '''指定元素刪除節點''' node = SingleNode(item) cur = self.__head pre = None while cur!= None: if cur.elem == node.elem: # 若是第一個就是刪除的節點 if not pre: self.__head = cur.next else: pre.next = cur.next break else: pre = cur cur = cur.next def search(self,item): '''查找節點是否存在''' cur = self.__head node = SingleNode(item) while cur !=None: if cur.elem == node.elem: return True else: cur = cur.next return False if __name__ == '__main__': l = SingleLink() print(l.is_empty()) l.add(1) l.add(2) l.append(0) l.insert(0,3) l.insert(2, 0) l.remove(0) l.remove(4) print(l.is_empty()) print(l.search(4)) print(l.search(0)) print(l.length()) l.travel()
2.2.2 雙向鏈表
class DoubleNode(object): def __init__(self,item): self.elem = item self.pre = None self.next = None class DoubleLink(object): def __init__(self): self.__head = None def is_empty(self): return self.__head == None def length(self): cur = self.__head count = 0 while cur!=None: count +=1 cur= cur.next return count def travel(self): cur = self.__head if self.__head ==None: return else: while cur!=None: print(cur.elem,end="") cur = cur.next print("") def add(self,item): node = DoubleNode(item) if self.__head ==None: self.__head = node else: node.next =self.__head self.__head.pre = node self.__head = node def append(self,item): node =DoubleNode(item) if self.__head ==None: self.__head = node else: cur = self.__head while cur.next!=None: cur = cur.next node.pre = cur cur.next = node def insert(self,pos,item): node = DoubleNode(item) if pos <=0: self.add(item) elif pos >= self.length(): self.append(item) else: count = 0 cur = self.__head if count != pos: count +=1 cur = cur.next node.next = cur node.pre = cur.pre cur.pre.next = node cur.pre = node def remove(self,item): node = DoubleNode(item) if self.__head ==None: return else: cur = self.__head if cur.elem == item: # 若是首節點的元素便是要刪除的元素 if cur.next == None: # 若是鏈表只有這一個節點 self.__head = None else: # 將第二個節點的prev設置爲None cur.next.pre = None # 將_head指向第二個節點 self.__head = cur.next return while cur.next!= None: if cur.elem == item: # 將cur的前一個節點的next指向cur的後一個節點 cur.pre.next = cur.next # 將cur的後一個節點的prev指向cur的前一個節點 cur.next.pre = cur.pre break cur = cur.next #刪除的是最後一個 if cur.elem ==item: cur.pre.next = None else: return def search(self,item): node = DoubleNode(item) cur = self.__head while cur !=None: if cur.elem != node.elem: cur =cur.next else: return True return False
2.2.3 單向循環列表
class SingleCircleNode(object): def __init__(self,item): self.elem = item self.node =None class SingleCircleLink(object): def __init__(self): self.__head = None def is_empty(self): return self.__head == None def length(self): if self.__head == None: return 0 else: count = 1 cur = self.__head while cur.next != self.__head: count +=1 cur = cur.next return count def travel(self): if self.__head == None: return else: cur = self.__head while cur.next !=self.__head: print(cur.elem,end="") cur = cur.next print(cur.elem) def add(self,item): node = SingleCircleNode(item) if self.__head == None: self.__head = node node.next = self.__head 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,item): node = SingleCircleNode(item) if self.__head ==None: self.__head = node node.next = self.__head else: cur = self.__head while cur.next !=self.__head: cur =cur.next node.next = self.__head cur.next = node def insert(self,pos,item): node = SingleCircleNode(item) if pos <=0: self.add(item) elif pos >= self.length(): self.append(item) else: count =1 cur = self.__head while pos !=count: count +=1 cur = cur.next node.next = cur.next cur.next = node def remove(self,item): """刪除一個節點""" # 若鏈表爲空,則直接返回 if self.is_empty(): return # 將cur指向頭節點 cur = self.__head pre = None # 若頭節點的元素就是要查找的元素item if cur.elem == item: # 若是鏈表不止一個節點 if cur.next != self.__head: # 先找到尾節點,將尾節點的next指向第二個節點 while cur.next != self.__head: cur = cur.next # cur指向了尾節點 cur.next = self.__head.next self.__head = self.__head.next else: # 鏈表只有一個節點 self.__head = None else: pre = self.__head # 第一個節點不是要刪除的 while cur.next != self.__head: # 找到了要刪除的元素 if cur.elem == item: # 刪除 pre.next = cur.next return else: pre = cur cur = cur.next # cur 指向尾節點 if cur.elem == item: # 尾部刪除 pre.next = cur.next # node = SingleCircleNode(item) # if self.__head == None: # return # cur = self.__head # pre = None # while cur.next != self.__head: # if cur.elem == node.elem: # if pre is None: # rear = self.__head # while rear.next != self.__head: # rear = rear.next # self.__head = cur.next # rear.next = self.__head # else: # pre.next = cur.next # break # else: # pre = cur # cur = cur.next # if cur.elem == node.elem: # #匹配只有一個元素且是第一個元素 # if pre is None: # self.__head ==None # #匹配最後一個元素 # else: # pre.next = self.__head # else: # return # else: # cur = self.__head # pre = None # if cur.elem == node.elem: # if cur.next == None: # self.__head = None # else: # node.next = cur.next # self.__head = node # while cur.next != self.__head: # if cur.elem == node.elem: # pre.next = cur.next # else: # pre = cur # cur = cur.next # break # if cur.elem == node.elem: # pre.next = self.__head # else: # return def search(self,item): if self.__head ==None: return False else: cur = self.__head while cur.next != self.__head: if cur.elem == item: return True else: cur =cur.next if cur.next == item: return True else: return False
3 棧(stack)
也稱堆棧,是一種容器,可存入數據元素、訪問元素、刪除元素。因爲棧數據結構只容許在一端進行操做,於是按照後進先出(LIFO, Last In First Out)的原理運做。
class Stack(object): def __init__(self): self.items = [] def is_empty(self): '''判斷棧是否爲空''' return self.items ==[] def size(self): '''返回棧的元素個數''' return len(self.items) def push(self,item): '''添加一個新的元素item到棧頂''' self.items.append(item) def pop(self): '''彈出棧頂元素''' return self.items.pop() def peek(self): '''返回棧頂元素''' return self.items[len(self.items)-1] if __name__ == '__main__': s = Stack() s.push(1) s.push(2) print(s.pop()) print(s.peek())
4 隊列(queue)
class Queue(object): def __init__(self): self.items = [] def is_empty(self): '''判斷一個隊列是否爲空''' return self.items == [] def size(self): '''返回隊列的大小''' return len(self.items) def enqueue(self,item): '''往隊列中添加一個item元素''' self.items.append(item) def dequeue(self): '''從隊列頭部刪除一個元素''' return self.items.pop(0)
4.1 雙端隊列
雙端隊列(deque,全名double-ended queue),是一種具備隊列和棧的性質的數據結構。
雙端隊列中的元素能夠從兩端彈出,其限定插入和刪除操做在表的兩端進行。雙端隊列能夠在隊列任意一端入隊和出隊。
class Deque(object): '''建立一個空的雙端隊列''' def __init__(self): self.items = [] def is_empty(self): '''判斷雙端隊列是否爲空''' return self.items == [] def size(self): '''返回隊列的大小''' return len(self.items) def add_front(self,item): '''從隊頭加入一個item元素''' self.items.insert(0,item) def add_rear(self,item): '''從隊尾加入一個item元素''' self.items.append(item) def remove_front(self): '''從隊頭刪除一個item元素''' return self.items.pop(0) def remove_rear(self): '''從隊尾刪除一個item元素''' return self.items.pop()
5 排序與算法
算法的穩定性:穩定性:穩定排序算法會讓本來有相等鍵值的紀錄維持相對次序。
5.1 冒泡排序(Bubble Sort)
冒泡排序算法以下:
時間複雜度:
def BubbleSort(alist): n = len(alist)
count =0 for j in range(n-1): for i in range(n-1-j): if alist[i] > alist[i+1]: alist[i],alist[i+1] = alist[i+1],alist[i]
count +=1
if count ==0
return
if __name__ == '__main__': alist = [7,2,5,7,1,9,0,3,4] BubbleSort(alist) print(alist)
5.2 選擇排序(Selection sort)
時間複雜度:
def SelectionSort(alist): n = len(alist) for j in range(n-1): min_index = j for i in range(j+1,n): if alist[min_index] > alist[i]: min_index=i alist[min_index],alist[j] = alist[j],alist[min_index] if __name__ == '__main__': alist = [7,2,5,7,1,9,0,3,4] SelectionSort(alist) print(alist)
5.3 插入排序(Insertion Sort)
經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。在從後向前掃描過程當中,須要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。
時間複雜度:
def InsertionSort(alist): n = len(alist) for i in range(n): while i>0: if alist[i] < alist[i-1]: alist[i],alist[i-1]=alist[i-1],alist[i] i -=1 if __name__ == '__main__': alist = [7,2,5,7,1,9,0,3,4] InsertionSort(alist) print(alist)
5.4 希爾排序(Shell Sort)
也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。
時間複雜度:
def ShellSort(alist): n = len(alist) gap = n//2 while gap >0: for i in range(gap,n): while i>0: if alist[i] < alist[i-gap]: alist[i],alist[i-gap]= alist[i-gap],alist[i] i -=gap gap = gap//2 if __name__ == '__main__': alist = [7,2,5,7,1,9,0,3,4] ShellSort(alist) print(alist)
5.5 快速排序(Quick Sort)
也稱劃分交換排序(partition-exchange sort)
快速排序步驟爲:
時間複雜度:
def QuickSort(alist,first,last): if first >= last: return mid = alist[first] low = first high = last while low<high: while low<high and alist[high]>=mid: high -=1 alist[high],alist[low]=alist[low],alist[high] while low <high and alist[low]<mid: low +=1 alist[low],alist[high]=alist[high],alist[low] alist[low]=mid QuickSort(alist,first,low-1) QuickSort(alist,low+1,last) if __name__ == '__main__': alist = [7,2,5,7,1,9,0,3,4] QuickSort(alist,0,len(alist)-1) print(alist)
5.6 歸併排序
歸併排序的思想就是先遞歸分解數組,再合併數組。
將數組分解最小以後,比較兩個數組的最前面的數,誰小就先取誰,取了後相應的指針就日後移一位。而後再比較,直至一個數組爲空,最後把另外一個數組的剩餘部分複製過來便可。
時間複雜度:
def MergeSort(alist): n = len(alist) if n <=1: return alist mid = n//2 left_list = MergeSort(alist[:mid]) right_list = MergeSort(alist[mid:]) left_point,right_point = 0,0 result = [] while left_point<len(left_list) and right_point<len(right_list): if left_list[left_point] <= right_list[right_point]: result.append(left_list[left_point]) left_point +=1 else: result.append(right_list[right_point]) right_point+=1 #將循環剩下的的元素添加到列表的最後 result += left_list[left_point:] result += right_list[right_point:] return result if __name__ == '__main__': alist = [7,2,5,7,1,9,0,3,4] l = MergeSort(alist) print(l)
5.7 算法效率比較
5.8 搜索
搜索的幾種常見方法:順序查找、二分法查找、二叉樹查找、哈希查找
二分查找:做用於有序的線性表。
時間複雜度:
def BinarySearch(alist,item): if len(alist) == 0: return False else: mid = len(alist)//2 if alist[mid] == item: return True elif alist[mid] > item: return BinarySearch(alist[:mid-1],item) else: return BinarySearch(alist[mid+1:],item) if __name__ =='__main__': print(BinarySearch([4,6,7,8],4))
6 樹與樹的算法
樹的概念:樹(tree)是一種抽象數據類型(ADT)或是實做這種抽象數據類型的數據結構。
特色:
術語:
種類:
存儲:順序存儲和鏈式存儲(經過lchild和rchild指針)。
class Node(object): def __init__(self,item,lchild=None,rchild=None): self.root = item self.lchild = lchild self.rchild = rchild class Tree(object): def __init__(self,root=None): self.root = root def add(self,elem): node = Node(elem) if self.root ==Node: self.root = node #若是根節點不爲空 else: queue = [] queue.append(self.root) while queue: cur = queue.pop(0) if cur.lchild == Node: cur.lchild ==node return elif cur.rchild ==None: cur.rchild ==node return else: queue.append(cur.lchild) queue.append(cur.rchild)
6.1 二叉樹
二叉樹是每一個節點最多有兩個子樹的樹結構。一般子樹被稱做「左子樹」(left subtree)和「右子樹」(right subtree)。
性質:
6.2 二叉樹的遍歷
深度優先遍歷和廣度優先遍歷,深度優先通常用遞歸,廣度優先通常用隊列。
6.2.1 廣度遍歷(層次遍歷)
def breadth_travel(self, root): """利用隊列實現樹的層次遍歷""" if root == None: return queue = [] queue.append(root) while queue: node = queue.pop(0) print (node.elem) if node.lchild != None: queue.append(node.lchild) if node.rchild != None: queue.append(node.rchild)
6.2.2 先序遍歷歷(preorder)
根節點->左子樹->右子樹
def preorder(self, root): """遞歸實現先序遍歷""" if root == None: return print (root.elem) self.preorder(root.lchild) self.preorder(root.rchild)
6.2.3 中序遍歷(inorder)
左子樹->根節點->右子樹
def inorder(self, root): """遞歸實現中序遍歷""" if root == None: return self.inorder(root.lchild) print (root.elem) self.inorder(root.rchild)
6.2.4 後序遍歷(postorder)
左子樹->右子樹->根節點
def postorder(self, root): """遞歸實現後續遍歷""" if root == None: return self.postorder(root.lchild) self.postorder(root.rchild) print (root.elem)
ps.有中序遍歷和先序遍歷或後序遍歷能夠肯定一棵樹。