小白的算法初识课堂(part2)--选择排序
學(xué)習(xí)筆記
學(xué)習(xí)書目:《算法圖解》- Aditya Bhargava
文章目錄
- 選擇排序
- 內(nèi)存工作原理
- 數(shù)組
- 鏈表
- 讀取
- 索引
- 插入
- 刪除
- 是常見的數(shù)組和鏈表操作的運行時間
- 選擇排序
- python實現(xiàn)
選擇排序
內(nèi)存工作原理
假如我去博物館參觀,帶了玩具熊和花盆,我想把這兩樣?xùn)|西寄存一下,那么我就在服務(wù)臺要了兩個柜子寄存它們,寄存過我就可以去參觀博物館啦!(先別管我為啥不把兩樣?xùn)|西寄存在一個柜子)
這大致就是計算機內(nèi)存的工作原理。計算機就像是很多柜子的集合體,每個柜子都有地址。
比如,當(dāng)我們需要將數(shù)據(jù)存儲到內(nèi)存時,我就請求計算機提供存儲空間,計算機就會給我一個存儲地址fe0ffeeb。
當(dāng)我們需要存儲多項數(shù)據(jù)時,有兩種基本方式:數(shù)組和鏈表。下面,我們就簡要介紹一下這兩種方式。
數(shù)組
假如我想將我明天的計劃(比如:吃早飯、學(xué)《概率論與數(shù)理統(tǒng)計》、睡午覺)寫入內(nèi)存中,那么我們應(yīng)該使用數(shù)組還是鏈表呢?
如果我們將計劃存儲在數(shù)組中,那么使用數(shù)組則意味著所有計劃在內(nèi)存中都是相連的(緊靠在一起的):
假設(shè)此時,我們要添加第4個計劃(吃水果),但是我們看到后面的格子中被別人占用了,那該咋整呢?此時,我們就要請求計算機重新分配給我們一塊可以容納4個計劃的內(nèi)存空間,再將所有計劃都移動到那里去。
但是,如果我又要添加1個計劃可咋整呢?難道我們要再移動一次?
此時,一種解決之道是“預(yù)留空間”:即便當(dāng)前只有3個計劃,也請計算機提供10個位置,以防需要添加計劃。這樣,只要待辦事項不超過10個,就無需轉(zhuǎn)移。
雖然這種方法不錯,但還是存在以下兩種缺陷:
- 我額外請求的位置可能根本用不上,這將浪費內(nèi)存。
- 我沒有使用,別人也用不了。 待辦事項超過10個后,你還得轉(zhuǎn)移。
鏈表
相比于數(shù)組,鏈表中的元素可存儲在內(nèi)存的任何地方。鏈表的每個元素都存儲了下一個元素的地址,從而使一系列隨機的內(nèi)存地址串在一起。
在鏈表中添加元素很容易:只需將其放入內(nèi)存,并將其地址存儲到前一個元素中。
使用鏈表時,根本就不需要移動元素。這還避免了使用數(shù)組時,因為內(nèi)存中沒有足夠的連續(xù)空間(只有足夠的不連續(xù)空間)而無法分配內(nèi)存的情況。
鏈表的優(yōu)勢在插入元素方面,那數(shù)組的優(yōu)勢又是什么呢?
讀取
如果我們使用鏈表來存儲數(shù)據(jù),在需要讀取鏈表的最后一個元素時,你不能直接讀取,因為你不知道它所處的地址,必須先訪問元素#1,從中獲取元素#2的地址,再訪問元素#2并從中獲取元素#3的地址,以此類推,直到訪問最后一個元素。需要同時讀取所有元素時,鏈表的效率很高:你讀取第一個元素,根據(jù)其中的地址再讀取第二個元素,以此類推。但如果你需要跳躍,鏈表的效率真的很低。
而數(shù)組則與此不同,因為數(shù)組是相連的,所以如果我們知道數(shù)元素#1的地址為00,那么元素#5的地址就是04。需要隨機地讀取元素時,數(shù)組的效率很高,因為可迅速找到數(shù)組的任何元素。
索引
數(shù)組的元素帶編號,編號從0而不是從1開始:
In [14]: (1, 2, 3, 4)[0] Out[14]: 1從0開始讓基于數(shù)組的代碼編寫起來更容易,因此程序員始終堅持這樣做。幾乎所有的編程語言都從0開始對數(shù)組元素進行編號。元素的位置稱為索引,因此,不說元素1的位置為0,而說元素1位于索引0處。
插入
假如,我突然想起來,明天吃完早飯后要先跑步,那么我們將跑步這個計劃插入總計劃中的時候,數(shù)組和鏈表哪一個表現(xiàn)更好呢?
使用鏈表時,插入元素很簡單,只需要修改它前面的那個元素指向的地址即可:
使用數(shù)組時,則需要將后面的元素都向后移動。但是,如果后面沒有空間了,那么還得將整個數(shù)組復(fù)制到其他地方。
刪除
如果你要刪除元素呢?
鏈表也是更好的選擇,因為只需修改前一個元素指向的地址即可。而使用數(shù)組時,刪除元素后,必須將后面的元素都向前移。不同于插入,刪除元素總能成功。如果內(nèi)存中沒有足夠的空間,插入操作可能失敗,但在任何情況下都能夠?qū)⒃貏h除。
是常見的數(shù)組和鏈表操作的運行時間
| 讀取 | O(1) | O(n) |
| 插入 | O(n) | O(1) |
| 刪除 | O(n) | O(1) |
數(shù)組和鏈表哪個用得更多呢?顯然要看情況。但數(shù)組用得很多,因為它支持隨機訪問。
順序訪問意味著從第一個元素開始逐個地讀取元素。鏈表只能順序訪問:要讀取鏈表的第十個元素,得先讀取前九個元素,并沿鏈接找到第十個元素。隨機訪問意味著可直接跳到第十個元素。
選擇排序
假設(shè)A班有5位同學(xué),對于這些同學(xué),我的計算機里都記錄了它們的期末考試總分。現(xiàn)在我將這些同學(xué)和他們的成績裝在列表1中:
| A | 259 |
| B | 199 |
| C | 287 |
| D | 250 |
| E | 266 |
我想按照成績的高低對它們進行降序排列,那么我們該怎么做呢?
一種辦法是遍歷這個列表1,找出成績最好的同學(xué),并將該同學(xué)添加到一個新列表(列表2)中;再次這樣做,找出成績第2好的同學(xué),再將該同學(xué)添加到列表2,以此類推,最終我們將得到一個有序的列表2。
下面從計算機科學(xué)的角度出發(fā),我們看看這需要多長時間。別忘了,O(n)時間意味著查看列表中的每個元素一次,如果我們想要找出成績最好的同學(xué),就要檢查列表中的每一個元素。需要的總時間為 O(n × n),即O(n2n^2n2).
- 需要檢查的元素越來越少
但是此時,我們可能會提出一個疑問。
隨著排序的進行,每次需要檢查的元素數(shù)在逐漸減少,最后一次需要檢查的元素都只有一個。既然如此,運行時間怎么還是O(n2n^2n2)呢?
你說得沒錯,并非每次都需要檢查n個元素。第一次需要檢查n個元素,但隨后檢查的元素數(shù)依次為n - 1, n – 2, …, 2和1。平均每次檢查的元素數(shù)為12n\frac{1}{2} n21?n,因此運行時間為O(12n2\frac{1}{2} n^221?n2)。但大O表示法省略諸如1/2這樣的常數(shù),因此簡單地寫作O(n × n)或O(n2n^2n2).
python實現(xiàn)
# -*- coding: utf-8 -*-def findMaximum(arr):maximum = arr[0]maximum_index = 0for item in range(1, len(arr)):if arr[item] > maximum :maximum = arr[item]maximum_index = itemreturn maximum_indexdef selectionSort(arr):newArr = []for item in range(len(arr)):maximum = findMaximum(arr)newArr.append(arr.pop(maximum))return newArrprint(selectionSort([259,199,287,250,266]))
輸出結(jié)果:
[287, 266, 259, 250, 199]總結(jié)
以上是生活随笔為你收集整理的小白的算法初识课堂(part2)--选择排序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 迅捷 FW450R 无线路由器端口映射设
- 下一篇: 小白的算法初识课堂(part3)--递归