20172313 2018-2019-1 《程序設計與數據結構》第五週學習總結
教材學習內容總結
public static <T extends Comparable<T>> Boolean
LinearSearch (T[] data, int min, int max, Ttarget)
- 線性查找法:從該列表頭開始依次比較每個值,直到找到該目標元素。
- 二分查找法
- 二分查找要求查找池中的項目組都是已排序的。
- 二分查找的每次比較都會刪除一半的可行候選項。
- 查找算法的比較
- 線性查找算法具備限行時間複雜度O(n)。由於試一次每回查找一個元素,因此複雜度是線性的————直接與待查找元素數目成比例。
- 二分查找具備一個對數算法且具備時間複雜度O(l0g2n)。二分查找的複雜度是對數集的,這使得它對於大型查找池很是有效率。
- 若是說對數查找比線性查找更有效率,那麼爲何咱們還要使用線性查找?第一,線性查找通常比二分查找要簡單,所以咱們編程和調試起來更容易。第二,線性查找無需花費額外成原本排序該查找列表。在將查找池保持爲排序狀態和提升查找效率的努力之間存在着權衡關係。
- 排序
- 排序是這樣一個過程,即基於某一標準,將某一組項目按照某個規定順序排列。
- 排序算法一般分爲兩類:順序排序: 它一般使用一對嵌套循環對n個元素排序,須要大約n2次比較;對數排序: 它對n個元素進行排序一般須要大約nlog2n次比較。
- 選擇排序法:經過反覆地將某特定值放到它在列表中的最終已排序位置從而完成對某一列表值的排序:(1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置(2)再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。(3)重複第二步,直到全部元素均排序完畢。。
- 插入排序:經過反覆地將某一特定值放到它在列表中的最終已排序位置從而完成對某一列表值的排序:(1)將第一待排序序列第一個元素看作一個有序序列,把第二個元素到最後一個元素當成是未排序序列。(2)從頭至尾依次掃描未排序序列,將掃描到的每一個元素插入有序序列的適當位置。(若是待插入的元素與有序序列中的某個元素相等,則將待插入元素插入到相等元素的後面。)
- 冒泡排序法:冒泡排序法經過重複地比較相鄰元素且在必要時將他們互換,從而完成對某個列表的排序。(1)比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。(2)對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。這步作完後,最後的元素會是最大的數。(3)針對全部的元素重複以上的步驟,除了最後一個。(4)持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較。
- 快速排序法:經過將列表分析,而後對着兩個分區進行遞歸式排序,從而完成對某個列表的排序。(1)從數列中挑出一個元素,稱爲 「基準」(pivot)(2)從新排序數列,全部元素比基準值小的擺放在基準前面,全部元素比基準值大的擺在基準的後面(相同的數能夠到任一邊)。在這個分區退出以後,該基準就處於數列的中間位置。這個稱爲分區(partition)操做。(3) 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。遞歸的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞歸下去,可是這個算法總會退出,由於在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。
- 歸併排序法:經過將列表遞歸式分紅兩半直至每一子列表都含有一個元素,而後將這些子列表歸併到一個排序順序中,從而完成對列表的排序。(1)申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列(2)設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置(3)比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置(4)重複步驟3直到某一指針達到序列尾(5)將另外一序列剩下的全部元素直接複製到合併序列尾
- 基數排序法:是基於隊列處理,使用排列密鑰而不是直接地進行比較元素,來實現元素排序。是將陣列分到有限數量的桶子裏。每一個桶子再個別排序(有可能再使用別的排序算法或是以遞迴方式繼續使用桶排序進行排序)。桶排序是鴿巢排序的一種概括結果。當要被排序的陣列內的數值是均勻分配的時候,桶排序使用線性時間(Θ(n))。但桶排序並非 比較排序,他不受到 O(n log n) 下限的影響。 簡單來講,就是把數據分組,放在一個個的桶中,而後對每一個桶裏面的在進行排序。
- 各類排序算法效率比較
教材學習中的問題和解決過程
- 問題一:在個人理解中,冒泡排序就是比較相鄰位置的元素,根據大小將它們互換。而插入排序把特定值放到已排序的位置也須要相鄰元素的比較,那麼它們二者的實現的區別到底在哪?
- 問題一解決方法:
冒泡排序 |
O(N^2) |
O(N^2) |
O(N) |
O(1) |
穩定 |
插入排序 |
O(N^2) |
O(N^2) |
O(N) |
O(1) |
穩定 |
二者在數據上簡直一毛同樣,只考慮複雜度的話徹底能夠互相替代。但深究的話,仍是能找出許多不一樣:
打個比方:這是咱們今天的主角小明,他機智勇敢熱愛學習樂於助人。有一天他上體育課,排的是3號位置,老師說:同7學們請用冒泡排序的方法排好隊。小明以爲本身比2號的小紅高,因此互換位置,成爲了2號。而後他以爲比1號小剛高,因此又互換位置排到了1號。老師說:小明,滾到最後去。最終他成了100號,這就是插入排序。有一天他上體育課,排的是3號位置,老師說:同窗們請用冒泡排序的方法排好隊。小明以爲本身比2號的小紅高,因此互換位置,成爲了2號。而後他以爲比1號小剛高,因此又互換位置排到了1號。老師說:小明,滾到最後去。最終他成了100號,這就是插入排序。html
插入排序:
將無序的元素插入到有序的元素序列中,插入後仍然有序
int min;
T temp;
for(int index = 0; index < data.length-1;index++)
{
min = index;
for(int scan = index + 1; scan < data.lenth; scan ++)
if(data[scan].compareTo(data[min])<0)
min = scan;
/*8swap the values*/
temp = data[min];
data[min] = data[index];
data[index] = temp;
}
/*
舉例:從小到大排列[2,1,4,3]
第一趟排序:[1,2,4,3] 交換次數:1 比較次數:1
第二趟排序:[1,2,4,3] 交換次數:0 比較次數:1
第三趟排序:[1,2,3,4] 交換次數:1 比較次數:2
從小到大排列[4,3,2,1]
第一趟排序:[3,4,2,1] 交換次數:1 比較次數:1
第二趟排序:[2,3,4,1] 交換次數:2 比較次數:2
第三趟排序:[1,2,3,4] 交換次數:3 比較次數:3
*/
冒泡排序:
比較相鄰元素,直到序列變爲有序爲止
int position, scan;
T temp;
for(position = data.length - 1; position >= 0; position--){
int position, scan;
T temp;
for(position = data.length - 1;position >= 0; position--){
for(scan = 0;scan <= position - 1;scan++){
if(data[scan].compareTo(data[scan+1]) > 0)
swap(data,scan, scan + 1);
}
}
}
/*
舉例:從小到大排列[2,1,4,3]
第一趟排序:[1,2,4,3] 交換次數:1 比較次數:3
第二趟排序:[1,2,4,3] 交換次數:0 比較次數:2
第三趟排序:[1,2,3,4] 交換次數:1 比較次數:1
從小到大排列[4,3,2,1]
第一趟排序:[3,2,1,4] 交換次數:3 比較次數:3
第二趟排序:[2,1,3,4] 交換次數:2 比較次數:2
第三趟排序:[1,2,3,4] 交換次數:1 比較次數:1
*/
- 問題二:在快速排序法中,若是說能夠將隨意一個數做爲分區元素的話,那麼如何保證分區元素先後元素個數相同呢?對快速排序法不是很瞭解。
- 問題三解決方案:我對於快速排序法理解有誤。假設咱們如今對「6 1 2 7 9 3 4 5 10 8」這個10個數進行排序。爲了方便,就讓第一個數6做爲基準數吧。接下來,須要將這個序列中全部比基準數大的數放在6的右邊,比基準數小的數放在6的左邊,:分別從初始序列「6 1 2 7 9 3 4 5 10 8」兩端開始「探測」。先從右往左找一個小於6的數,再從左往右找一個大於6的數,而後交換他們。這裏能夠用兩個變量i和j,分別指向序列最左邊和最右邊。咱們爲這兩個變量起個好聽的名字「哨兵i」和「哨兵j」。剛開始的時候讓哨兵i指向序列的最左邊(即i=1),指向數字6。讓哨兵j指向序列的最右邊(即=10),指向數字。
首先哨兵j開始出動。由於此處設置的基準數是最左邊的數,因此須要讓哨兵j先出動,這一點很是重要(請本身想想爲何)。哨兵j一步一步地向左挪動(即j--),直到找到一個小於6的數停下來。接下來哨兵i再一步一步向右挪動(即i++),直到找到一個數大於6的數停下來。最後哨兵j停在了數字5面前,哨兵i停在了數字7面前。
如今交換哨兵i和哨兵j所指向的元素的值。交換以後的序列以下:
6 1 2 5 9 3 4 7 10 8
到此,第一次交換結束。接下來開始哨兵j繼續向左挪動(再友情提醒,每次必須是哨兵j先出發)。他發現了4(比基準數6要小,知足要求)以後停了下來。哨兵i也繼續向右挪動的,他發現了9(比基準數6要大,知足要求)以後停了下來。此時再次進行交換,交換以後的序列以下:
6 1 2 5 4 3 9 7 10 8
第二次交換結束,「探測」繼續。哨兵j繼續向左挪動,他發現了3(比基準數6要小,知足要求)以後又停了下來。哨兵i繼續向右移動,糟啦!此時哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。說明此時「探測」結束。咱們將基準數6和3進行交換。交換以後的序列以下:
3 1 2 5 4 6 9 7 10 8
到此第一輪「探測」真正結束。此時以基準數6爲分界點,6左邊的數都小於等於6,6右邊的數都大於等於6。回顧一下剛纔的過程,其實哨兵j的使命就是要找小於基準數的數,而哨兵i的使命就是要找大於基準數的數,直到i和j碰頭爲止。
OK,解釋完畢。如今基準數6已經歸位,它正好處在序列的第6位。此時咱們已經將原來的序列,以6爲分界點拆分紅了兩個序列,左邊的序列是「3 1 2 5 4」,右邊的序列是「9 7 10 8」。接下來還須要分別處理這兩個序列。由於6左邊和右邊的序列目前都仍是很混亂的。不過沒關係,咱們已經掌握了方法,接下來只要模擬剛纔的方法分別處理6左邊和右邊的序列便可。git
- 問題三:在問題二中提到由於此處設置的基準數是最左邊的數,因此須要讓哨兵j先出動,這一點很是重要,對這句話不是特別理解。
- 問題三解決方案:若是選取最左邊的數arr[left]做爲基準數,那麼先從右邊開始可保證i,j在相遇時,相遇數是小於基準數的,交換以後temp所在位置的左邊都小於temp。但先從左邊開始,相遇數是大於基準數的,沒法知足temp左邊的數都小於它。因此進行掃描,要從基準數的對面開始,又或者選取最中間的數做爲基準數就不會出現相似問題。
- 問題四:在學習基數排序法的時候,瞭解到基數排序是基於隊列處理的,而且要基於排序關鍵字,但基數排序法是如何實現的?基數和排序關鍵字又是如何選取的?
- 問題四解決方案:我以爲用理論來解釋是空泛的,舉個例子能更好的幫助理解。
例如要對大小爲[1..1000]範圍內的n個整數A[1..n]排序
首先,能夠把桶設爲大小爲10的範圍,具體而言,設集合B[1]存儲[1..10]的整數,集合B[2]存儲 (10..20]的整數,……集合B[i]存儲( (i-1)10, i10]的整數,i = 1,2,..100。總共有 100個桶。
而後,對A[1..n]從頭至尾掃描一遍,把每一個A[i]放入對應的桶B[j]中。 再對這100個桶中每一個桶裏的數字排序,這時可用冒泡,選擇,乃至快排,通常來講任 何排序法均可以。
最後,依次輸出每一個桶裏面的數字,且每一個桶中的數字從小到大輸出,這 樣就獲得全部數字排好序的一個序列了。
假設有n個數字,有m個桶,若是數字是平均分佈的,則每一個桶裏面平均有n/m個數字。若是對每一個桶中的數字採用快速排序,那麼整個算法的複雜度是
O(n + m * n/m*log(n/m)) = O(n + nlogn – nlogm)
從上式看出,當m接近n的時候,桶排序複雜度接近O(n)
固然,以上覆雜度的計算是基於輸入的n個數字是平均分佈這個假設的。這個假設是很強的 ,實際應用中效果並無這麼好。若是全部的數字都落在同一個桶中,那就退化成通常的排序了。
前面說的幾大排序算法 ,大部分時間複雜度都是O(n2),也有部分排序算法時間複雜度是O(nlogn)。而桶式排序卻能實現O(n)的時間複雜度。但桶排序的缺點是:
1)首先是空間複雜度比較高,須要的額外開銷大。排序有兩個數組的空間開銷,一個存放待排序數組,一個就是所謂的桶,好比待排序值是從0到m-1,那就須要m個桶,這個桶數組就要至少m個空間。
2)其次待排序的元素都要在必定的範圍內等等。
代碼調試中的問題和解決過程
- 問題1:在作編程項目pp9_3的時候,不知道怎麼計算程序的執行時間。
- 問題一解決方案:
通常輸出日期時間常常會用到Date這個類:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式
System.out.println(df.format(new Date()));// new Date()爲獲取當前系統時間
(1)以毫秒爲單位計算
static long currentTimeMillis() , 該方法返回值是從1970年1月1日凌晨到此時刻的毫秒數
long startTime=System.currentTimeMillis(); //獲取開始時間
doSomeThing(); //測試的代碼段
long endTime=System.currentTimeMillis(); //獲取結束時間
System.out.println("程序運行時間: "+(end-start)+"ms");
(2)以納秒爲單位計算
long startTime=System.nanoTime(); //獲取開始時間
doSomeThing(); //測試的代碼段
long endTime=System.nanoTime(); //獲取結束時間
System.out.println("程序運行時間: "+(end-start)+"ns");
上週考試錯題總結
這周沒有錯題哦~算法
結對及互評
- 博客中值得學習的或問題:
- 排版精美,對教材的總結細緻,善於發現問題,對於問題研究得很細緻,解答也很周全。
- 代碼中值得學習的或問題:
點評過的同窗博客和代碼
其餘(感悟、思考等,可選)
國慶假期過去了,這周的學習效率還算能夠,完成了學習任務,達到了本身預期的目標,但在看了其餘同窗的博客後,仍是感到本身的學習時間仍是遠遠不夠,相比之下,本身的學習效率仍是較低的,但願可以在之後的學習中繼續進步!編程
學習進度條
第一週 |
200/200 |
1/1 |
5/20 |
|
第二週 |
981/1181 |
1/2 |
15/20 |
|
第三週 |
1694/2875 |
1/3 |
15/35 |
|
第四周 |
3129/6004 |
1/4 |
15/50 |
|
第五週 |
1294/7298 |
1/5 |
15/65 |
|
計劃學習時間:15小時數組
實際學習時間:15小時數據結構
參考資料