2018-2019-20172329 《Java軟件結構與數據結構》第五週學習總結
教材學習內容總結
《Java軟件結構與數據結構》第九章-排序與查找
一、查找
- 1、查找概念簡述:
(1)查找3是這樣一個過程,即在某個項目組中尋找某一指定目標元素,或者確定該組中並不存在該目標元素。對其進行查找的項目組有時也稱爲查找組。
(2)查找方式的分類:線性查找,二分查找。
(3)查找所要完成的目標:儘可能高效的完成查找,從算法分析的角度而言,我們希望最小化比較操作的次數,通常,查找池裏項目數目越多,爲了尋找該目標而做出的比較操作次數就越多,因此該查找池中項目的樹目定義了該問題的大小。
- 2、線性查找法:
(1)如果該查找池組織成了一個某類型的列表,那麼完成該查找的一個簡單方式就是從該列表頭開始一次比較每一個值,直到找到該目標元素爲止。
(2)具體的樣式圖:
(3)代碼實現:
public static <T>
boolean linearSearch(T[] data, int min, int max, T target)
{
int index = min;
boolean found = false;
while (!found && index <= max)
{
found = data[index].equals(target);
index++;
}
return found;
}
- 3、二分查找法:
(1)前提:查找池中的項目組是已排序的。
(2)具體過程:二分查找是從排序列表的中間開始查找,而不是從一端或另一端開始。如果沒有在那個中間元素找到目標元素則繼續查找。
(3)具體詳細的樣式圖:
(4)特點:二分查找將利用了查找池是已排序的這一事實;二分查找的每次比較都會刪除一半的可行候選項;結合了遞歸的思想。
(5)代碼實現:
public static <T extends Comparable<T>>
boolean binarySearch(T[] data, int min, int max, T target)
{
boolean found = false;
int midpoint = (min + max) / 2; // determine the midpoint
if (data[midpoint].compareTo(target) == 0)
found = true;
else if (data[midpoint].compareTo(target) > 0)
{
if (min <= midpoint - 1)
found = binarySearch(data, min, midpoint - 1, target);
}
else if (midpoint + 1 <= max)
found = binarySearch(data, midpoint + 1, max, target);
return found;
}
(6)注:如果二分查找分爲元素爲奇數和偶數個兩種樣式,就具體的問題有具體的解決方式,如果線性表爲一個偶數個的有序列表的時候,所採用的中點可以是中間兩個值的任意一個。
- 4、查找算法的比較:
(1)對於線性查找而言,最好的情形是目標元素剛好是我們所考察項目組中的第一個項目。
(2)線性查找算法具有線性時間複雜度O(n),因爲是依次每回查找一個元素,所以複雜度是線性的——直接與待查找元素數目成比例。
(3)二分查找算法普遍要快得多。最好的情形就是一次比較就找到了該目標——也就是說,目標元素剛好位於數組的中點。最壞的情形出現在元素不在該列表的時候,在這種情形下,在刪除所有數據之前,我們不得不進行大約log2n次比較。因此找到位於該查找池中某一元素的預期情形是大約(log2n)/2次比較。
(4)二分查找的複雜度是對數級的,這使得它對於大型查找池非常有效率。
二、排序
- 1、排序概念簡述
(1)排序是這樣一個過程,即基於某一標準,要麼以升序要麼以降序將某一組項目按照某個規定順序排列。
(2)基於效率排序算法分爲兩類:順序排序和對數排序。
(3)在順序排序裏,有三種排序方式,分別爲:選擇排序、插入排序以及冒泡排序。
(4)在對數排序中,有兩種排序方式:快速排序和歸併排序。
- 2、選擇排序法
(1)選擇排序算法通過反覆地將某一特定值放到它在列表中的最終已排序位置,從而完成對某一列值的排序。
(2)算法實現原理圖
- 3、插入排序法
(1)插入排序算法通過反覆地將某一特定值插入到該列某個已排序的子集中來完成對列表值的排序。
(2)算法實現原理圖
- 4、冒泡排序法
(1)冒泡排序法是另一種使用了兩個嵌套循環的順序排序算法,通過重複地比較相鄰元素且在必要時將它們互換,從而完成對某個列表的排序。
(2)算法實現原理圖
- 5、快速排序法
(1)快速排序算法通過將列表分區,然後對這兩個分區進行遞歸式排序,從而完成對整個列表的排序。
(2)算法實現原理圖
- 6、歸併排序法
(1)歸併排序算法通過將列表遞歸式分爲兩半直至每一字列表都含有一個元素,然後將這些字列表歸併到一個排序順序中,從而完成對列表的排序。
(2)算法實現原理圖
- 7、基數排序法
(1)基數排序是基於隊列處理的。
教材學習中的問題和解決過程
- 問題1:在學習到二分查找的時候書中的例子考慮到的是奇數個變量,假如是偶數個變量,我們將如何進行處理?
問題1解決過程:包括在老師上課的時候也都講過,偶數個的話,中間兩位任意一個都可以,就這個任意一個都可以就讓我有點費解,在看了書中的解釋
在二分查找實現中,確定中點索引的計算丟棄了任何分數部分,因此它選擇的是兩個中間值的第一個。
就這個解釋而言,而且當我僅僅看書看到這裏的時候,理解起來肯定還是不容易的,所以我繼續看了書後面的內容,在看到對數排序的歸併排序中,發現其實現的原理非常相似,其中在歸併排序的代碼實現中,我的問題在其中也有所體現:
int mid = (min + max) / 2;
while (first1 <= last1 && first2 <= last2)
{
if (data[first1].compareTo(data[first2]) < 0)
{
temp[index] = data[first1];
first1++;
}
else
{
temp[index] = data[first2];
first2++;
}
index++;
}
從上述的代碼就可以看出,代碼實現的過程其實就是分開比較,分類比較,先確定一個數,比它的左邊和右邊。
問題2:在學習選擇排序法的過程中,其中在那個
Sorting
類中有swap
這個寫好的方法,其作用就是交換兩個數組位置的內容,但是在我們之前學習單鏈表的時候,在交換節點的時候總是需要考慮各個方面的因素,那我們能不能也寫這樣一個方法進行結點互換呢?問題2解決過程:因爲鏈表中每一個結點都是鏈接起來的,假如盲目的斷掉某一個可能就會導致空指針的問題或者更嚴重就會導致後面所有數據的丟失,所以在之前學習的過程中,在我學習的過程中,都是用先賦值給一個臨時結點,然後把後面的結點插入到前面結點之前,再用後面結點連接到它後面的後面那個結點,這樣實現結點的交換,但是這僅僅只能實現於在相鄰的兩個結點,所以有沒有一種方法,我們可以實現類似於
swap
的方法呢?所以我首先在網絡上尋找有沒有講解類似相關知識的博文以得到靈感。
這是我所找到的一個博主自己畫的一張圖
其實回想一下之前自己寫過的一個程序中,彷彿自己好像也用到了相關的知識,因爲我覺得交換無非就是首先需要找到要交換的元素,再計算它們之間需要進行尋找的次數,也就是它們之間的距離,通過這樣幾項元素,就可以大致寫出僞代碼了。
我的僞代碼思路就是:(1)因爲首先我們需要定義三個形式參數(這裏我沒有考慮假如交換的元素在鏈表中出現了多次,這個在接下來的時間裏我會再繼續解決的),三個形參分別爲list
node1
node2
,其中node1
node2
爲需要交換的兩個元素,首先要找到node1和node2,然後把node1插入到node2的後面,把node2插入到node1的前面,再刪除之前的node1和node2。
因爲問題提出的時間很短,代碼實現正在初步形成中。
- 問題3:在學習快速排序法的時候,有這樣一個方法
partition
方法,在這個方法中,它最終返回的是一個整型的right
,這個類的用處是什麼呢? 問題3解決方法:首先,這個方法的兩個內層循環用於尋找位於錯誤分區的交換元素,第一個循環從左邊掃描右邊以尋找到大於分區元素的元素,第二個循環,從右邊掃描左邊以尋找到小於分區元素的元素,在尋找到之後,進行互換。
問題4:就上一個問題繼續,其中有這樣一句代碼
int middle = (min + max) / 2;
,在上學期中,有學習過在計算機語言中,假如定義一個整型的變量,如果給它賦予一個double型的數字,比如小數,變量最終只取整形的部位,舉個例子類似於這樣
在這個方法中,假如是這個樣子,會不會對程序有影響?問題4解決方法:這個程序中,有考慮到如此的情況,所以並不會受影響。
代碼調試中的問題和解決過程
問題1:在做pp9.2的作業中,發現自己所編寫的外循環總是走一遍就停下來了,如下:
問題1解決方法:
一開始以爲自己是不是哪裏編寫錯了,就大改了一番,自己重新多寫了一個循環,仿照冒泡排序的代碼寫了一個,錯的更厲害了,最後發現自己的錯因竟是變量的名稱沒有修改對。
修改成如下就可以了。
問題2:在寫pp9.3的時候,再計算時間的時候,發現自己輸出的一直是0毫秒?如下圖
問題2解決方法:因爲詢問了郭愷同學,在今天也就是十月十四日的時候,郭凱同學說他也遇到了同樣的問題,可能是因爲毫秒太大使得只顯示了實數位,將其改爲納秒的時候,問題也就隨之解決了
也就是說,當我們計算毫秒的時候所用的語句是下面代碼:
long startTime = System.currentTimeMillis(); //獲取開始時間
//doSomething(); 測試的代碼段
long endTime = System.currentTimeMillis(); //獲取結束時間
System.out.println("程序運行時間:" + (endTime - startTime) + "ms"); //輸出程序運行時間
計算納秒的時候用的計算時間爲:
long startTime=System.nanoTime(); //獲取開始時間
doSomeThing(); //測試的代碼段
long endTime=System.nanoTime(); //獲取結束時間
System.out.println("程序運行時間: "+(endTime-startTime)+"ns");
代碼鏈接
上週考試錯題總結
上週無錯題,太秀了!
結對及互評
- 本週結對學習情況
- 博客中值得學習的或問題:
- 內容詳略得當;
- 代碼調試環節比較詳細;
- 基於評分標準,我給本博客打分:5分。得分情況如下:
- 正確使用Markdown語法(加1分):
- 模板中的要素齊全(加1分)
- 教材學習中的問題和解決過程, 一個問題加1分
代碼調試中的問題和解決過程, 一個問題加1分
- 博客中值得學習的或問題:
- 內容詳略得當;
- 代碼調試環節比較詳細;
- 基於評分標準,我給本博客打分:9分。得分情況如下:
- 正確使用Markdown語法(加1分):
- 模板中的要素齊全(加1分)
- 教材學習中的問題和解決過程, 一個問題加1分
- 代碼調試中的問題和解決過程, 一個問題加1分
感悟
這週上了三節Java課,也發現了自己在學習當中的一些問題,希望自己可以把握住現在可以學習的機會,不要在以後後悔莫及的時候反思自己沒有在正確的時間做正確的事情。加油!
不忘初心,方得始終!
學習進度條
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | |
---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 |
第一週 | 0/0 | 1/1 | 6/6 |
第二週 | 1313/1313 | 1/2 | 20/26 |
第三週 | 901/2214 | 1/3 | 20/46 |
第四周 | 3635/5849 | 2/4 | 20/66 |
第五週 | 1525/7374 | 1/5 | 20/86 |
參考資料
藍墨雲班課
Java程序設計
Java計算程序代碼執行時間的方法小結
Java實現-兩兩交換鏈表中的節點
Java數據結構和算法(九)——高級排序
數據結構常見的八大排序算法