咱們如何把現實中大量並且很是複雜的問題以特定的數據類型(個體)和特定的存儲結構(個體的關係)保存到相應的主存儲器(內存)中,以及在此基礎上爲實現某個功能而執行的相應操做,這個相應的操做也叫作算法
數據結構 == 個體 + 個體的關係 node
算法 == 對存儲數據的操做python
程序 = 數據的存儲 + 數據的操做 + 能夠被計算機執行的語言算法
常見的幾個排序(基於Python實現):數據庫
def BubbleSort(li): for i in range(len(li)): #i 0 - 8 flag = False for j in range(len(li)-i-1): #j 0 - 7 if li[j] > li[j+1]:# li[0] > li[1] li[j],li[j+1] = li[j+1],li[j] # [5,7,4,6.....] flag = True if not flag: print('你這是有序的') return
def SelectSort(li): for i in range(len(li)): minLoc = i for j in range(i+1,len(li)): if li[j] < li[minLoc]: li[j],li[minLoc] = li[minLoc],li[j]
def insert_sort(li): for i in range(1,len(li)): tmp = li[i] j = i - 1 while j >=0 and li[j] > tmp: li[j+1] = li[j] # print(li) j = j-1 li[j+1] = tmp
def partition(li,left,right): tmp= li[left] while left < right: while left < right and li[right] >= tmp: right = right-1 li[left] = li[right] while left <right and li[left] <= tmp: left = left+1 li[right] = li[left] li[left] = tmp return left def _quick_sort(li,left,right): if left<right: mid = partition(li,left,right) _quick_sort(li,left,mid-1) _quick_sort(li,mid+1,right)
# 歸併排序 '分治法' # O(nlogn) # O(n) 空間複雜度 def merge(li,low,mid,high): i = low j = mid +1 ltmp = [] while i <= mid and j <=high: if li[i] < li[j]: ltmp.append(li[i]) i +=1 else: ltmp.append(li[j]) j +=1 while i <= mid: ltmp.append(li[i]) i +=1 while j <= high: ltmp.append(li[j]) j +=1 li[low:high+1] = ltmp def merge_sort(li,low,high): if low < high: mid = (low+high)//2 merge_sort(li,low,mid) merge_sort(li,mid+1,high) merge(li,low,mid,high)
時間複雜度 O(n) def count_sort(li): count = [0 for i in range(11)] print(count) for index in li: count[index] +=1 print(count) li.clear() print(li) for index,val in enumerate(count): for i in range(val): li.append(index)
小結:數組
常見的查找:瀏覽器
# 順序查找 O(n) def lineSearch(li,val): for i in range(len(li)): if li[i] == val: return i
# 二分查找,有序的。無序的也能夠 # O(logn) def binSearch(li,low,high,val): if low < high : mid = (low+high) // 2 if li[mid] == val: return mid elif li[mid] > val: binSearch(li,low,mid-1,val) elif li[mid] < val: binSearch(li,mid + 1,high,val) else: return -1
應用場景:數據結構
from timeit import Timer def t1(): li = [] for i in range(10000): li.append(i) def t2(): li = [] for i in range(10000): li = li + [i] def t3(): li = [ i for i in range(10000)] def t4(): li = list(range(10000)) def t5(): li = [] for i in range(10000): li.extend([i]) tm1 = Timer("t1()", "from __main__ import t1") print("append:", tm1.timeit(1000)) tm2 = Timer("t2()", "from __main__ import t2") print("+:", tm2.timeit(1000)) tm3 = Timer("t3()", "from __main__ import t3") print("[i for i in range]:", tm3.timeit(1000)) tm4 = Timer("t4()", "from __main__ import t4") print("list:", tm4.timeit(1000)) tm5 = Timer("t5()", "from __main__ import t5") print("extend:", tm5.timeit(1000)) ### 測試往隊頭和隊尾進行添加
def t6(): li = [] for i in range(10000): li.append(i) def t7(): li = [] for i in rnage(10000): li.insert(0,i) tm6 = Timer("t6()", "from __main__ import t6") print("append:", tm6.timeit(1000)) tm7 = Timer("t7()", "from __main__ import t7") print("insert:", tm7.timeit(1000))
算法的思想:app
把全部的節點用一根線串起來
數組須要一塊連續的內存空間來存儲,對內存的要求比較高。若是咱們申請一個 100MB 大小的數組,當內存中沒有連續的、足夠大的存儲空間時,即使內存的剩餘總可用空間大於 100MB,仍然會申請失敗。 而鏈表偏偏相反,它並不須要一塊連續的內存空間,它經過「指針」將一組零散的內存塊串聯起來使用,因此若是咱們申請的是 100MB 大小的鏈表,根本不會有問題。
數組,在其python語言中稱爲列表,是一種基本的數據結構類型
關於列表的問題:ide
關於數組(列表)的優缺點函數
1.定義:
優勢:
缺點:
鏈表的節點的結構以下:
2.專業術語:
data爲自定義的數據,next爲下一個節點的地址。
3.鏈表的分類:
4.算法:
有一堆數據1,2,3,5,6,7咱們要在3和5之間插入4,若是用數組,咱們會怎麼作?固然是將5以後的數據日後退一位,而後再插入4,這樣很是麻煩,可是若是用鏈表,我就直接在3和5之間插入4就行,聽着就很方便
5.若是但願經過一個函數來對鏈表進行處理操做,至少須要接受鏈表的哪些參數?
只須要一個參數,頭結點便可,由於咱們能夠經過頭結點來推算出鏈表的其餘全部的參數
6.單鏈表的算法
7.雙向鏈表
雙鏈表中每一個節點有兩個指針:一個指向後面節點、一個指向前面節點
class Node(object): def __init__(self, data=None): self.data = data self.next = None self.prior = None
插入:雙向鏈表的操做:
p.next = curNode.next curNode.next.prior = p curNode.next = p p.prior = curNode
刪除:
p = curNode.next curNode.next = p.next p.next.prior = curNode del p
8.循環鏈表
循環鏈表是另外一種形式的鏈式存貯結構。它的特色是表中最後一個結點的指針域指向頭結點,整個鏈表造成一個環。
9.約瑟夫問題
設編號爲1,2,… n的n我的圍坐一圈,約定編號爲k(1<=k<=n)的人從1開始報數,數到m 的那我的出列,它的下一位又從1開始報數,數到m的那我的又出列,依次類推,直到全部人出列爲止,由此產生一個出隊編號的序列
循環鏈表 class Child(object): first = None def __init__(self, no = None, pNext = None): self.no = no self.pNext = pNext def addChild(self, n=4): cur = None for i in range(n): child = Child(i + 1) if i == 0: self.first = child self.first.pNext = child cur = self.first else: cur.pNext = child child.pNext = self.first cur = cur.pNext def showChild(self): cur = self.first while cur.pNext != self.first: print("小孩的編號是:%d" % cur.no) cur = cur.pNext print("小孩的編號是: %d" % cur.no) def countChild(self, m, k): tail = self.first while tail.pNext != self.first: tail = tail.pNext # 出來後,已是在first前面
# 從第幾我的開始數
for i in range(k-1): tail = tail.pNext self.first = self.first.pNext # 數兩下,就是讓first和tail移動一次
# 數三下,就是讓first和tail移動兩次
while tail != self.first: # 當tail == first 說明只剩一我的
for i in range(m-1): tail = tail.pNext self.first = self.first.pNext self.first = self.first.pNext tail.pNext = self.first print("最後留在圈圈中的人是:%d" % tail.no) c = Child() c.addChild(4) c.showChild() c.countChild(3,2)
一種能夠實現「先進後出」的存儲結構
棧相似於一個箱子,先放進去的書,最後才能取出來,同理,後放進去的書,先取出來
靜態棧
動態棧
棧的算法主要是壓棧和出棧兩種操做的算法,下面我就用代碼來實現一個簡單的棧。
首先要明白如下思路:
一種能夠實現「先進先出」的數據結構
全部和時間有關的操做都和隊列有關
一個函數本身或者間接調用本身
當有多個函數調用時,按照「先調用後返回」的原則,函數之間的信息傳遞和控制轉移必須藉助棧來實現,即系統將整個程序運行時所須要的數據空間安排在一個棧中,每當調用一個函數時,就在棧頂分配一個存儲區,進行壓棧操做,每當一個函數退出時,就釋放他的存儲區,即進行出棧操做,當前運行的函數永遠在棧頂的位置
def f(): print('FFFFFFF') g() def g(): print('GGGGGGG') k() def k(): print('KKKKKKK') if __name__ == "__main__": f()
def f(n): if n == 1: print('hello') else: f(n-1)
本身調用本身也是和上面的原理同樣
n規模的實現,得益於n-1規模的實現
# for循環實現
multi = 1
for i in range(3): multi = multi * (i+1) print(multi) # 遞歸實現
def f(n): if 1 == n: return 1
else: return f(n-1)*n
def sum(n): if 1 == n: return n else: return sum(n-1) + n
樹和森林就是以遞歸的方式定義的
樹和圖的算範就是以遞歸的方式實現的
不少數學公式就是以遞歸的方式實現的(斐波那楔序列)
數據結構:
從狹義的方面講:
從廣義方面來說:
算法
從狹義的方面講:
從廣義的方面講:
咱們能夠簡單的認爲:
通俗的定義:
1.樹就是由節點和邊組成的
2.每個節點只能有一個父節點,但能夠有多個子節點。但有一個節點例外,該節點沒有父節點,此節點就稱爲根節點
如何把一個非線性結構的數據轉換成一個線性結構的數據存儲起來?
二叉樹的操做
森林的操做
1.二叉樹的先序遍歷[先訪問根節點]
2.二叉樹的中序遍歷 [中間訪問根節點]
3.二叉樹的後序遍歷 [最後訪問根節點]
4.已知先序和中序,如何求出後序?
#### 例一
先序:ABCDEFGH 中序:BDCEAFHG 求後序? 後序:DECBHGFA #### 例二
先序:ABDGHCEFI 中序:GDHBAECIF 求後序? 後序:GHDBEIFCA
5.已知中序和後序,如何求出先序?
中序:BDCEAFHG
後序:DECBHGFA
求先序?
先序:ABCDEFGH
class Node(object): """節點類"""
def __init__(self, elem=-1, lchild=None, rchild=None): self.elem = elem self.lchild = lchild self.rchild = rchild class Tree(object): """樹類"""
def __init__(self): self.root = Node() self.myli = [] def add(self, elem): """爲樹添加節點""" node = Node(elem) if self.root.elem == -1: # 若是樹是空的,則對根節點賦值
self.root = node self.myli.append(self.root) else: treeNode = self.myli[0] # 此結點的子樹尚未齊。
if treeNode.lchild == None: treeNode.lchild = node self.myli.append(treeNode.lchild) else: treeNode.rchild = node self.myli.append(treeNode.rchild) self.myli.pop(0) def front_digui(self, root): """利用遞歸實現樹的先序遍歷"""
if root == None: return
print(root.elem) self.front_digui(root.lchild) self.front_digui(root.rchild) def middle_digui(self, root): """利用遞歸實現樹的中序遍歷"""
if root == None: return self.middle_digui(root.lchild) print(root.elem) self.middle_digui(root.rchild) def later_digui(self, root): """利用遞歸實現樹的後序遍歷"""
if root == None: return self.later_digui(root.lchild) self.later_digui(root.rchild) print(root.elem) if __name__ == '__main__': """主函數""" elems = range(10) #生成十個數據做爲樹節點
tree = Tree() #新建一個樹對象
for elem in elems: tree.add(elem) #逐個添加樹的節點
print('遞歸實現先序遍歷:') tree.front_digui(tree.root)
數據結構研究的就是數據的存儲和數據的操做的一門學問
數據的存儲又分爲兩個部分:
從某個角度而言,數據存儲最核心的就是個體關係如何進行存儲
個體的存儲能夠忽略不計