給定一組隨機數字的數列,將它們按照從小到大的順序從新排列好。就像冒泡同樣,小的數不斷地向上漂浮,直到沒有爲止。html
排序基本思路:循環這個數列,將循環到的數字n1與下一個數字n2做出對比,若是n2>n1,那麼將兩個值換位,如此下去當第一次循環結束時,最小的數已經在最前面,重複這樣的循環就會將這組數按從小到大的順序排列好。node
li = [11,432,5,4576,234,3,324,5,876,456,235432,56]
for m in range(len(li)-1):
for n in range(m+1, len(li)):
if li[m] > li[n]:
tmp = li[m]
li[m] = li[n]
li[n] = tmp
print li
測試排列5萬個小於2000的隨機數字,結果發現用時爲:118秒python
與冒泡排序相似,循環整個數組,將數組中的值與第一個數值作對比,並將較小的值放在第一位。第一次循環就能夠找出最小的一個值,以此類推下去,知道所有排列好。git
#!/usr/bin/env python # -*- coding:utf-8 -*- import random,time def selection_sort(array): for i in range(len(array)): for j in range(i, len(array)): if array[i] > array[j]: tmp = array[i] array[i] = array[j] array[j] = tmp if __name__ == '__main__': # array = [871,100,160,755,614,621,403,671,256,915,174,906] array = [] for i in range(50000): array.append(random.randrange(100000)) time_start = time.time() selection_sort(array) time_end = time.time() ret = time_end-time_start print array print(ret)
測試排列5萬個小於2000的隨機數字,結果發現用時爲:115秒算法
選擇排序的優化數據庫
當咱們在循環中,判斷出後面的值與第一個值的大小,那麼先不作出交換,僅僅記錄下這個較小值的下標。當第一次循環完時才把最小值放置在最前面,這樣交換的次數更少了,速度就更快樂了。數組
#!/usr/bin/env python # -*- coding:utf-8 -*- import random,time def selection_sort(array): for i in range(len(array)): for j in range(i, len(array)): if array[i] > array[j]: tmp = array[i] array[i] = array[j] array[j] = tmp if __name__ == '__main__': # array = [871,100,160,755,614,621,403,671,256,915,174,906] array = [] for i in range(50000): array.append(random.randrange(2000)) time_start = time.time() selection_sort(array) time_end = time.time() ret = time_end-time_start # print array print(ret)
測試排列5萬個小於2000的隨機數字,結果發現用時爲:105秒數據結構
排序基本思路:插入排序簡單的理解爲 從一組數據的第二個值n開始循環,經過n與前面數值對比大小,將n插入到前面合適的位置。經過不斷地向後循環對比大小,那麼前面一部分的數據始終是已經排序好的數組。這樣所有循環下來,就獲得了一個從小到大排列OK的數組。app
[77, 92, 67, 8, 6, 84, 55, 85, 43, 67]
[67, 77, 92, 8, 6, 84, 55, 85, 43, 67]
[8, 67, 77, 92, 6, 84, 55, 85, 43, 67]
[6, 8, 67, 77, 92, 84, 55, 85, 43, 67]
[6, 8, 67, 77, 84, 92, 55, 85, 43, 67]
[6, 8, 55, 67, 77, 84, 92, 85, 43, 67]
[6, 8, 55, 67, 77, 84, 85, 92, 43, 67]
[6, 8, 43, 55, 67, 77, 84, 85, 92, 67]
[6, 8, 43, 55, 67, 67, 77, 84, 85, 92]dom
# _*_coding:utf:8_*_
import random import time def sort_meth(source): for i in range(1, len(source)): currant_val = source[i] # 獲取當前循環列表中的值
position = i # 獲取當前循環的次數
while position > 0 and source[position-1] > currant_val: # 當左邊的數大於大循環中的數時
source[position] = source[position-1] # 將小循環此刻的數賦值等於左邊較大的值
position -= 1 source[position] = currant_val # 最終小循環結束時,就是大循環的數應該插入的位置
if __name__ == '__main__': #array = [871,100,160,755,614,621,403,671,256,915,174,906]
array = [] for i in range(50000): array.append(random.randrange(2000)) time_start = time.time() sort_meth(array) time_end = time.time() ret = time_end-time_start print(ret)
測試排列5萬個小於2000的隨機數字,結果發現用時爲:115秒
設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(一般選用數組的第一個數)做爲關鍵數據,而後將全部比它小的數都放到它前面,全部比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變更
注:在待排序的文件中,若存在多個關鍵字相同的記錄,通過排序後這些具備相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具備相同關鍵字的記錄之間的相對次序發生改變,則稱這種排序方法是不穩定的。
要注意的是,排序算法的穩定性是針對全部輸入實例而言的。即在全部可能的輸入實例中,只要有一個實例使得算法不知足穩定性要求,則該排序算法就是不穩定的。
排序演示
下標
|
0
|
1
|
2
|
3
|
4
|
5
|
數據
|
6
|
2
|
7
|
3
|
8
|
9
|
下標
|
0
|
1
|
2
|
3 |
4
|
5
|
數據
|
3
|
2
|
7
|
6
|
8
|
9
|
下標
|
0
|
1
|
2
|
3
|
4
|
5
|
數據
|
3
|
2
|
6
|
7
|
8
|
9
|
下標
|
0
|
1
|
2
|
3
|
4
|
5
|
數據
|
3
|
2
|
6
|
7
|
8
|
9
|
# _*_coding:utf-8_*_ import random import time def quick_sort(array, start, end): if start >= end: return left = start right = end value = array[left] while left < right: while left < right and array[right] > value: right -= 1 array[left] = array[right] array[right] = value while left < right and array[left] <= value: left += 1 array[right] = array[left] array[left] = value quick_sort(array, start, left-1) quick_sort(array, left+1, end) if __name__ == '__main__': # array = [871,100,160,755,614,621,403,671,256,915,174,906] array = [] for i in range(50000): array.append(random.randrange(2000)) time_start = time.time() quick_sort(array, 0, len(array)-1) time_end = time.time() ret = time_end-time_start print(ret)
測試排列5萬個小於2000的隨機數字,結果發現用時爲:0.233秒,
當看到這個結果,其實我是驚呆了!
樹的特徵和定義
樹(Tree)是元素的集合。咱們先以比較直觀的方式介紹樹。下面的數據結構是一個樹:
樹有多個節點(node),用以儲存元素。某些節點之間存在必定的關係,用連線表示,連線稱爲邊(edge)。邊的上端節點稱爲父節點,下端稱爲子節點。樹像是一個不斷分叉的樹根。
每一個節點能夠有多個子節點(children),而該節點是相應子節點的父節點(parent)。好比說,3,5是6的子節點,6是3,5的父節點;1,8,7是3的子節點, 3是1,8,7的父節點。樹有一個沒有父節點的節點,稱爲根節點(root),如圖中的6。沒有子節點的節點稱爲葉節點(leaf),好比圖中的1,8,9,5節點。從圖中還能夠看到,上面的樹總共有4個層次,6位於第一層,9位於第四層。樹中節點的最大層次被稱爲深度。也就是說,該樹的深度(depth)爲4。
若是咱們從節點3開始向下看,而忽略其它部分。那麼咱們看到的是一個以節點3爲根節點的樹:
三角形表明一棵樹
再進一步,若是咱們定義孤立的一個節點也是一棵樹的話,原來的樹就能夠表示爲根節點和子樹(subtree)的關係:
上述觀察實際上給了咱們一種嚴格的定義樹的方法:
1. 樹是元素的集合。
2. 該集合能夠爲空。這時樹中沒有元素,咱們稱樹爲空樹 (empty tree)。
3. 若是該集合不爲空,那麼該集合有一個根節點,以及0個或者多個子樹。根節點與它的子樹的根節點用一個邊(edge)相連。
上面的第三點是以遞歸的方式來定義樹,也就是在定義樹的過程當中使用了樹自身(子樹)。因爲樹的遞歸特徵,許多樹相關的操做也能夠方便的使用遞歸實現。咱們將在後面看到。
樹的實現
樹的示意圖已經給出了樹的一種內存實現方式: 每一個節點儲存元素和多個指向子節點的指針。然而,子節點數目是不肯定的。一個父節點可能有大量的子節點,而另外一個父節點可能只有一個子節點,而樹的增刪節點操做會讓子節點的數目發生進一步的變化。這種不肯定性就可能帶來大量的內存相關操做,而且容易形成內存的浪費。
一種經典的實現方式以下:
樹的內存實現
擁有同一父節點的兩個節點互爲兄弟節點(sibling)。上圖的實現方式中,每一個節點包含有一個指針指向第一個子節點,並有另外一個指針指向它的下一個兄弟節點。這樣,咱們就能夠用統一的、肯定的結構來表示每一個節點。
計算機的文件系統是樹的結構,好比Linux文件管理背景知識中所介紹的。在UNIX的文件系統中,每一個文件(文件夾一樣是一種文件),均可以看作是一個節點。非文件夾的文件被儲存在葉節點。文件夾中有指向父節點和子節點的指針(在UNIX中,文件夾還包含一個指向自身的指針,這與咱們上面見到的樹有所區別)。在git中,也有相似的樹狀結構,用以表達整個文件系統的版本變化 (參考版本管理三國志)。
二叉樹是由n(n≥0)個結點組成的有限集合、每一個結點最多有兩個子樹的有序樹。它或者是空集,或者是由一個根和稱爲左、右子樹的兩個不相交的二叉樹組成。
特色:
(1)二叉樹是有序樹,即便只有一個子樹,也必須區分左、右子樹;
(2)二叉樹的每一個結點的度不能大於2,只能取0、一、2三者之一;
(3)二叉樹中全部結點的形態有5種:空結點、無左右子樹的結點、只有左子樹的結點、只有右子樹的結點和具備左右子樹的結點。
二叉樹(binary)是一種特殊的樹。二叉樹的每一個節點最多隻能有2個子節點:
二叉樹
因爲二叉樹的子節點數目肯定,因此能夠直接採用上圖方式在內存中實現。每一個節點有一個左子節點(left children)和右子節點(right children)。左子節點是左子樹的根節點,右子節點是右子樹的根節點。
若是咱們給二叉樹加一個額外的條件,就能夠獲得一種被稱做二叉搜索樹(binary search tree)的特殊二叉樹。二叉搜索樹要求:每一個節點都不比它左子樹的任意元素小,並且不比它的右子樹的任意元素大。
(若是咱們假設樹中沒有重複的元素,那麼上述要求能夠寫成:每一個節點比它左子樹的任意節點大,並且比它右子樹的任意節點小)
二叉搜索樹,注意樹中元素的大小
二叉搜索樹能夠方便的實現搜索算法。在搜索元素x的時候,咱們能夠將x和根節點比較:
1. 若是x等於根節點,那麼找到x,中止搜索 (終止條件)
2. 若是x小於根節點,那麼搜索左子樹
3. 若是x大於根節點,那麼搜索右子樹
二叉搜索樹所須要進行的操做次數最多與樹的深度相等。n個節點的二叉搜索樹的深度最多爲n,最少爲log(n)。
二叉樹的遍歷
遍歷即將樹的全部結點訪問且僅訪問一次。按照根節點位置的不一樣分爲前序遍歷,中序遍歷,後序遍歷。
前序遍歷:根節點->左子樹->右子樹
中序遍歷:左子樹->根節點->右子樹
後序遍歷:左子樹->右子樹->根節點
例如:求下面樹的三種遍歷
前序遍歷:abdefgc
中序遍歷:debgfac
後序遍歷:edgfbca
二叉樹的類型
如何判斷一棵樹是徹底二叉樹?按照定義,
教材上的說法:一個深度爲k,節點個數爲 2^k - 1 的二叉樹爲滿二叉樹。這個概念很好理解,
就是一棵樹,深度爲k,而且沒有空位。
首先對滿二叉樹按照廣度優先遍歷(從左到右)的順序進行編號。
一顆深度爲k二叉樹,有n個節點,而後,也對這棵樹進行編號,若是全部的編號都和滿二叉樹對應,那麼這棵樹是徹底二叉樹。
二叉樹遍歷實現
class TreeNode(object): def __init__(self,data=0,left=0,right=0): self.data = data self.left = left self.right = right class BTree(object): def __init__(self,root=0): self.root = root def preOrder(self,treenode): if treenode is 0: return print(treenode.data) self.preOrder(treenode.left) self.preOrder(treenode.right) def inOrder(self,treenode): if treenode is 0: return self.inOrder(treenode.left) print(treenode.data) self.inOrder(treenode.right) def postOrder(self,treenode): if treenode is 0: return self.postOrder(treenode.left) self.postOrder(treenode.right) print(treenode.data) if __name__ == '__main__': n1 = TreeNode(data=1) n2 = TreeNode(2,n1,0) n3 = TreeNode(3) n4 = TreeNode(4) n5 = TreeNode(5,n3,n4) n6 = TreeNode(6,n2,n5) n7 = TreeNode(7,n6,0) n8 = TreeNode(8) root = TreeNode('root',n7,n8) bt = BTree(root) print("preOrder".center(50,'-')) print(bt.preOrder(bt.root)) print("inOrder".center(50,'-')) print (bt.inOrder(bt.root)) print("postOrder".center(50,'-')) print (bt.postOrder(bt.root))