20172310 2017-2018《程序設計與數據結構》(下)第五週學習總結

20172310 2017-2018《程序設計與數據結構》(下)第五週學習總結

教材學習內容總結

第九章_排序與查找

學習幾種排序算法,並討論這些算法的複雜度html

9.1查找(線性查找與二分查找算法)

  • 查找(searching) 是在某個項目組中尋找某一指定目標元素, 或者肯定該組中並不存在該目標元素的一個過程。對其進行查找的項目組有時
    也稱爲查找池( search pool)。java

  • 咱們的目標就是儘量高效地完成查找。從算法分析的角度而言,高效的查找會使該過程所作的比較操做次數最小化。同時該查找池中項目的數目定義了該問題的大小。git

  • 查找某一對象,必須可以將其跟另外一個對象進行比較。咱們對這些算法的實現就是對某個Comparable對象的數組進行查找。
    如:public class Searching<T extends Comparable<T>>這個泛型聲明的實際結果就是以用任何實現Comparable接口的類來實例化Searching類。算法

以Comparable接口的方式定義的Searching要求咱們在使用查找或排序方法時必須實例化因而引出了泛型靜態方法。數組


  • 靜態方法(static method),又稱爲類方法(class mehod),能夠經過類名來激話。經過使用static 修飾符就能夠把它聲明爲靜態的。
    例如,Math類的全部方法都是靜志的能夠以下經過Math類來調用sqrt方法:

System. out.printinl("squace root of 27: 」+ Math.sqrt(27));安全

  • 靜態方法不是做用於具體的對象中,所以不能引用實例變量(實例變量只存在類的一個實例中)。若是某個靜態方法試圖使用一個非靜態的變量,編譯器將發出一個錯誤。可是,靜態方法能夠引用靜態變量,由於靜態變量的存在與具體的對象無關。

  • 泛型方法:與建立泛型類類似,咱們也能夠建立泛型方法。即,不是建立一個引用泛 型參數的類,而是建立一個引用泛型的方法。泛型參數只應用於該方法。

要建立個泛型方法, 只需在方法頭的返回類型前插入一個泛型聲明便可:數據結構

public static<T extends Comparable< T> > boolean

lineatsearch (T[] data, int min, int max, T target)

泛型聲明必須位於返回類型以前,這樣泛型纔可做爲返回類型的部分。函數

  • 不必定非要指定用於替代泛型的數據類型。編譯器能夠從所提供的參數中推導出該數據類型。

  • 線性查找法
    若是該查找池組織成一個某類型的列表,那麼完成該查找的一個簡單方式就是從該列表頭開始依次比較每個值, 直至找到該目標元素。最後,咱們要麼找到該目標,要麼到達列表尾並得出該組中不存在該目標的結論。這種方式之因此稱爲線性查找(linear search),是由於它是從一端開始並以線性方式搜索該查找池的

線性查找法代碼:學習

public static<T extends Comparable<?super T> >
 boolean lineatsearch (T[] data, int min, int max, T target)
int index = min;
boolean found = false;
while (!found && index <= max)
if (data [index]. compareToltarget) == 0
found=true;
index++;
return found;

該while循環將遍歷數組元素,在找到元素或到達數組尾時其會終止測試

  • 線性查找算法至關容易理解,但不是特別高效的。優勢是線性查找並不要求查找池中的元素在數組中具備任何特定順序。

  • 二分查找法

    • 二分查找算法提高了查找過程的效率,可是查找池必須已排序。二分查找是從排序列表的中間開始查找。 二分查找都會將用於查找的剩餘數據去掉大約一半(它一樣會去掉中間元素)。 經過比較操做,咱們將消減半的查找池。 剩下的一半查找池將表示可行候選項(viable candidates),目格元素就有待於在它們中間數找到。

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;
    }
  }

binarySearch方法是遞歸實現的。若是沒有找到目標元素,且有更多待查找數據,則該方法將調用其自身,同時傳遞參數,這些參數縮減了數組內可行候選項的規模。min和max索引用於肯定是否還具備更多待查找數據。這就是說,若是削減後的查找區域一個元素也不含有,則該方法將不會調用其自身且會返回一個false 值。

  • 在該過程當中的任什麼時候刻, 咱們可能會有偶數個待查找值,所以就有兩個「中間」值,根據該算法,所採用的中點能夠是這兩個中間值的任何一個。在該分 查找實現中,肯定中點索引的計算丟棄了任何分數部分,所以它選擇的是兩個中間值中的第一個。

  • 查找算法的比較

    • 若是查找池中有n個元素,平均而言,在咱們找到所查找的那個元素以前咱們將不得不考察n/2個元素。所以,線性直找算法具備線性時間複雜度O(n),由於是依次每回查找一個元素, 因此複雜度是線性的一直接與待查找元素數目成比例。

    • 找到位於該查找地中某元素的預期情形是大約(log2n)/2次比較。所以,二分查找具備一個對數算法且具備時間複雜度O(log2n)。與線性查找相比,n值較大時,二分查找要快得多。

    • 兩種查找算法的優點
      1.線性查找般比查找要簡單,線性查找無需花費額外成原本排序該查找列表。
      2.二分查找的複雜度是對教級的,這使得它對於大型查找池很是有效率。對於小型問題,這兩種類型的算法之間幾乎不存在實用差異。可是,隨着n的變大,二分查找就會變得吸引人。



9.2排序

  • 排序是基於某一標準,將某一組項目按照某個規定順序排列的一個過程。

  • 基於效率排序算法一般也分爲兩類:順序排序,它一般使用一對嵌套循環對n個元素排序,須要大約n^2次比較;以及對數排序, 它對n個元素進行排序一般須要大約nlog2 n次比較。與查找算法中同樣,在n較小時,這兩類算法之間幾乎不存在實際差異。

  • 本章學習三種順序排序選擇排序、 插入排序以及冒泡排序,以及兩種對數排序快速排序和歸併排序。


  • 選擇排序法

    • 選擇排序(selctionsomnd)算法經過反覆地將某特定 值放到它在列表中的最終已排序位置,從面完成對某列值的排序。換句話說,對於列表中的每一位置,該算法都將選擇由應該放進這位置的值並將其放在那裏。

    -選擇持排字算法的通常策略:打描整個列表以找出最小值。將這個值與該列表第一個位置處的值交換。掃描(除了第一個值的)剩餘部分列表並找出最小值,而後將它和該列表第二個位置處的值交換,掃描(除了前兩個值的)剩餘部分列表並找出最小值,而後將它和該列表第三個位置處的值交換,直到全部的數字排完。

  • 插入排序法
    • 插入排序算法經過反覆地將某一特定值插入到該列表某個已排序的子集中來完成對列表值的排序。

    • 插入排序算法的通常策略:對列表中的頭兩個值依據其相對大小對其進行排序,若是有必要則將它們互換。將列表的第三個值插入到頭兩個(已排序的)值中的恰當位置。而後將第四個值插入到列表頭三個值中的正確位置。每作出一次插入, 該排序子集中的值數目就會增長一個。繼續這一過程, 直至列表中的全部元素都獲得徹底排序。該插入過程須要對數組中的其餘元素移位,以給插入元素騰出空間。

  • 冒泡排序法
    • 冒泡排序算法經過重複地比較相鄰元素且在必要時將它們互換,從而完成對某個列表的排序。

    • 冒泡排序算法的通常策略:掃描該列表且比較鄰接元素,若是它們不是按相對順序排列則將其互換。這就像把最大值「冒泡」到列表的最後位置,這是它在最終已排序列表中的恰當位置。而後再次掃描該列表,冒泡出倒數第二個值。繼續這一過程, 直至全部元素都被冒泡到它們正確的位置。


  • 快速排序法

    • 快速排序(quick sort)算法是這樣對列表進行排序的:經過使用個任意選定的分區元素(partition clement)將該列表分區,而後對分區元素的任邊的子列表進行遞歸排序。
    • 快速排序算法的般策略:首先, 選擇一個列表元素做爲分區元素。下步,分割該列表,使得小於該分區無素的全部元素位於該元素的左邊,全部大於該分區元素的元素位於右邊。最後,將該快速接序策略(遞歸式)應用於兩個分區。

  • 歸併排序法
    • 歸併排序(merge sort)算法是另外一種遞歸排序算法, 經過將列表遞歸式分紅兩半直至每一子列表都只含有一個元素,而後將這些子列表按順序重組,這樣就完成了對列表的排序。

    • 歸併排序算法的 通常策:首先將該列表分紅兩個大約相等的部分,而後對每一部分列表遞歸調用其自身。繼續該列表的遞歸分解,直至達到該遞歸的基本情形,這時活列表被分割成長度爲1的列表,根據定義,它是已排序的了。而後,隨着程序控制權傳園率該通歸調用結構,該算法將兩個遞歸調用所產生的那兩個排序子列表歸併爲個排序列表。


  • 基數排序法
    • 基數排序法能夠無需進行元素之間的相互比較來排序,是種更高效的排序算法。
    • 基數排序是基於隊列處理的。排序要基於某個特定的值,這個值稱爲排序關鍵字(sort key)。對於排序關鍵字中每一個數字/字符的每種可能取值,都會建立個單獨的隊列。隊列的數目(或可能取值的種數)就稱爲基數(radix)。 例如,若是要排序十進制數,則基數應該是10, 0~9的每一個數字都對應着一個隊列。



  • 各類排序算法的比較

教材學習中的問題和解決過程

  • 問題1:

但有時代碼倒是

  • 問題1解決方案:這兩種寫法我都嘗試了,沒有體現出區別,那第一種代碼是什麼意思呢?
    <T extends Comparable<T>><T extends Comparable<? super T>> 有什麼不一樣呢?

<T extends Comparable<T>>
類型 T 必須實現 Comparable 接口,而且這個接口的類型是 T。只有這樣,T 的實例之間才能相互比較大小。例如,在實際調用時若使用的具體類是 Dog,那麼 Dog 必須 implements Comparable <

<T extends Comparable<? super T>>
類型 T 必須實現 Comparable 接口,而且這個接口的類型是 T 或 T 的任一父類。這樣聲明後,T 的實例之間,T 的實例和它的父類的實例之間,能夠相互比較大小。例如,在實際調用時若使用的具體類是 Dog (假設 Dog 有一個父類 Animal),Dog 能夠從 Animal 那裏繼承 Comparable ,或者本身 implements Comparable 。<

按我理解這樣聲明的好處就是,例如對 Animal/Dog 這兩個有父子關係的類來講: <T extends Comparable<? super T>>能夠接受 List<Animal> ,也能夠接收 List 。 而 <T extends Comparable > 只能夠接收 List
因此,<T extends Comparable<? super T>> 這樣的類型參數對所傳入的參數限制更少,提升了 API 的靈活性。總的來講,在保證類型安全的前提下,要使用限制最少的類型參數。

能夠參考一下這篇文章:
如何理解 Java 中的 <T extends Comparable<? super T>>

代碼調試中的問題和解決過程

  • 問題1:在完成藍墨雲線性表實踐時,實現選擇排序法的時候,排出來的結果是

  • 問題1解決方案:當時的代碼是這樣的

選擇排序的邏輯確實是個人代碼所體現的那樣,可是爲何會出錯呢?
因而我就進行了單步調試,發現由於while(temp2.getNext()!=null)這行代碼是隻有temp2以後還存在元素時纔會進入循環體,這樣就有一個問題,若是temp2就是最後一個元素了,就不會在進行比較,因而發生了上面的錯誤。如今的關鍵就是要解決最後兩個數比較的位置在哪。
我先將

這段代碼放在了第二個循環體的後面,結果陷入了無限死循環;我又把他放在了第一個循環的外面,這個if條件句不會成立。最後我修改成

才解決了問題。

  • 問題2:pp9.3中要求編寫代碼來計算每次代碼執行時間,時間該如何計算呢?

  • 問題2解決方案:
    方案一:咱們要計算程序,函數的執行之間,一般是在代碼執行先後加入時間戳,二者的差值即爲執行時間,
var count=1000;
var begin=new Date();
for(var i=0;i<count;i++){
document.createElement("div");
}
var end=new Date();
var time=end-begin;
console.log("time is="+time);

方案二:咱們可使用System類的currentTimeMillis()方法來返回當前的納秒數,並保存到一個變量中,在方法執行完畢後再次調用 System的nanoTime()方法,並計算兩次調用之間的差值,就是方法執行所消耗的納秒數。由於隊友建議ms爲單位對於計算機來講時間有點長,因此獲得的可能值會爲0,因此使用ns爲單位。

long startTime = System.nanoTime(); //獲取開始時間
doSomething(); //測試的代碼段
long endTime = System.nanoTime(); //獲取結束時間
System.out.println("程序運行時間:" + (endTime - startTime) + "ns"); //輸出程序運行時間

Java計算程序代碼執行時間的方法小結

代碼託管

(statistics.sh腳本的運行結果截圖)

上週考試錯題總結

  • 錯題1及緣由,理解狀況

    java集合API包含一個索引表__的實現。由於沒有注意課本關鍵概念的總結,其實120面有答案,含有索引列表的三種實現。

結對及互評

點評:

  • 博客中值得學習的或問題:
    教材內容總結很精簡,我以爲能夠添加一下對方法的簡單的解釋。
    教材問題2,其實能夠寫在教材內容總結裏,有點缺乏圖示。但都是本身總結,進行了學習和簡化。

  • 本週結對學習狀況

點評過的同窗博客和代碼

其餘(感悟、思考等,可選)

天天都要花好幾個小時站在操場,還落了好多課,感受少了好多時間學習,並且這周的任務量也不輕,本身仍是要多抽出時間來學習。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積)
目標 5000行 30篇 400小時
第一週 0/0 1/1 10/10
第二週 326/326 1/2 18/28
第三週 784/1110 1/3 25/53
第四周 2529/3638 2/5 37/90
第五週 1254/4892 2/7 20/110
  • 計劃學習時間:28小時

  • 實際學習時間:20小時

  • 最近由於學校的活動少了一些時間來學習。

參考資料

相關文章
相關標籤/搜索