時間複雜度是用來估算算法運行速度的一種方式,一般採用大O表示法。
須要注意如下幾點:python
遞歸比較容易理解,有如下兩個特徵:git
#遞歸實現斐波那契數列 def fibnacci(n): if n=0 or n=1: return 1 else: return fibnacci(n-1)+fibnacci(n-2) #這就是遞歸的精髓,把複雜重複的運算抽絲剝繭,每遞歸一次就簡化一次 #斐波那契數列能夠用更簡單的方法實現 def fibnacci(n): a=b=c=1 for i in range(2,n+1): c=a+b a=b b=c return c #遞歸實現漢諾塔 def hanoi(n, A, B, C): if n > 0: hanoi(n-1, A, C, B) print('%s->%s' % (A, C)) hanoi(n-1, B, A, C)
簡單查找就是按順序查找,直到查到指定元素,時間複雜度爲O(n)。算法
二分查找是對簡單查找的一種優化,可是操做的只能是有序數組,就是經過中間值和需求數比較,經過比較結果來改變左右範圍。
須要注意的是,不要經過切片改變列表,那樣會加大空間複雜度。
尾遞歸的定義:尾遞歸就是全部遞歸語句都是在函數的最後出現的,正常是至關於循環的複雜度,可是python內沒有優化。shell
def bin_search(li, val): low = 0 high = len(li)-1 while low <= high: # 只要候選區不空,繼續循環 mid = (low + high) // 2 if li[mid] == val: return mid elif li[mid] < val: low = mid + 1 else: # li[mid] > val high = mid - 1 return -1
#遞歸實現二分法 lst = [1, 2, 33, 44, 555] def func(left,right,n): if left <= right: mid = (right + left) // 2 if n > lst[mid]: left = mid+1 if n < lst[mid]: right = mid - 1 if n == lst[mid]: return( "找到了") else: return("沒找到") return func(left,right,n) ret = func(0,len(lst)-1,44) print(ret)
#區別:本方法利用切片,改變原序列表,增長了空間複雜度 lst = [1, 2, 33, 44, 555] count = 0 def func(lis,n): left = 0 right = len(lis)-1 global count count = count + 1 print(count) if left <= right: mid = (right + left) // 2 if n > lst[mid]: return func(lis[mid+1:],n) elif n < lst[mid]: return func(lis[:mid], n) else: return( "找到了") else: return("沒找到") ret = func(lst,44)
主要思想爲:新建列表做爲索引,若是一個數的索引存在,說明這個數也存在.api
lis = [2,4,6,7] #就是計數排序的反向使用,計數排序會在線性排序模塊裏說 n = 3 #查找n lst = [0,0,0,0,0,0,0,0] #建立一個元素均爲0的列表,元素個數爲lis中最大的數字加1 li = [0,0,1,0,1,0,1,1] #把 lis 中對應的數字值變爲1 if li[3] == 1: print("存在") else: print("不存在")
排序算法是有穩定和不穩定之分。
穩定的排序就是保證關鍵字相同的元素,排序以後相對位置不變,所謂關鍵字就是排序的憑藉,對於數字來講就是大小。
排序算法的關鍵點是分爲有序區和無序區兩個區域。數組
冒泡排序思路:數據結構
#基礎冒泡 def bubble_sort(li): for i in range(len(li)-1): #第一層循環表明處於第幾回冒泡 for j in range(len(li)-i-1): #第二層循環表明無序區的範圍 if li[j]>li[j+1]: li[j],li[j+1]=li[k+1],li[j]
若是考慮冒泡的最好狀況,也就是冒泡沒有進行到n次的時候就已經不出現j>j+1了,那麼排序已經進行完畢。app
def bubble_sort(li): for i in range(len(li)-1): #第一層循環表明處於第幾回冒泡 a= 1 for j in range(len(li)-i-1): #第二層循環表明無序區的範圍 if li[j]>li[j+1]: li[j],li[j+1]=li[k+1],li[j] a=2 if a=1: break
選擇排序的思路:函數
def select_sort(li): for i in range(len(li)-1): min_pos=i #第幾回,無序區的第一個位置的索引就爲幾減一 for j in range(i+1,len(li)): if li[j]<li[min_pos]: #min_pos會隨着循環變換值 min_pos=j li[i],li[min_pos]=li[min_pos],li[i]
插入排序的思路:優化
def insert_sort(li): for i in range(1,len(li)): #i表示須要進行插入的元素的位置 j=i-1 #j的初始位置,也就是無序區第一個元素的位置 while j!=-1 and li[j]>li[i]:#只要可以與無序區元素進行比較,循環就不中止 #跳出循環的狀況只有是有序區進行比較的元素沒了,可是跳出循環時與li[j]<=li[i]時執行的語句是同樣的,都是li[j+1]=li[i],因此進行一個合併,減小代碼量 li[j+1]=li[j] #進行比較的有序區索引加一 j-=1 #進行比較元素的索引減一 li[j+1]=li[i] #也就是成爲0號元素
快排思路:
def _quick_sort(li,left,right): if left<right: #待排序區域至少有兩個元素,left和right指的是索引 mid = partition(li,left,right) _quick_sort(li,left,mid-1) _quick_sort(li,mid+1,right) def quick_sort(li): #包裝一下,由於循環不能直接遞歸,會很是慢 _quick_sort(li,0,len[li]-1) def partition(li,left,right): #此函數的意義是歸位 tmp=li[left] #left爲第一個元素的索引,也就是須要進行歸位的元素的索引 while left<right: #注意,小的在左邊,大的在右邊 while li[right]>tmp and left<right: #當right的值小於tmp是退出 right-=1 #進行下一個right li[left]=li[right] #把left位置放比tmp小的right while li[left]<=tmp and left<right: left+=1 li[right]=li[left] #把right位置放比tmp大的left li[left]=tmp #把tmp放在left=right時剩下的位置 return left
在Haskell中只須要6行
quicksort :: (Ord a) =>[a] ->[a] quicksort [] = [] quicksort (x:xs) = let smallersort = quicksort[a|a<- xs, a <=x] -- 屬於xs,而且小於x let biggersort = quick[a|a<- xs,a>x] in smallersort ++ x ++ biggersort
知識儲備:
二叉樹的存儲方式:
堆排序也就是優先隊列,進行屢次向下調整,得出一個根堆,而後根據索引從後往前挨個輸出節點。
向下調整
def sift(li,low,high): i=low #相對根節點 j=2*i+1 #它的左子節點位置 tmp=li[i] #根節點元素大小 while j<=high: if j<high and li[j]<li[j+1]: #先判斷左右節點大小,j<high是由於可能出現沒有有節點的狀況 j+=1 if tmp <li[j]: #再判斷左節點或有節點與根節點的大小 li[i]=li[j] #把左右節點移動到根節點 i=j #把相對根節點移動到下一層 j=2*i+1 #新的子節點索引 else: break li[i]=tmp #最後把原來的根節點放到索引i上
從堆中取出節點元素
def heap_sort(li): for low in range(len(li)//2-1,-1,-1): #構造堆,low的取值是倒序,從後面到0 sift(li,low,len(li)-1) #high被假設是固定的,由於它爲最小對結果不會影響。 for high in range(len(li)-1,-1,-1): #取出時high一直是動態的,讓取出的low不參加以後的調整,也就是構建新堆的過程 li[0],li[high]=li[high],li[0] #把得出的無序區最後一個值,放到根結點處進行構建新堆 sift(li,0,high-1) #
python的heapq內置模塊
歸併排序思路:
def merge(li, low, mid, high): #mid爲兩段有序分界線左邊第一個數的索引 # 列表兩段有序: [low, mid] [mid+1, high] i = low #i指向左半邊列表進行比較的元素 j = mid + 1 #j指向右半邊列表進行比較的元素 li_tmp = [] #比較出較小的元素暫存的位置 while i <= mid and j <= high: #當左右兩側比較的元素都不爲空時 if li[i] <= li[j]: #左邊小,左邊元素拿到暫存li_tmp中,左邊指針向右移動 li_tmp.append(li[i]) i += 1 else: #右邊小,右邊元素拿到li_tmp中,右邊元素的指針向左移動 li_tmp.append(li[j]) j += 1 while i <= mid: #將剩餘的元素都拿到li_tmp中 li_tmp.append(li[i]) i += 1 while j <= high: li_tmp.append(li[j]) j += 1 for i in range(low, high+1): #把li_tmp中的元素放到li中 li[i] = li_tmp[i-low] # 也能夠這樣移動: li[low:high+1] = li_tmp def _merge_sort(li, low, high): #排序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) #合併
希爾排序思路:
def shell_sort(li): gap = len(li) // 2 while gap > 0: #接下來進行的其實就是插入排序的代碼,只不過無序區起始是從gap也就是從gap開始。 for i in range(gap, len(li)): tmp = li[i] j = i - gap while j >= 0 and tmp < li[j]: li[j + gap] = li[j] j -= gap li[j + gap] = tmp gap /= 2
線性時間排序都有侷限性,並不經常使用。
計數排序思路:
def count_sort(li,max_num): count = [0 for _ in range(max_num+1)] #列表表達式,以前的博客有寫,和Haskell中同樣_表明咱們徹底再也不在意它的值 for i in li: count[i]+=1 #出現一次,值加一 li = [] for i,v in enumerate(count): #i是元素,v是出現的次數 for _ in range(v): li.append(i)
桶排序思路:
要說基數排序,先說多關鍵字排序。
多關鍵字排序思路:
預備知識:
def get_dight(num,i): #i爲1時,取個位數,i爲2時取十位數 return num//(10 * i)%10
還能夠轉成列表後根據索引得出,思路和上面是同樣的,就當科普了。
#int轉list def int2list(num): li=[] while num>0: li.append(num%10) num //=10 li.reverse_list() #再科普一個列表反轉吧 return li列表反轉思路:
def reverse_list(li): n=len(li) for i in range(n//2): li[i],li[n-i-1] = li[n-i-1],li[i] return li
def reverse_int(num): is_neg = False if num < 0: is_neg = True num = -1 * num res = 0 while num >0: res = res * 10 res = res + num%10 num = num // 10 if is_neg: res = res * -1 return res
。。
直接上基數排序:
def max_nu(li): for i in range(len(li)-1): max_pos=i for j in range(i+1,len(li)-1): if li[i] < li[j]: max_pos=j def radix_sort(li): max_num=max_nu(li) i = 0 #從個位數開始, while (10 ** i <= max_num): #這裏注意是能夠 = 的,要考慮 10 100 的狀況 buckets = [[] for i in range(0,10)] for val in li: digit = li // (10 ** i)%10 buckets[digit].append[val] #以餘數做爲索引,放入桶內,第一次循環是個位數,第二次循環是十位數 li = [] for bucket in buckets: for val in bucket: #在桶中依次取出數據 li.append(val) #進行一次排序後放回li中 i+=1