數據結構和算法是什麼?答曰:兵法!node
算法是計算機處理信息的本質,由於計算機程序本質上是一個算法來告訴計算機確切的步驟來執行一個指定的任務。通常地,當算法在處理信息時,會從輸入設備或數據的存儲地址讀取數據,把結果寫入輸出設備或某個存儲地址供之後再調用。python
對於算法而言,實現的語言並不重要,重要的是思想。算法
算法能夠有不一樣的語言描述實現版本(如C描述、C++描述、Python描述等),咱們如今是在用Python語言進行描述實現。數據結構
算法的五大特性app
需求:已知a+b+c=1000 a2+b2=c2 求a,b,c可能的值數據結構和算法
import time start_time = time.time() # 注意是三重循環 for a in range(0, 1001): for b in range(0, 1001): for c in range(0, 1001): if a**2 + b**2 == c**2 and a+b+c == 1000: print("a, b, c: %d, %d, %d" % (a, b, c)) end_time = time.time() print("elapsed: %f" % (end_time - start_time)) print("complete!") 運行結果: a, b, c: 0, 500, 500 a, b, c: 200, 375, 425 a, b, c: 375, 200, 425 a, b, c: 500, 0, 500 elapsed: 214.583347 complete! 注意運行的時間: 160.913325秒 個人電腦運行了大概26min。。。
import time start_time = time.time() # 注意是兩重循環 for a in range(0, 1001): for b in range(0, 1001-a): c = 1000 - a - b if a**2 + b**2 == c**2: print("a, b, c: %d, %d, %d" % (a, b, c)) end_time = time.time() print("elapsed: %f" % (end_time - start_time)) print("complete!") 運行結果: a, b, c: 0, 500, 500 a, b, c: 200, 375, 425 a, b, c: 375, 200, 425 a, b, c: 500, 0, 500 elapsed: 0.182897 complete! 注意運行的時間: 0.609427秒
對於同一問題,咱們給出了兩種解決算法,在兩種算法的實現中,咱們對程序執行的時間進行了測算,發現兩段程序執行的時間相差懸殊(160.913325秒相比於0.609427秒),由此咱們能夠得出結論:實現算法程序的執行時間能夠反應出算法的效率,即算法的優劣。函數
假設咱們將第二次嘗試的算法程序運行在一臺配置古老性能低下的計算機中,狀況會如何?極可能運行的時間並不會比在咱們的電腦中運行算法一的160.913325秒快多少。
==單純依靠運行的時間來比較算法的優劣並不必定是客觀準確的!==性能
程序的運行離不開計算機環境(包括硬件和操做系統),這些客觀緣由會影響程序運行的速度並反應在程序的執行時間上。那麼如何才能客觀的評判一個算法的優劣呢?測試
咱們假定計算機執行算法每個基本操做的時間是固定的一個時間單位,那麼有多少個基本操做就表明會花費多少時間單位。算然對於不一樣的機器環境而言,確切的單位時間是不一樣的,可是對於算法進行多少個基本操做(即花費多少時間單位)在規模數量級上倒是相同的,由此能夠忽略機器環境的影響而客觀的反應算法的時間效率。操作系統
對於算法的時間效率,咱們能夠用「大O記法」來表示。
「大O記法」:對於單調的整數函數f,若是存在一個整數函數g和實常數c>0,使得對於充分大的n總有f(n)<=c*g(n),就說函數g是f的一個漸近函數(忽略常數),記爲f(n)=O(g(n))。也就是說,在趨向無窮的極限意義下,函數f的增加速度受到函數g的約束,亦即函數f與函數g的特徵類似。
時間複雜度:假設存在函數g,使得算法A處理規模爲n的問題示例所用時間爲T(n)=O(g(n)),則稱O(g(n))爲算法A的漸近時間複雜度,簡稱時間複雜度,記爲T(n)
對於算法進行特別具體的細緻分析雖然很好,但在實踐中的實際價值有限。對於算法的時間性質和空間性質,最重要的是其數量級和趨勢,這些是分析算法效率的主要部分。而計量算法基本操做數量的規模函數中那些常量因子能夠忽略不計。例如,能夠認爲3n2和100n2屬於同一個量級,若是兩個算法處理一樣規模實例的代價分別爲這兩個函數,就認爲它們的效率「差很少」,都爲n2級。
分析算法時,存在幾種可能的考慮:
對於最優時間複雜度,其價值不大,由於它沒有提供什麼有用信息,其反映的只是最樂觀最理想的狀況,沒有參考價值。
對於最壞時間複雜度,提供了一種保證,代表算法在此種程度的基本操做中必定能完成工做。
對於平均時間複雜度,是對算法的一個全面評價,所以它完整全面的反映了這個算法的性質。但另外一方面,這種衡量並無保證,不是每一個計算都能在這個基本操做內完成。並且,對於平均狀況的計算,也會由於應用算法的實例分佈可能並不均勻而難以計算。
因而可知,咱們嘗試的第二種算法要比第一種算法的時間複雜度好多的。
所消耗的時間從小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)
timeit模塊能夠用來測試一小段Python代碼的執行速度。
timeit.Timer(stmt='pass', setup='pass', timer=<timer function>) Timer是測量小段代碼執行速度的類。 stmt參數是要測試的代碼語句(statment); setup參數是運行代碼時須要的設置; timer參數是一個定時器函數,與平臺有關。
timeit.Timer.timeit(number=1000000) Timer類中測試語句執行速度的對象方法。 number參數是測試代碼時的測試次數,默認爲1000000次。 方法返回執行代碼的平均耗時,一個float類型的秒數。
list的操做測試 def test1(): ls= [] for i in range(1000): ls = ls + [i] def test2(): ls = [] for i in range(1000): ls.append(i) def test3(): ls = [] for i in range(1000): ls.insert(0,i) def test4(): ls = [i for i in range(1000)] def test5(): ls = list(range(1000)) from timeit import Timer t1 = Timer(stmt='test1()',setup='from __main__ import test1') print('concat',t1.timeit(number=10000),'second') t2 = Timer(stmt='test2()',setup='from __main__ import test2') print('append',t2.timeit(number=10000),'second') t3 = Timer(stmt='test3()',setup='from __main__ import test3') print('insert',t3.timeit(number=10000),'second') t4 = Timer(stmt='test4()',setup='from __main__ import test4') print('list gen',t4.timeit(number=10000),'second') t5 = Timer(stmt='test5()',setup='from __main__ import test5') print('list',t5.timeit(number=10000),'second') 運行結果 concat 18.341660484899588 second append 1.0099177848925684 second insert 5.361922586350083 second list gen 0.45405794180203074 second list 0.18035762155434298 second
pop操做測試 from timeit import Timer x1 = [x for x in range(2000000)] t = Timer('x1.pop(0)','from __main__ import x1') print('pop(0)',t.timeit(number=1000),'second') x2 = [x for x in range(2000000)] t = Timer('x2.pop()','from __main__ import x2') print('pop()',t.timeit(number=1000),'second') 運行結果 pop(0) 2.033923430381945 second pop() 0.00011084076000633658 second
測試pop操做:從結果能夠看出,pop最後一個元素的效率遠遠高於pop第一個元素
數據是一個抽象的概念,將其進行分類後獲得程序設計語言中的基本類型。如:int,float,char等。數據元素之間不是獨立的,存在特定的關係,這些關係即是結構。數據結構指數據對象中數據元素之間的關係。
Python給咱們提供了不少現成的數據結構類型,這些系統本身定義好的,不須要咱們本身去定義的數據結構叫作Python的內置數據結構,好比列表、元組、字典。而有些數據組織方式,Python系統裏面沒有直接定義,須要咱們本身去定義實現這些數據的組織方式,這些數據組織方式稱之爲Python的擴展數據結構,好比棧,隊列等。
抽象數據類型(ADT)的含義是指一個數學模型以及定義在此數學模型上的一組操做。即把數據類型和數據類型上的運算捆在一塊兒,進行封裝。
引入抽象數據類型的目的是把數據類型的表示和數據類型上運算的實現與這些數據類型和運算在程序中的引用隔開,使它們相互獨立。
最經常使用的數據運算有五種:
在程序中,常常須要將一組(一般是同爲某個類型的)數據元素做爲總體管理和使用,須要建立這種元素組,用變量記錄它們,傳進傳出函數等。一組數據中包含的元素個數可能發生變化(能夠增長或刪除元素)。
對於這種需求,最簡單的解決方案即是將這樣一組元素當作一個序列,用元素在序列裏的位置和順序,表示實際應用中的某種有意義的信息,或者表示數據之間的某種關係。
這樣的一組序列元素的組織形式,咱們能夠將其抽象爲線性表。一個線性表是某類元素的一個集合,還記錄着元素之間的一種順序關係。線性表是最基本的數據結構之一,在實際程序中應用很是普遍,它還常常被用做更復雜的數據結構的實現基礎。
根據線性表的實際存儲方式,分爲兩種實現模型:
一個順序表的完整信息包括兩部分,一部分是表中的元素集合,另外一部分是爲實現正確操做而需記錄的信息,即有關表的總體狀況的信息,這部分信息主要包括元素存儲區的容量和當前表中已有的元素個數兩項。
元素存儲區替換:
元素存儲區擴充
採用分離式結構的順序表,若將數據區更換爲存儲空間更大的區域,則能夠在不改變表對象的前提下對其數據存儲區進行了擴充,全部使用這個表的地方都沒必要修改。只要程序的運行環境(計算機系統)還有空閒存儲,這種表結構就不會由於滿了而致使操做沒法進行。人們把採用這種技術實現的順序表稱爲動態順序表,由於其容量能夠在使用中動態變化。
擴充的兩種策略
每次擴充增長固定數目的存儲位置,如每次擴充增長10個元素位置,這種策略可稱爲線性增加。
特色:節省空間,可是擴充操做頻繁,操做次數多。
每次擴充容量加倍,如每次擴充增長一倍存儲空間。
特色:減小了擴充操做的執行次數,但可能會浪費空間資源。以空間換時間,推薦的方式。
增長元素,刪除元素
Python中的list和tuple兩種類型採用了順序表的實現技術,具備前面討論的順序表的全部性質。
tuple是不可變類型,即不變的順序表,所以不支持改變其內部狀態的任何操做,而其餘方面,則與list的性質相似。
鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,可是不像順序表同樣連續存儲數據,而是在每個節點(數據存儲單元)裏存放下一個節點的位置信息(即地址)。
單向鏈表也叫單鏈表,是鏈表中最簡單的一種形式,它的每一個節點包含兩個域,一個信息域(元素域)和一個連接域。這個連接指向鏈表中的下一個節點,而最後一個節點的連接域則指向一個空值。
單鏈表的實現 #定義節點類 class SingleNode(object): def __init__(self,item): #信息域:存儲節點數據的 self.item = item #連接域:連接下一個節點的 self.next = None #定義單向鏈表 class SingleLinkList(object): def __init__(self): #head:頭節點的引用 self._head = None def is_empty(self): #鏈表是否爲空 return self._head == None def length(self): #鏈表長度 count = 0 #cur指向鏈表的首節點 cur = self._head #判斷cur是否爲None while cur != None: #cur不等於None,就表示一個節點存在 #給計數器加1 count += 1 #cur指向當前節點的下一個節點 cur = cur.next return count def travel(self): # 遍歷整個鏈表 # cur指向鏈表的首節點 cur = self._head # 判斷cur是否爲None while cur != None: #打印當前節點的數據 print(cur.item) #cur指向下一個節點 cur = cur.next def add(self,item): #鏈表頭部添加元素 #生成新的節點對象 node = SingleNode(item) #設置node節點的next指向原來的頭節點 node.next = self._head #把node節點設置成了新的頭節點 self._head = node def append(self,item): #鏈表尾部添加元素 #定義新的節點 node = SingleNode(item) if self.is_empty(): self._head = node else: #cur指向鏈表的開頭 cur = self._head while cur.next != None: cur = cur.next #cur是鏈表的最後一個節點 cur.next = node def insert(self,pos, item): #指定位置添加元素 if pos<=0: #在鏈表的頭部添加節點 self.add(item) elif pos>=self.length(): #在鏈表的尾部添加節點 self.append(item) else: node = SingleNode(item) #計數器,用來肯定插入位置 count = 0 cur = self._head while count < pos - 1: count+=1 cur = cur.next; node.next = cur.next cur.next = node def remove(self,item): #刪除節點 cur = self._head #pre:保存cur的上一個節點 pre = None while cur != None: if cur.item == item: #肯定要刪除的節點 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 while cur != None: #斷定是否是要查找的節點 if cur.item == item: return True else: cur = cur.next return False if __name__ == '__main__': sl = SingleLinkList() sl.add(1) sl.add(2) sl.append(3) sl.insert(2,6) print('length:',sl.length()) sl.travel() print(sl.search(2)) sl.remove(2) print('length:', sl.length())
鏈表與順序表的對比
鏈表失去了順序表隨機讀取的優勢,同時鏈表因爲增長告終點的指針域,空間開銷比較大,但對存儲空間的使用要相對靈活。
單鏈表的一個變形是單向循環鏈表,鏈表中最後一個節點的next域再也不爲None,而是指向鏈表的頭節點。
單向循環列表的實現 class Node(object): """節點""" def __init__(self, item): self.item = item self.next = None class SinCycLinkedlist(object): """單向循環鏈表""" def __init__(self): self._head = None def is_empty(self): """判斷鏈表是否爲空""" return self._head == None def length(self): """返回鏈表的長度""" # 若是鏈表爲空,返回長度0 if self.is_empty(): return 0 count = 1 cur = self._head while cur.next != self._head: count += 1 cur = cur.next return count def travel(self): """遍歷鏈表""" if self.is_empty(): return cur = self._head print(cur.item) while cur.next != self._head: cur = cur.next print(cur.item) def add(self, item): """頭部添加節點""" node = Node(item) if self.is_empty(): self._head = node node.next = self._head else: #添加的節點指向_head node.next = self._head # 移到鏈表尾部,將尾部節點的next指向node cur = self._head while cur.next != self._head: cur = cur.next cur.next = node #_head指向添加node的 self._head = node def append(self, item): """尾部添加節點""" node = Node(item) if self.is_empty(): self._head = node node.next = self._head else: # 移到鏈表尾部 cur = self._head while cur.next != self._head: cur = cur.next # 將尾節點指向node cur.next = node # 將node指向頭節點_head node.next = self._head def insert(self, pos, item): """在指定位置添加節點""" if pos <= 0: self.add(item) elif pos > (self.length()-1): self.append(item) else: node = Node(item) cur = self._head count = 0 # 移動到指定位置的前一個位置 while count < (pos-1): 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.item == 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.item == item: # 刪除 pre.next = cur.next return else: pre = cur cur = cur.next # cur 指向尾節點 if cur.item == item: # 尾部刪除 pre.next = cur.next def search(self, item): """查找節點是否存在""" if self.is_empty(): return False cur = self._head if cur.item == item: return True while cur.next != self._head: cur = cur.next if cur.item == item: return True return False if __name__ == "__main__": ll = SinCycLinkedlist() ll.add(1) ll.add(2) ll.append(3) ll.insert(2, 4) ll.insert(4, 5) ll.insert(0, 6) print("length:",ll.length()) ll.travel() print(ll.search(3)) print(ll.search(7)) ll.remove(1) print("length:",ll.length()) ll.travel()
一種更復雜的鏈表是「雙向鏈表」或「雙面鏈表」。每一個節點有兩個連接:一個指向前一個節點,當此節點爲第一個節點時,指向空值;而另外一個指向下一個節點,當此節點爲最後一個節點時,指向空值。
class Node(object): #節點的類 def __init__(self,item): self.item = item self.prev = None self.next = None class DLinkList(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 while cur != None: print(cur.item) cur = cur.next def add(self,item): #鏈表頭部添加 node = Node(item) if self.is_empty(): #若是是空鏈表,將_head指向node #給鏈表添加第一個元素 self._head = node else: #若是鏈表不爲空,在新的節點和原來的首節點之間創建雙向連接 node.next = self._head self._head.prev = node #讓_head指向鏈表的新的首節點 self._head = node def append(self,item): #鏈表尾部添加 #建立新的節點 node = Node(item) if self.is_empty(): #空鏈表, self._head = node else: #鏈表不爲空 cur = self._head while cur.next != None: cur = cur.next #cur的下一個節點是node cur.next = node #node的上一個節點是 node.prev = cur def insert(self,pos,item): #指定位置添加 if pos <=0: self.add(item) elif pos > self.length()-1: self.append() else: node = Node(item) cur = self._head count = 0 #把cur移動到指定位置的前一個位置 while count < (pos - 1): count+=1 cur = cur.next #node的prev指向cur node.prev = cur #node的next指向cur的next node.next = cur.next cur.next.prev = node cur.next = node def remove(self,item): #刪除節點 if self.is_empty(): return else: cur = self._head if cur.item == item: #首節點是要刪除的節點 if cur.next == None: #說明鏈表中只有一個節點 self._head = None else: #鏈表多於一個節點的狀況 cur.next.prev = None self._head = cur.next else: # 首節點不是要刪除的節點 while cur != None: if cur.item == item: cur.prev.next = cur.next cur.next.prev = cur.prev break cur = cur.next def search(self,item): #查找節點是否存在 cur = self._head while cur != None: if cur.item == item: return True cur = cur.next return False if __name__ == '__main__': dls = DLinkList() dls.add(10) dls.add(12) dls.append(15) dls.append(16) dls.insert(2,32) dls.insert(3,36) print('dls lenght:',dls.length()) dls.travel() print(dls.search(15)) dls.remove(32) print('dls length:',dls.length()) dls.travel()
棧(stack),有些地方稱爲堆棧,是一種容器,可存入數據元素、訪問元素、刪除元素,它的特色在於只能容許在容器的一端(稱爲棧頂端指標,英語:top)進行加入數據(英語:push)和輸出數據(英語:pop)的運算。沒有了位置概念,保證任什麼時候候能夠訪問、刪除的元素都是此前最後存入的那個元素,肯定了一種默認的訪問順序。
因爲棧數據結構只容許在一端進行操做,於是按照後進先出(LIFO, Last In First Out)的原理運做。
棧能夠用順序表實現,也能夠用鏈表實現。
class Stack(object): #棧的實現 def __init__(self): self.items = [] def is_empty(self): #判斷棧是否爲空 return self.items == [] def push(self,item): #壓棧:在棧中加入數據元素 self.items.append(item) def pop(self): #彈棧操做:從棧中彈出元素 return self.items.pop() def peek(self): #返回棧頂的數據元素 return self.items[len(self.items)-1] def size(self): #返回棧的尺寸 return len(self.items) if __name__ == '__main__': stack = Stack() print(stack.is_empty()) stack.push('hello') stack.push('python') stack.push('qiku') stack.push('zhengzhou') print(stack.is_empty()) print(stack.size()) print(stack.peek()) print(stack.pop()) print(stack.pop()) print(stack.pop()) print(stack.pop())
隊列(queue)是隻容許在一端進行插入操做,而在另外一端進行刪除操做的線性表。
隊列是一種先進先出的(First In First Out)的線性表,簡稱FIFO。
容許插入的一端爲隊尾,容許刪除的一端爲隊頭。隊列不容許在中間部位進行操做!假設隊列是q=(a1,a2,……,an),那麼a1就是隊頭元素,而an是隊尾元素。這樣咱們就能夠刪除時,老是從a1開始,而插入時,老是在隊列最後。這也比較符合咱們一般生活中的習慣,排在第一個的優先出列,最後來的固然排在隊伍最後。
同棧同樣,隊列也能夠用順序表或者鏈表實現。
class Queue(object): #隊列的實現 def __init__(self): self.items = [] def is_empty(self): #判斷隊列是否爲空 return self.items == [] def enqueue(self,item): #插入到隊列的頭部 self.items.insert(0,item) def dequeue(self): #刪除數據 return self.items.pop() def size(self): #返回隊列的大小 return len(self.items) if __name__ == '__main__': q = Queue() q.enqueue("hello") q.enqueue("sssss") q.enqueue("aaaaa") print(q.size()) print(q.is_empty()) print(q.dequeue()) print(q.size()) print(q.dequeue()) print(q.size())
雙端隊列(deque,全名double-ended queue),是一種具備隊列和棧的性質的數據結構。
雙端隊列中的元素能夠從兩端彈出,其限定插入和刪除操做在表的兩端進行。雙端隊列能夠在隊列任意一端入隊和出隊。
#雙端列表的定義 class Deque(object): def __init__(self): 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() def is_empty(self): #判斷雙端隊列是否爲空 return self.items == [] def size(self): # 返回隊列的大小 return len(self.items) if __name__ == '__main__': deqeue = Deque() print(deqeue.is_empty()) deqeue.add_front(22) deqeue.add_front(33) deqeue.add_rear(44) deqeue.add_rear(55) print(deqeue.is_empty()) print(deqeue.size()) print(deqeue.remove_front()) print('size:',deqeue.size()) print(deqeue.remove_rear()) print('size:',deqeue.size())
排序算法(英語:Sorting algorithm)是一種能將一串數據依照特定順序進行排列的一種算法。
穩定性:穩定排序算法會讓本來有相等鍵值的紀錄維持相對次序。也就是若是一個排序算法是穩定的,當有兩個相等鍵值的紀錄R和S,且在本來的列表中R出如今S以前,在排序過的列表中R也將會是在S以前。
當相等的元素是沒法分辨的,好比像是整數,穩定性並非一個問題。然而,假設如下的數對將要以他們的第一個數字來排序。
(4, 1) (3, 1) (3, 7)(5, 6)
在這個情況下,有可能產生兩種不一樣的結果,一個是讓相等鍵值的紀錄維持相對的次序,而另一個則沒有:
(3, 1) (3, 7) (4, 1) (5, 6) (維持次序)
(3, 7) (3, 1) (4, 1) (5, 6) (次序被改變)
不穩定排序算法可能會在相等的鍵值中改變紀錄的相對次序,可是穩定排序算法歷來不會如此。不穩定排序算法能夠被特別地實現爲穩定。做這件事情的一個方式是人工擴充鍵值的比較,如此在其餘方面相同鍵值的兩個對象間之比較,(好比上面的比較中加入第二個標準:第二個鍵值的大小)就會被決定使用在原先數據次序中的條目,看成一個同分決賽。然而,要記住這種次序一般牽涉到額外的空間負擔。
冒泡排序(英語:Bubble Sort)是一種簡單的排序算法。它重複地遍歷要排序的數列,一次比較兩個元素,若是他們的順序錯誤就把他們交換過來。遍歷數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。這個算法的名字由來是由於越小的元素會經由交換慢慢「浮」到數列的頂端。
冒泡排序算法的運做以下:
def bubble_sort(alist): for j in range(len(alist)-1,0,-1): # j表示每次遍歷須要比較的次數,是逐漸減少的 for i in range(j): if alist[i] > alist[i+1]: alist[i], alist[i+1] = alist[i+1], alist[i] li = [54,26,93,17,77,31,44,55,20] bubble_sort(li) print(li)
時間複雜度
選擇排序(Selection sort)是一種簡單直觀的排序算法。
它的工做原理以下:
選擇排序的主要優勢與數據移動有關。若是某個元素位於正確的最終位置上,則它不會被移動。選擇排序每次交換一對元素,它們當中至少有一個將被移到其最終位置上,所以對n個元素的表進行排序總共進行至多n-1次交換。在全部的徹底依靠交換去移動元素的排序方法中,選擇排序屬於很是好的一種。
def selection_sort(alist): n = len(alist) # 須要進行n-1次選擇操做 for i in range(n-1): # 記錄最小位置 min_index = i # 從i+1位置到末尾選擇出最小數據 for j in range(i+1, n): if alist[j] < alist[min_index]: min_index = j # 若是選擇出的數據不在正確位置,進行交換 if min_index != i: alist[i], alist[min_index] = alist[min_index], alist[i] alist = [54,226,93,17,77,31,44,55,20] selection_sort(alist) print(alist)
時間複雜度
插入排序(英語:Insertion Sort)是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,在從後向前掃描過程當中,須要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。
def insert_sort(alist): # 從第二個位置,即下標爲1的元素開始向前插入 for i in range(1, len(alist)): # 從第i個元素開始向前比較,若是小於前一個元素,交換位置 for j in range(i, 0, -1): if alist[j] < alist[j-1]: alist[j], alist[j-1] = alist[j-1], alist[j] alist = [54,26,93,17,77,31,44,55,20] insert_sort(alist) print(alist)
時間複雜度