這篇文章主要為大家詳細介紹了python演算法的基礎教程,具有一定的參考價值,有興趣的小夥伴們可以參考一下
前言:前兩天騰訊筆試受到1萬點暴擊,感覺浪費我兩天時間去牛客網做題…這篇部落格介紹幾種簡單/常見的排序演算法,算是整理下。
時間複雜度
(1)時間頻度一個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只要知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。而一個演算法花費的時間與演算法中語句的執行次數成正比例,哪個演算法中語句執行次數多,它花費時間就多。 一個演算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。
(2)時間複雜度在剛才提到的時間頻度中,n稱為問題的規模,當n不斷變化時,時間頻度T(n)也會不斷變化。但有時我們想知道它改變時呈現什麼規律。為此,我們引入時間複雜度概念。 一般情況下,演算法中基本運算重複執行的次數是問題規模n的某個函數,用T(n)表示,若有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)為T(n)的同數量級函數。記作T(n)=O(f(n)),稱O(f(n))為演算法的漸進時間複雜度,簡稱時間複雜度。
指數時間
指的是一個問題解所需要的計算時間m(n),依輸入資料的大小而呈指數成長(即輸入資料的數量依線性成長,所花的時間將會以指數成長)
for (i=1; i<=n; i++) x++; for (i=1; i<=n; i++) for (j=1; j<=n; j++) x++;
第一個for循環的時間複雜度為Ο(n),第二個for迴圈的時間複雜度為Ο(n2),則整個演算法的時間複雜度為Ο(n+n2)=Ο(n2)。
常數時間
若對於演算法的上界與輸入大小無關,則稱其具有常數時間,並記作時間。一個例子是存取數組中的單一元素,因為存取它只需要一條指令。但是,找到無序數組中的最小元素則不是,因為這需要遍歷所有元素來找出最小值。這是一項線性時間的操作,或稱為時間。但如果預先知道元素的數量並假設數量保持不變,則該操作也可稱為具有常數時間。
對數時間
若演算法的T(n) =O(logn),則稱其具有對數時間
常見的具有對數時間的演算法有二元樹的相關操作和二分搜尋。
對數時間的演算法是非常有效的,因為每增加一個輸入,其所需的額外計算時間會變小。
遞歸地將字串砍半並且輸出是這個類別函數的一個簡單例子。它需要O(log n)的時間因為每次輸出之前我們都將字串砍半。 這意味著,如果我們想增加輸出的次數,我們需要將字串長度加倍。
線性時間
如果一個演算法的時間複雜度為O(n),則稱這個演算法具有線性時間,或O(n)時間。非正式地說,這意味著對於足夠大的輸入,運行時間增加的大小與輸入成線性關係。例如,一個計算列表所有元素的和的程序,所需的時間與列表的長度成正比。
一、冒泡演算法
基本思想:
在要排序的一組數中,對目前還未排好序的範圍內的全部數,自上而下將相鄰的兩個數依序進行比較和調整,讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就將它們互換。
冒泡排序的範例:
#演算法實作:
def bubble(array): for i in range(len(array)-1): for j in range(len(array)-1-i): if array[j] > array[j+1]: # 如果前一个大于后一个,则交换 temp = array[j] array[j] = array[j+1] array[j+1] = temp if __name__ == "__main__": array = [265, 494, 302, 160, 370, 219, 247, 287, 354, 405, 469, 82, 345, 319, 83, 258, 497, 423, 291, 304] print("------->排序前<-------") print(array) bubble(array) print("------->排序后<-------") print(array)
輸出:
------->排序前[265, 494, 302, 160, 370, 219, 247, 287, 354, 405, 469, 82, 345, 319, 83, 258, 497, 423, 291, 304]
------->排序後[82, 83, 160, 219, 247, 258, 265, 287, 291, 302, 304, 319, 345, 354, 370, 405, 423, 345, 354, 370, 405, 423, 46949.
講解:#
以随机产生的五个数为例: li=[354,405,469,82,345]
冒泡排序是怎么实现的?
首先先来个大循环,每次循环找出最大的数,放在列表的最后面。在上面的例子中,第一次找出最大数469,将469放在最后一个,此时我们知道
列表最后一个肯定是最大的,故还需要再比较前面4个数,找出4个数中最大的数405,放在列表倒数第二个......
5个数进行排序,需要多少次的大循环?? 当然是4次啦!同理,若有n个数,需n-1次大循环。
现在你会问我: 第一次找出最大数469,将469放在最后一个??怎么实现的??
嗯,(在大循环里)用一个小循环进行两数比较,首先354与405比较,若前者较大,需要交换数;反之不用交换。
当469与82比较时,需交换,故列表倒数第二个为469;469与345比较,需交换,此时最大数469位于列表最后一个啦!
难点来了,小循环需要多少次??
进行两数比较,从列表头比较至列表尾,此时需len(array)-1次!! 但是,嗯,举个例子吧: 当大循环i为3时,说明此时列表的最后3个数已经排好序了,不必进行两数比较,故小循环需len(array)-1-3. 即len(array)-1-i
冒泡排序复杂度:
时间复杂度: 最好情况O(n), 最坏情况O(n^2), 平均情况O(n^2)
空间复杂度: O(1)
稳定性: 稳定
简单选择排序的示例:
二、选择排序
The selection sort works as follows: you look through the entire array for the smallest element, once you find it you swap it (the smallest element) with the first element of the array. Then you look for the smallest element in the remaining array (an array without the first element) and swap it with the second element. Then you look for the smallest element in the remaining array (an array without first and second elements) and swap it with the third element, and so on. Here is an example
基本思想:
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
简单选择排序的示例:
算法实现:
def select_sort(array): for i in range(len(array)-1): # 找出最小的数放与array[i]交换 for j in range(i+1, len(array)): if array[i] > array[j]: temp = array[i] array[i] = array[j] array[j] = temp if __name__ == "__main__": array = [265, 494, 302, 160, 370, 219, 247, 287, 354, 405, 469, 82, 345, 319, 83, 258, 497, 423, 291, 304] print(array) select_sort(array) print(array)
选择排序复杂度:
时间复杂度: 最好情况O(n^2), 最坏情况O(n^2), 平均情况O(n^2)
空间复杂度: O(1)
稳定性: 不稳定
举个例子:序列5 8 5 2 9, 我们知道第一趟选择第1个元素5会与2进行交换,那么原序列中两个5的相对先后顺序也就被破坏了。
排序效果:
三、直接插入排序
插入排序(Insertion Sort)的基本思想是:将列表分为2部分,左边为排序好的部分,右边为未排序的部分,循环整个列表,每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。
插入排序非常类似于整扑克牌。
在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。
也许你没有意识到,但其实你的思考过程是这样的:现在抓到一张7,把它和手里的牌从右到左依次比较,7比10小,应该再往左插,7比5大,好,就插这里。为什么比较了10和5就可以确定7的位置?为什么不用再比较左边的4和2呢?因为这里有一个重要的前提:手里的牌已经是排好序的。现在我插了7之后,手里的牌仍然是排好序的,下次再抓到的牌还可以用这个方法插入。编程对一个数组进行插入排序也是同样道理,但和插入扑克牌有一点不同,不可能在两个相邻的存储单元之间再插入一个单元,因此要将插入点之后的数据依次往后移动一个单元。
设监视哨是我大一在书上有看过,大家忽视上图的监视哨。
算法实现:
import time def insertion_sort(array): for i in range(1, len(array)): # 对第i个元素进行插入,i前面是已经排序好的元素 position = i # 要插入数的下标 current_val = array[position] # 把当前值存下来 # 如果前一个数大于要插入数,则将前一个数往后移,比如5,8,12,7;要将7插入,先把7保存下来,比较12与7,将12往后移 while position > 0 and current_val < array[position-1]: array[position] = array[position-1] position -= 1 else: # 当position为0或前一个数比待插入还小时 array[position] = current_val if __name__ == "__main__": array = [92, 77, 67, 8, 6, 84, 55, 85, 43, 67] print(array) time_start = time.time() insertion_sort(array) time_end = time.time() print("time: %s" % (time_end-time_start)) print(array)
输出:
[92, 77, 67, 8, 6, 84, 55, 85, 43, 67]
time: 0.0
[6, 8, 43, 55, 67, 67, 77, 84, 85, 92]
如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
直接插入排序复杂度:
时间复杂度: 最好情况O(n), 最坏情况O(n^2), 平均情况O(n^2)
空间复杂度: O(1)
稳定性: 稳定
个人感觉直接插入排序算法难度是选择/冒泡算法是两倍……
四、快速排序
快速排序示例:
算法实现:
def quick_sort(array, left, right): ''' :param array: :param left: 列表的第一个索引 :param right: 列表最后一个元素的索引 :return: ''' if left >= right: return low = left high = right key = array[low] # 第一个值,即基准元素 while low < high: # 只要左右未遇见 while low < high and array[high] > key: # 找到列表右边比key大的值 为止 high -= 1 # 此时直接 把key跟 比它大的array[high]进行交换 array[low] = array[high] array[high] = key while low < high and array[low] <= key: # 找到key左边比key大的值,这里为何是<=而不是<呢?你要思考。。。 low += 1 # 找到了左边比k大的值 ,把array[high](此时应该刚存成了key) 跟这个比key大的array[low]进行调换 array[high] = array[low] array[low] = key quick_sort(array, left, low-1) # 最后用同样的方式对分出来的左边的小组进行同上的做法 quick_sort(array,low+1, right) # 用同样的方式对分出来的右边的小组进行同上的做法 if __name__ == '__main__': array = [8,4,1, 14, 6, 2, 3, 9,5, 13, 7,1, 8,10, 12] print("-------排序前-------") print(array) quick_sort(array, 0, len(array)-1) print("-------排序后-------") print(array)
输出:
-------排序前-------
[8, 4, 1, 14, 6, 2, 3, 9, 5, 13, 7, 1, 8, 10, 12]
-------排序后-------
[1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 12, 13, 14]
22行那里如果不加=号,当排序64,77,64是会死循环,此时key=64, 最后的64与开始的64交换,开始的64与本最后的64交换…… 无穷无尽
直接插入排序复杂度:
时间复杂度: 最好情况O(nlogn), 最坏情况O(n^2), 平均情况O(nlogn)
下面空间复杂度是看别人博客的,我也不大懂了……改天再研究下。
最优的情况下空间复杂度为:O(logn);每一次都平分数组的情况
最差的情况下空间复杂度为:O( n );退化为冒泡排序的情况
稳定性:不稳定
快速排序效果:
以上是常見python中排序的程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和C 各有優勢,選擇應基於項目需求。 1)Python適合快速開發和數據處理,因其簡潔語法和動態類型。 2)C 適用於高性能和系統編程,因其靜態類型和手動內存管理。

選擇Python還是C 取決於項目需求:1)如果需要快速開發、數據處理和原型設計,選擇Python;2)如果需要高性能、低延遲和接近硬件的控制,選擇C 。

通過每天投入2小時的Python學習,可以有效提升編程技能。 1.學習新知識:閱讀文檔或觀看教程。 2.實踐:編寫代碼和完成練習。 3.複習:鞏固所學內容。 4.項目實踐:應用所學於實際項目中。這樣的結構化學習計劃能幫助你係統掌握Python並實現職業目標。

在兩小時內高效學習Python的方法包括:1.回顧基礎知識,確保熟悉Python的安裝和基本語法;2.理解Python的核心概念,如變量、列表、函數等;3.通過使用示例掌握基本和高級用法;4.學習常見錯誤與調試技巧;5.應用性能優化與最佳實踐,如使用列表推導式和遵循PEP8風格指南。

Python適合初學者和數據科學,C 適用於系統編程和遊戲開發。 1.Python簡潔易用,適用於數據科學和Web開發。 2.C 提供高性能和控制力,適用於遊戲開發和系統編程。選擇應基於項目需求和個人興趣。

Python更適合數據科學和快速開發,C 更適合高性能和系統編程。 1.Python語法簡潔,易於學習,適用於數據處理和科學計算。 2.C 語法複雜,但性能優越,常用於遊戲開發和系統編程。

每天投入兩小時學習Python是可行的。 1.學習新知識:用一小時學習新概念,如列表和字典。 2.實踐和練習:用一小時進行編程練習,如編寫小程序。通過合理規劃和堅持不懈,你可以在短時間內掌握Python的核心概念。

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Atom編輯器mac版下載
最受歡迎的的開源編輯器

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。