1. 程式人生 > >20172304 實驗三報告

20172304 實驗三報告

20172304 實驗二報告

  • 課程:《軟體結構與資料結構》
  • 班級: 1723
  • 姓名: 段志軒
  • 學號:20172304
  • 實驗教師:王志強
  • 助教:張師瑜&張之睿
  • 實驗日期:2018年11月5日-2018年11月12日
  • 必修選修: 必修

    實驗要求

    實驗三-查詢與排序-1
    定義一個Searching和Sorting類,並在類中實現linearSearch(教材P162 ),SelectionSort方法(P169),最後完成測試。要求不少於10個測試用例,提交測試用例設計情況(正常,異常,邊界,正序,逆序),用例資料中要包含自己學號的後四位提交執行結果圖。
    實驗三-查詢與排序-2
    重構你的程式碼
    把Sorting.java Searching.java放入 cn.edu.besti.cs1723.(姓名首字母+四位學號) 包中(例如:cn.edu.besti.cs1723.G2301)把測試程式碼放test包中重新編譯,執行程式碼,提交編譯,執行的截圖(IDEA,命令列兩種)

實驗三-查詢與排序-3
參考http://www.cnblogs.com/maybe2030/p/4715035.html 在Searching中補充查詢演算法並測試提交執行結果截圖
實驗三-查詢與排序-4
補充實現課上講過的排序方法:希爾排序,堆排序,二叉樹排序等(至少3個)
測試實現的演算法(正常,異常,邊界)
提交執行結果截圖
實驗三-查詢與排序-5(選做,加分)
編寫Android程式對各種查詢與排序演算法進行測試
提交執行結果截圖

實驗過程及實驗結果

實驗一過程及結果:
本次實驗主要內容是對之前已經學習過的順序查詢和選擇排序進行測試,實際上就是讓我們再次熟悉一下junit測試的相關內容以及複習一下有關順序查詢和選擇排序的相關內容。但是由於後續實驗的內容都是在Sorting類以及Searching類的基礎上繼續改進的,所以在此我就不講相關的碼雲連結給出,而只給出順序查詢以及選擇排序的相關程式碼。
順序查詢

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

選擇排序

public static <T extends Comparable<T>> T[] selectionSort(T[] data)
    {
        int min;
        T temp;

        for (int index = 0; index < data.length-1; index++)
        {
            min = index;
            for (int scan = index+1; scan < data.length; scan++)
                if (data[scan].compareTo(data[min])<0)
                    min = scan;

            swap(data, min, index);
        }
        return data;
        }
private static <T extends Comparable<? super T>> void swap(T[] data, int index1, int index2)
        {
            T temp = data[index1];
            data[index1] = data[index2];
            data[index2] = temp;
        }

然後將相關的junit測試截圖發過來
這是順序查詢的圖片


這是選擇排序的圖片

說明:由於要求至少十個測試用例,所以我將兩個方法的測試的每種情況(正常,異常,邊界,正序,逆序)在查詢的測試中,雖然我不知道測試邊界究竟有什麼用,但是我還是查找了兩個位於給定陣列邊界的兩個元素,在設定異常的時候,考慮到實現的順序查詢的是需要給定的查詢範圍的,於是就給定了一個超過給定陣列範圍的範圍,並且為他大度劃分出了一個測試的空間,最後不出意外的出現了異常,丟擲了ArrayIndexOutOfBoundsException,也就是我們認知意義上的陣列越界異常。
在處理選擇排序的時候,雖然還是不理解測試一個正序陣列道德排序究竟有何意義,但是還是做了,並且為了滿足10個用例的要求,最終也是每個情況做了多組資料的測試,但是考慮到排序演算法似乎並沒有所謂的什麼邊界,所以最終就沒有做這方面的測試,在進行設定異常的時候。首先聲明瞭一個Comparable陣列,這個陣列有個很神奇的地方就是它能夠同時容納不同型別的資料,所以我就在這個陣列中儲存了整數型的資料和文字型也就是String型別的資料。然後在進行排序的時候理所當然的就會出現異常。最終丟擲的異常是ClassCastException: java.lang.Integer cannot be cast to java.lang.String,也就是型別轉換異常:整數型變數不能轉換為文字型變數。

實驗二過程與結果
重構你的程式碼
把Sorting.java Searching.java放入 cn.edu.besti.cs1723.(姓名首字母+四位學號) 包中(例如:cn.edu.besti.cs1723.G2301)把測試程式碼放test包中重新編譯,執行程式碼,提交編譯,執行的截圖(IDEA,命令列兩種)
實驗二的idea部分實際上並沒有什麼難度,只是換了一個位置,然後重新進行測試就是了。最後的結果和實驗一雷同,於是就不在此放出圖片了。然後就是命令列部分的實現了,出於嚴謹的態度,我請示了一下王老師,命令列模式是否也要在junit中進行測試,在得到肯定回答後就是噩夢的開始。首先上網上查資料。查到了首先應該是匯入一個外掛,也就是所謂的字尾為.jar的東西,下載之後就是匯入的過程了。首先是找到匯入位置,可能是太久沒有人有勇氣嘗試在命令列中進行junit測試了,網上給定的教程的版本不知落後了我的虛擬機器版本不知道多少輩子子孫孫。終於在費盡九牛二虎之力後,終於找到疑似目標之後,卻又被許可權困住了。還曾記得老師曾經教過我們,我們所學的是面向物件的設計是頂層設計,但是沒想到自己最後卻敗在底層結構上了。圖形介面沒許可權,命令列一般的移動命令mv似乎也不能識.jar這個東西。系統給我的提示是系統並不認為這是個檔案,當時我就崩了,內心中猶如滔滔江水,氾濫而過,一發不可收拾。最終在掙扎與無奈中,伴隨著12點鐘聲的響起,只能與我的實驗二的另一半分數作別。別了,實驗二的另一半。曾經有一份分數擺在我面前,我沒有珍惜,如果上天能再給我一次機會,我會對他說:“junit還是idea的好。”
實驗三過程與結果
決策樹,這個的實現是基於書上的程式碼,這個實際上也沒有也沒有太大的難度
DecisionTree碼雲連結
CharacterAnalyzer
這是我設計的決策樹

測試結果

實驗三
這個實驗主要就是要求我們閱讀老師給定的部落格,然後將相關演算法補充完整,實際上,這更加類似於,將C++翻譯成java的過程。除了實驗一中已經使用過的順序查詢,這篇部落格中還給出了二分查詢,插值查詢,斐波那契查詢,平衡查詢樹,分塊查詢,雜湊查詢。處於治學嚴謹的態度,首先我要介紹著幾種演算法的原理。。
首先是順序查詢。
順序查詢顧名思義,就是按照順序查詢,就是從頭開始依次向下進行。
然後二分查詢是建立在已經排序好的陣列的基礎上的。首先由首尾兩個資料算出將要作為標準的中間值,然後將查詢目標與中間值進行比較。如果相等就返回true。如果小於中間值。則取中間值左面的陣列,如果大於則反之。最後如果找到元素返回true,否則返回false。
下面是有關兩個查詢演算法的動圖

然後是插值查詢,
其實插值查詢時類似於二分查詢的下面的公式中的第一行就是二分查詢的公式,第二行是插值查詢的公式。其中mid就是每次所確定的要與目標進行比較的值。而low和high就是每次重新劃分完區域之後重新確定的最大值和最小值。可以看出插值查詢通過改進演算法,使得改進後的演算法在面對分佈較為均勻的資料時,有較好的效率。
mid=(low+high)/2, 即mid=low+1/2(high-low);
mid=low+(key-a[low])/(a[high]-a[low])
(high-low),
插值查詢示意圖

然後是斐波那契查詢
相對於折半查詢,一般將待比較的key值與第mid=(low+high)/2位置的元素比較,比較結果分三種情況:
1)相等,mid位置的元素即為所求
2)>,low=mid+1;
3)<,high=mid-1。 斐波那契查詢與折半查詢很相似,他是根據斐波那契序列的特點對有序表進行分割的。他要求開始表中記錄的個數為某個斐波那契數小1,及n=F(k)-1;
開始將k值與第F(k-1)位置的記錄進行比較(及mid=low+F(k-1)-1),比較結果也分為三種
1)相等,mid位置的元素即為所求
2)>,low=mid+1,k-=2;
說明:low=mid+1說明待查詢的元素在[mid+1,high]範圍內,k-=2 說明範圍[mid+1,high]內的元素個數為n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1個,所以可以遞迴的應用斐波那契查詢。
3)<,high=mid-1,k-=1。
說明:low=mid+1說明待查詢的元素在[low,mid-1]範圍內,k-=1 說明範圍[low,mid-1]內的元素個數為F(k-1)-1個,所以可以遞迴 的應用斐波那契查詢。
斐波那契查詢動態圖

平衡查詢樹
這個實際上就是利用構建好的平衡查詢樹的性質來進行查詢元素。平衡查詢樹構建要求右子樹大於左子樹。左子樹小於右子樹。且左右兩子樹高度差絕對值不超過一。
平衡查詢樹動態圖展示。

分塊查詢
要求將資料分成塊,並且前一塊的任一元素都不能大於後面塊中的元素。所以這個演算法應該是建立在資料已經排好序的的基礎上道德。然後將每一塊的最大元素取出然後建立一個索引列表。在將目標元素與索引列表中的元素進行比較,最後找到目標元素所在的分塊,最後再次使用順序或者二分查詢進行查詢,然後根據查詢結果分別返回true或false。
分塊查詢圖片展示

雜湊查詢
首先得將代查資料按照雜湊規則存入雜湊表中。所謂雜湊規則就是將資料通過某種函式,來確定他在表中的位置。例如資料b=a%11,其中a表示資料,b表示資料在表中的位置,但是有時資料之間會產生衝突。例如。1%11=1,12%11=1,這時兩個不同的資料卻通過函式產生了相同的地址,這就是所謂的衝突。解決雜湊衝突的辦法有很多,但是其中最常用的就是鏈地址法。就是在陣列中儲存的並不是一個個具體的資料,而是連結串列,一旦發生衝突,就將資料插入到連結串列的末尾。在查詢的時候也是遵循同樣的規則的,首先將待查元素通過函式求得其目標位置,然後,在遍歷連結串列,如果找到就返回true,如果沒有找到就返回false。
實現程式碼。由於程式碼太多所以就將其置於下面連結之中。
Searching
測試類程式碼

package cn.edu.besti.cs1723.DZX2304;



public class Searchingtest {
    public static void main(String[] args) {
    Integer[] a={10,2,8,19,16,18,20,51,};//被查詢的陣列
       Integer []a1={2,8,10,16,18,19,20,51};
        Searching b=new Searching();


        System.out.println("線性查詢查詢50的結果:" + Searching.linearSearch(a,0,7,50));
        System.out.println("線性查詢查詢2的結果:"+Searching.linearSearch(a,0,7,2));
        System.out.println("二分查詢50的結果:" + b.BinarySearch1(a1,50,7));
        System.out.println("二分查詢2的結果:" + b.BinarySearch1(a1,2,7));
       System.out.println("插值查詢能否找到50:" + b.InterpolationSearch(a1, 50));
        System.out.println("插值查詢能否找到2:" + b.InterpolationSearch(a1,2));
        System.out.println("斐波那契查詢能否找到50:" + Searching.FibonacciSearch(a1,0,11, 50));
        System.out.println("斐波那契查詢能否找到2:" + Searching.FibonacciSearch(a1,0,11, 2));
        System.out.println("二叉查詢樹查詢能否找到50:" + b.BinarySearchTreeSearch(a,0,7,50));
        System.out.println("二叉查詢樹查詢能否找到2:" + b.BinarySearchTreeSearch(a,0,7,2));
        System.out.println("分塊查詢能否找到50:" + Searching.partitionSearch(a,0,7,50));
        System.out.println("分塊查詢能否找到2:" + Searching.partitionSearch(a,0,7,2));
        System.out.println("雜湊表查詢50的結果" +b.Hashsearching(a,50));
        System.out.println("雜湊表查詢2的結果"+b.Hashsearching(a,2));
    }}

測試結果截圖

實驗四
這個實驗主要是補充老師曾經講過的排序演算法,什麼希爾排序堆排序以及二叉樹排序,自己也可以實現排序演算法。除了這兩個排序演算法之外,我還實現了插入排序和快速排序。
首先向大家介紹一下這幾種演算法。
首先是希爾排序,希爾排序的具體演算法應用就是首先確定一個步長,從第一個元素開始,比較間隔步長-1個元素的元素,如果不符合與其的排序規則就交換兩個元素。然後逐漸減少步長直至最後步長小於1為之。
希爾排序動態示意圖

堆排序講解
堆排序是一種比較複雜的排序,它是在二叉平衡樹的基礎上實現的。對分為兩種,一種是大頂堆,大頂堆的所有子樹的父結點都不小於其孩子結點。而小頂堆的所有子樹的父結點都不大於他的孩子結點。我就簡單介紹一下使用對進行排序的部分,而具體生成堆的部分我就不再贅言了。主要說一下使用堆進行排序的部分。以大頂堆為例。由於大頂堆的性質,導致最大的元素就是根節點,於是將根節點取出,然後將第一個非葉子結點取出,置為根節點。然後再次生成堆。依此類推。
堆排序動態展示

二叉樹排序講解
實際上二叉樹排序實際上就是生成二叉查詢樹然後通過中序遍歷就會得到排好序的資料。
二叉查詢樹生成動態示意圖

插入排序講解
實際上插入排序就是從頭開始遍歷,將未排序的元素與已排序的元素逐個進行比較然後,直接將其插入到適合的位置。

插入排序示意圖

快速排序
從數列中挑出一個元素,稱為 “基準”(pivot);
重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作;
遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
快速排序動態展示

實現程式碼。由於程式碼太多所以就將其置於下面連結之中。
Sorting.java
測試類程式碼

package cn.edu.besti.cs1723.DZX2304;

public class SortingTest {
    public static void main(String[] args) {
        Comparable a[] = {10, 25, 2304,  44, 56, 11, 9, 8};
        Sorting b = new Sorting();
       Sorting.selectionSort(a);
        System.out.println("選擇排序後的陣列");
        for(Comparable c:a)
        {
            System.out.print(c+" ");
        }
        System.out.println();
        Comparable a2[]= {10, 25, 2304,  44, 56, 11, 9, 8};
         Sorting.insertionSort(a2);
        System.out.println("插入排序後的陣列");
        for(Comparable c:a2)
        {
            System.out.print(c+" ");
        }
        System.out.println();
        Comparable a3[]= {10, 25, 2304,  44, 56, 11, 9, 8};
        Sorting.quickSort(a3);
        System.out.println("快速排序後的陣列");
        for(Comparable c:a3)
        {
            System.out.print(c+" ");
        }
        System.out.println();
        Comparable a4[]= {10, 25, 2304,  44, 56, 11, 9, 8};
        Sorting.shellSort(a4);
        System.out.println("希爾排序後的陣列");
        for(Comparable c:a4)
        {
            System.out.print(c+" ");
        }
        System.out.println();
        Comparable a5[]= {10, 25, 2304,  44, 56, 11, 9, 8};
        Sorting.heapSort(a5);
        System.out.println("堆排序後的陣列");
        for(Comparable c:a5)
        {
            System.out.print(c+" ");
        }
        System.out.println();
        Comparable a6[]= {10, 25, 2304,  44, 56, 11, 9, 8};
        Sorting.binarySearchTreeSort(a6);
        System.out.println("二叉查詢樹排序後的陣列");
        for(Comparable c:a6)
        {
            System.out.print(c+" ");
        }
        Comparable[] a7={10, "a", 2304,  44, "b", 11, 9, 8};
        Sorting.binarySearchTreeSort(a7);
        System.out.println("二叉查詢樹排序後的陣列");
        for(Comparable c:a7)
        {
            System.out.print(c+" ");
        }
    }
}

測試結果截圖

這裡我放置了一組異常對照。
實驗五
實驗五是真的沒什麼好說的了,就是將程式碼在android上實現

程式碼除錯時遇見的問題

問題:為什麼斐波那契查詢的結果和預期結果不一致
解決方案:後來發現是在進行斐波那契查詢時沒有使用已經排序好的陣列。

其他

 這次實驗又是一次複習實驗,經過多次的磨練相信我們對各種短髮的理解會更加深入,對各種程式碼的運用會更加熟練。我們會不斷地成長進步直至自己能開拓出一片屬於自己的天空。

參考資料

1.藍墨雲班課
2.java軟體結構與資料結構