20172303 2018-2019-1 《程序設計與數據結構》實驗三報告

20172303 2018-2019-1 《程序設計與數據結構》實驗三報告

  • 課程:《程序設計與數據結構》
  • 班級: 1723
  • 姓名: 範雯琪
  • 學號:20172303
  • 實驗教師:王志強
  • 助教:張師瑜/張之睿
  • 實驗日期:2018年11月19日
  • 必修/選修: 必修

實驗內容

節點一

  • 定義一個SearchingSorting類,並在類中實現LinearSearch(教材P162),SelectionSort方法(P169),最後完成測試。
  • 要求很多於10個測試用例,提交測試用例設計狀況(正常,異常,邊界,正序,逆序),用例數據中要包含本身學號的後四位
    提交運行結果圖。

節點二

  • 重構你的代碼,把Sorting.java Searching.java放入cn.edu.besti.cs1723.(姓名首字母+四位學號)包中(例如:cn.edu.besti.cs1723.G2301)
  • 把測試代碼放test包中,從新編譯,運行代碼,提交編譯,運行的截圖(IDEA,命令行兩種)

節點三

  • 參考七大查找算法在Searching中補充查找算法並測試,提交運行結果截圖。

節點四

  • 補充實現課上講過的排序方法:希爾排序,堆排序,二叉樹排序等(至少3個)
  • 測試實現的算法(正常,異常,邊界)
  • 提交運行結果截圖
  • (3分,若是編寫多個排序算法,即便其中三個排序程序有瑕疵,也能夠酌情得滿分)

節點五(選作)

  • 編寫Android程序對各類查找與排序算法進行測試。
  • 提交運行結果截圖。
  • 推送代碼到碼雲。

實驗過程及結果

節點一

Searching類——LinearSearch

  • 概念:在一個元素類型相同的項目組中,從頭開始依次比較每個值直至結尾,若找到指定元素返回索引值,不然返回false。
  • 此處LinearSearch方法的實現用的是教材162頁的方法,回去翻博客20172303 2018-2019-1《程序設計與數據結構》第5周學習總結發現當時沒有進行代碼分析,在這裏補上。
  • 代碼分析:課本上實現的linearSearch方法要求用戶輸入所查找的數組、數組中的最小索引值(一般爲0)、最大索引值和所要查找的元素。初始時設定一個boolean值爲false,使用一個while循環對數組從頭到尾進行遍歷,將數組中的每個元素與目標查找元素target進行比較,當比較的結果爲true時,將true賦值給found輸出,不然返回false。
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;
    }
  • JUnit測試:在正常測試中,我使用了int和String兩種數據類型共測試了四組,其中每一組都測試了兩種狀況:元素在查找的範圍內(包括正常狀況和邊界狀況)和元素不在所查找的範圍內。在異常測試中,測試了兩種異常錯誤,一是聲明異常(即應該輸入的信息和實際輸入的信息不符),二是空指針異常(即NullPointerException錯誤)
    • 正常測試
    • 錯誤測試

Sorting類——SelectionSort

  • SelectionSort方法的正常測試中有三種:正序測試、逆序測試和亂序測試。在異常測試中也測試了兩種錯誤,一是空指針異常(即NullPointerException錯誤),二是越界異常(即查找的數組索引值超出實際數組的索引值範圍)
    • 正常測試
    • 錯誤測試

節點二

在IDEA中重構

  • 在IDEA中重構比較簡單,只要從新按命名要求建一個包,而後將相應的文件複製粘貼到新的包中再運行便可。
  • Searching類測試
  • Sorting類測試

在命令行在重構

  • 在命令行中重構的重點是要熟悉Linux命令行和vi編輯器命令行,通過將近一年基本都忘的差很少了,再次複習一下一些本次實驗中用到的命令行:
  • Linux
命令 用途
cd 切換目錄,其中cd.或cd~是回到上一級目錄,cd..是返回根目錄
ls 列出文件夾內容
mkdir 建立目錄
mv file1 file2 移動file1到file2
rm 刪除文件
man command 查看命令的參考手冊
vi file.java 建立名爲file的java文件
javac file.java 建立名爲file的class文件
java file 運行java文件
  • vi編輯器
操做 用途
i 進入插入模式
wq 保存並退出
q 強制退出
  • 只要掌握命令行在Linux中重構就也很簡單了,由於在Linux中使用JUnit的話還須要下載,這裏我就直接用測試類來進行測試了。
  • Searching類測試
  • Sorting類測試

節點三

  • 在題目給出的博客中我選擇了三種查找方法:二分查找、插值查找、斐波那契查找。

二分查找

  • 概念:也稱折半查找,在一個已排序的項目組中,從列表的中間開始查找,若是中間元素不是要找的指定元素,則削減一半查找池,從剩餘一半的查找池(可行候選項)中繼續以與以前相同的方式進行查找,屢次循環直至找到目標元素或肯定目標元素不在查找池中。
  • 其實這個查找方法在書上也有給出相應的方法,博客中的方法與書上的方法的不一樣之處在於書上的方法使用的是遞歸,而博客中的方法使用的是循環,其他的原理等方面都是同樣的。
// 二分查找(書上的方法)
 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;
    }
//  二分查找(博客中的方法)
public static Comparable binarySearch(Comparable[] data, Comparable target) {
    Comparable result = null;
    int first = 0, last = data.length - 1, mid;

    while (result == null && first <= last) {
        mid = (first + last) / 2;
        if (data[mid].compareTo(target) == 0) {
            result = data[mid];
        } else if (data[mid].compareTo(target) > 0) {
            last = mid - 1;
        } else {
            first = mid + 1;
        }
    }
    return result;
}

插值查找

  • 概念:插值查找的算法與二分查找相似,它基於二分查找算法,將查找點的選擇改進爲自適應選擇,能夠提升查找效率。
    • 二分查找:mid = low + 1/2 * (high - low)
    • 插值查找:mid = low + (key - a[low])/(a[high] - a[low])* (high - low)
  • 對於查找元素較多,且關鍵字分佈較均勻時,使用插值查找將比二分查找的效率快不少。插值查找的時間複雜度爲O(logn)
// 插值查找
public static int InsertionSearch(int[] a, int value, int low, int high) {
    int mid = low + (value - a[low]) / (a[high] - a[low]) * (high - low);
    if (a[mid] == value) {
        return mid;
    }
    if (a[mid] > value) {
        return InsertionSearch(a, value, low, mid - 1);
    } else {
        return InsertionSearch(a, value, mid + 1, high);
    }
}

斐波那契查找

  • 想要弄清斐波那契查找,首先先要搞清楚斐波那契數列。
  • 斐波那契數列:又稱黃金分割數列,指的是這樣一個數列:0、一、一、二、三、五、八、1三、2一、34.....在數學上,斐波納契數列以以下被以遞歸的方法定義:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)
  • 斐波那契查找:斐波那契查找一樣是基於二分查找實現的,只不過它是根據斐波那契數列的特色對數組進行劃分,將key值與temp[mid](mid = low + Fibonacci(k - 1) - 1)進行比較,分爲三種狀況:當key=temp[mid]時,mid即爲查找元素在數組中的索引值;當key>temp[mid]時,在temp數組的左邊繼續進行查找;當key<temp[mid]時,在temp數組的右邊繼續進行查找。
// 斐波那契查找
// 使用遞歸創建斐波那契數列
public static int Fibonacci(int n) {
    if(n == 0) {
        return 0;
    }
    if(n == 1) {
        return 1;
    }
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}

public static int FibonacciSearch(int[] data,int n,int key) {
    int low = 1;
    int high = n;
    int mid;

    // 尋找k值,k值要求k值能使得F[k]-1剛好大於或等於n
    int k = 0;
    while(n > Fibonacci(k) - 1)
    {
        k++;
    }
    //由於沒法直接對原數組增長長度,因此定義一個新的數組
    //採用System.arraycopy()進行數組間的賦值
    int[] temp = new int[Fibonacci(k)];
    System.arraycopy(data, 0, temp, 0, data.length);
    //對數組中新增的位置進行賦值
    for(int i = n + 1;i <= Fibonacci(k) - 1;i++)
    {
        temp[i]=temp[n];
    }

    while(low <= high) {
        mid = low + Fibonacci(k - 1) - 1;
        // 在temp的左邊進行查找
        if(temp[mid] > key) {
            high = mid - 1;
            k = k - 1;
        }
        // 在temp的右邊進行查找
        else if(temp[mid] < key) {
            low = mid + 1;
            k = k - 2;
        }else {
            if(mid <= n) {
                return mid;
            }
            //當mid位於新增的數組中時,返回n
            else {
                return n;
            }
        }
    }
    return 0;
}

測試結果

節點四

  • 節點四的要求是再實現幾種排序方法,我一共實現了七種排序算法:插入排序、冒泡排序、歸併排序、快速排序、堆排序、希爾排序和二叉樹排序。其中前五種排序都是書上直接實現的(堆排序在第十二章,其他在第九章),主要說一下希爾排序和二叉樹排序的實現。

希爾排序

  • 概念:希爾排序是簡單插入排序的改進版。它與插入排序的不一樣之處在於,它會優先比較距離較遠的元素,希爾排序又叫縮小增量排序。
  • 代碼分析:希爾排序的核心在於間隔序列的設定。既能夠提早設定好間隔序列,也能夠動態的定義間隔序列。我在實現的過程當中設置的是動態定義間隔,每次循環時新的間隔等於原間隔的通常。
public static void ShellSort(int[] data, int length){
    int i = length;
    while (i > 1){
        // 動態定義間隔
        i = (i + 1) / 2;
        for (int j = 0;j < length - i;j++){
            // 當對應比較的兩個數中前者大於後者時進行交換
            if (data[j + i] < data[j]){
                int temp = data[j + i];
                data[j + i] = data[j];
                data[j] = temp;
            }
        }
    }
}

二叉樹查找

  • 概念:二叉樹查找的實際實際上是使用二叉查找樹來實現排序。
  • 代碼分析:實現的方法是將目標數組中的數打亂順序隨機添加到二叉查找樹中,以後將樹中的元素按照層序遍歷的順序輸出造成一個新的數組,將新數組的元素與原數組的元素進行比較。
public void testBinaryTreeSort(){
    String list[] = {"1","3","6","13","17","20","23","81","99","2303"};
    LinkedBinarySearchTree tree = new LinkedBinarySearchTree();
    tree.addElement(2303);
    tree.addElement(99);
    tree.addElement(81);
    tree.addElement(23);
    tree.addElement(20);
    tree.addElement(17);
    tree.addElement(13);
    tree.addElement(6);
    tree.addElement(3);
    tree.addElement(1);
    int a = tree.size();
    String[] b = new String[a];
    for (int i = 0;i < a;i++){
        b[i] = String.valueOf(tree.findMin());
        tree.removeMin();
    }
    // 正常測試+邊界測試
    assertEquals(b[0],list[0]);
}

測試結果

  • 正常測試
  • 異常測試

節點五(選作)

  • 剛開始在AS裏實現查找和排序時由於沒有想好,因此就直接設定了一個數組進行測試,可是後來作完實驗以後想到了其實可讓用戶輸入一系列數字的字符串再用StringTokenizer或者split進行拆分便可。
  • 我設置了兩個Activity,第一個Activity中實現查找,第二個Activity中實現排序。界面的跳轉代碼是關鍵。
button3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        startActivity(intent);
    }
});
  • 這個測試的截圖比較多,這裏查找和排序各放一個,剩下的能夠到藍墨雲班課裏去看。

實驗過程當中遇到的問題和解決過程

  • 問題1:JUnit的使用方法?
  • 問題1解決狀況:將近一個學期沒有用過JUnit了,這回從新記起來以後以爲以防以後再次出現相同狀況,仍是記錄一下比較好。
    • Step1:選擇要進行JUnit測試的類,在類名旁會出現一個小燈泡,點擊燈泡後選擇Create Test
    • Step2:在彈出的界面選擇Junit4而後點擊OK。
    • Step3:而後就建立了一個JUnit的測試類,以後在其中進行正常測試就好,要注意的是測試名中必定要含有test,能夠直接編寫正常的測試類,也能夠用一些經常使用的JUnit的斷言介紹進行測試:
      • 用於值判斷:assertEquals(String msg, Object expectRes, Object Res)
      • 用於地址判斷:assertSame(String msg, Object expectRes, Object Res)
      • 用於Boolean判斷:assertTrue(String msg,Boolean result)
      • 用於判斷result是否爲NULL:assertNull(String msg,Object result)
      • 用於地址判斷:assertSame(String msg, Object expectRes, Object Res)
  • 問題2:在剛剛開始進行JUnit測試時,顯示「assertEquals
  • 問題2解決狀況:當時顯示的問題是「您使用的方法已過期」,不是很明白爲何,上網查了半天也沒有查到解決方法,最後在極爲暴躁的狀況下詢問告終對夥伴張昊然同窗,他說是由於我沒有extend TestCase,在嘗試以後發現真的沒有透印了,就很開心地解決了這個問題並收穫告終對夥伴的嘲笑

其餘(感悟、思考等)

  • 本次實驗和上回實驗的感受差很少,難度並不大,可是須要從新撿起不少東西,在本次實驗中又複習了不少知識,開心!

參考資料

相關文章
相關標籤/搜索