20162311 實驗三-查找與排序 實驗報告


20162311 實驗三-查找與排序 實驗報告

目錄

一、查找與排序-1

二、查找與排序-2

三、查找與排序-3

四、查找與排序-4

五、查找與排序-5

六、遇到的問題和解決辦法

七、總結

八、參考資料

一、查找與排序-1

(一) 實驗目的

(二) 實驗過程

Searching.javaSorting.java是書上已經寫好的類,只需進行junit測試就行。為了體現查找的異常情況,我在線性查找中添加了一個在數組中不存在的測試用例,二分查找則通過逆序(即降序)來體現異常情況

測試截圖

  • 線性查找正常
  • 線性查找異常
  • 二分查找正序
  • 二分查找逆序
  • 選擇排序
  • 插入排序
  • 冒泡排序
  • 快速排序
  • 歸並排序

(三) 代碼鏈接

返回目錄

二、查找與排序-2

(一) 實驗目的

(二) 實驗過程

首先把Searching.javaSorting.java放入cn.edu.besti.cs1623.zzr123包中,然后新建一個Test文件夾,把書上的兩個測試代碼放入其中,然后分別在IDEA和命令行下運行

測試截圖

  • 重構代碼
  • IDEA運行測試代碼

  • 命令行運行測試代碼

(三) 代碼鏈接

返回目錄

三、查找與排序-3

(一) 實驗目的

參考《[Data Structure & Algorithm] 七大查找算法》

(二) 實驗過程

參考的博客中給出了七大查找方法,其中順序查找和二分查找之前已經實現並測試了,我需要實現剩下的五種查找方法並測試

  1. 插值查找

插值查找類似於二分查找,只不過二分查找每次是取中點作為查找點,從而排除一半的數據,插值查找是將查找點的選擇改進為自適應選擇,即更加靠近查找對象。

 public static int insertionSearch(int[] data, int target,
                                             int min, int max)
    {
        int mid = min+(target-data[min])/(data[max]-data[min])*(max-min);
        if(data[mid] == target)
            return data[mid];
        else
        if(data[mid]>target)
            return insertionSearch(data, target, min, mid-1);
        else
            return insertionSearch(data, target, mid+1, max);
    }
  1. 斐波那契查找

也是二分查找的一種提升算法,通過運用黃金比例的概念在數列中選擇查找點進行查找,提高查找效率。同樣地,斐波那契查找也屬於一種有序查找算法。首先需要構造一個斐波那契數組

 //構造一個斐波那契數組
    private static void fibonacci(int F[])
    {
        F[0]=0;
        F[1]=1;
        for(int i=2;i<MAX_SIZE;i++)
            F[i]=F[i-1]+F[i-2];
    }

斐波那契查找與折半查找很相似,他是根據斐波那契序列的特點對有序表進行分割的。他要求開始表中記錄的個數為某個斐波那契數小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個,所以可以遞歸 的應用斐波那契查找。

/*定義斐波那契查找法*/
    //data為要查找的數組,length為數組長度,target為要找的目標
    public static int fibonacciSearch(int data[],int length,int target)
    {
       int min = 0;
       int max = length-1;
       int F[] = new int[MAX_SIZE];
       fibonacci(F);

        int k=0;
        while(length>F[k]-1)//計算length位於斐波那契數列的位置
            k++;

        int []temp;//將數組data擴展到F[k]-1的長度
        temp= Arrays.copyOf(data,F[k]-1);

        for(int i=length;i<F[k]-1;i++)
            temp[i]=data[length-1];

        while(min<=max)
        {
            int mid=min+F[k-1]-1;
            if(target<temp[mid])
            {
                max=mid-1;
                k-=1;
            }
            else if(target>temp[mid])
            {
                min=mid+1;
                k-=2;
            }
            else
            {
                if(mid<length)
                    return data[mid]; //若相等則說明mid即為查找到的位置
                else
                    return data[length-1]; //若mid>=n則說明是擴展的數值,返回n-1
            }
        }
        return -1;//未查找到
    }
  1. 數表查找

我這里的數表查找是用的之前實現的二叉查找樹實現的,先構造一顆二叉查找樹,然后將要查找的數組中的元素依次添加到二叉查找樹中,再調用其中的find方法即可

 //數表查找,利用二叉查找樹實現
    public static Comparable treeSearch(int []data,int target)
    {
        LinkedBinarySearchTree<Integer> bsTree = new LinkedBinarySearchTree<>();
        for(int i=0;i<data.length;i++){
            bsTree.add(data[i]);
        }

        return bsTree.find(target);
    }
  1. 分塊查找

分塊查找又稱索引順序查找,它是順序查找的一種改進方法。
 詳情參考《數據結構Java版的查找算法實現》

(1)算法思想:

將n個數據元素"按塊有序"划分為m塊(m ≤ n)。每一塊中的結點不必有序,但塊與塊之間必須"按塊有序";即第1塊中任一元素的關鍵字都必須小於第2塊中任一元素的關鍵字;而第2塊中任一元素又都必須小於第3塊中的任一元素,……
 

(2)算法流程:
  

  • step1 先選取各塊中的最大關鍵字構成一個索引表;
  • step2 查找分兩個部分:先對索引表進行二分查找或順序查找,以確定待查記錄在哪一塊中;然后,在已確定的塊中用順序法進行查找。
/**
     * 分塊查找
     * 采用數組保存區塊的極值和起始下標
     * @param data 查找數組
     * @param n 分塊個數
     */
    public static int blockSearch(int []data,int target,int n) {
        HashMap<Integer, Integer> block = new HashMap<>();
        int tem = data[0];
        for (int i = 0; i < data.length - 1; i++) {
            if (i % n == 0) { //起始分塊
                if ((i + n) >= data.length - 1) { //判斷是否是最后一個區塊 最后一個區塊元素可能小於或大於前面區塊元素
                    for (int j = i; j < data.length; j++) { //區塊內查找極值
                        if (data[j] > tem) {
                            tem = data[j];
                        }
                    }
                    //保存區塊極值和起始下標
                    block.put(tem, i);
                } else {
                    for (int j = i; j < i + n; j++) { //區塊內查找極值
                        if (data[j] > tem) {
                            tem = data[j];
                        }
                    }
                    //保存區塊極值和起始下標
                    block.put(tem, i);
                    //初始化區塊比較值
                    tem = data[i + n - 1];
                }
            }
        }

        //獲取索引極值進行排序
        Iterator<Integer> ite = block.keySet().iterator();
        int[] index1 = new int[block.size()];
        int i = 0;
        while (ite.hasNext()){
            index1[i++] = ite.next();
        }
        //索引極值從小到大排序
        Arrays.sort(index1);
        //查找元素所在區塊
        for (int j = 0; j < index1.length; j++) {
            if (target <= index1[j]){ //小於索引值說明在此區塊內進行查找
                int start = block.get(index1[j]);
                int end = 0;
                if (j != index1.length -1){
                    end = block.get(index1[j+1]);
                }else {
                    end = data.length;
                }
                //查找區塊元素位置
                for (int k = start; k < end; k++) {
                    if (target == data[k]){
                        return data[k];
                    }
                }
            }
        }
        return -1;//找不到返回-1
    }
  1. 哈希查找

構造一個HashMap,把查找數組中的元素作為value,它的hash值作為key,每次查找通過計算目標的hash值直接找到目標

 //哈希查找
    public static int hashSearch(int data[],int target)
    {
       HashMap<Integer,Integer> hashMap = new HashMap<>();
       for(int i=0;i<data.length;i++)
           hashMap.put(Integer.hashCode(data[i]),data[i]);

       int key = Integer.hashCode(target);
       if(hashMap.containsKey(key))
           return hashMap.get(key);

       return -1;//找不到返回-1
    }

6.測試截圖

(三) 代碼鏈接

返回目錄

四、查找與排序-4

(一) 實驗目的

(二) 實驗過程

依次實現四種排序方法

  1. 希爾排序

希爾排序是插入排序的一種,其基本原理是,現將待排序的數組元素分成多個子序列,使得每個子序列的元素個數相對較少,然后對各個子序列分別進行直接插入排序,待整個待排序列“基本有序”后,最后在對所有元素進行一次直接插入排序。詳情可參考《【排序算法】希爾排序原理及Java實現》

 //希爾排序
    public static void shellSort(Comparable []data)
    {
        Comparable temp;
        int dataLength = data.length / 2;
        int pointer;
        while (dataLength != 0) {
            for (int i = dataLength; i < data.length; i++) {
                temp = data[i];
                pointer = i - dataLength;
                while (pointer >= 0 && temp.compareTo(data[pointer])<0) {
                    data[pointer + dataLength] = data[pointer];
                    pointer -= dataLength;
                    if (pointer > data.length) {
                        break;
                    }
                    data[pointer + dataLength] = temp;
                }
            }
            dataLength /= 2;
        }
    }
  1. 堆排序

堆排序的實現可利用教材上的最大堆,先構造一個最大堆,然后將要排序的數組依次放入堆中,再依次取出,只不過得到的是降序排列,只需將第一個取出的元素放在數組最后位置接下來依次類推,即可得到升序排列

 //堆排序
    public static void heapSort(int []data)
    {
        LinkedMaxHeap<Integer> heap = new LinkedMaxHeap<>();
        int length = data.length;
        for(int i=0;i<length;i++)
            heap.add(data[i]);

        for(int j=0;j<length;j++)
            data[length-1-j] = heap.removeMax();
    }
  1. 桶排序

桶排序是一種以空間換時間的排序方法,桶排序的基本思想是:把數組 arr 划分為n個大小相同子區間(桶),每個子區間各自排序,最后合並。
計數排序是桶排序的一種特殊情況,可以把計數排序當成每個桶里只有一個元素的情況。詳情參考《計數排序和桶排序(Java實現)》

(1)找出待排序數組中的最大值max、最小值min

(2)我們使用 動態數組ArrayList 作為桶,桶里放的元素也用 ArrayList 存儲。桶的數量為(max-min)/arr.length+1

(3)遍歷數組 arr,計算每個元素 arr[i] 放的桶

(4)每個桶各自排序

(5)遍歷桶數組,把排序好的元素放進輸出數組

 //桶排序
    public static void bucketSort(int []arr) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
            min = Math.min(min, arr[i]);
        }

        //桶數
        int bucketNum = (max - min) / arr.length + 1;
        ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
        for (int i = 0; i < bucketNum; i++) {
            bucketArr.add(new ArrayList<Integer>());
        }

        //將每個元素放入桶
        for (int i = 0; i < arr.length; i++) {
            int num = (arr[i] - min) / (arr.length);
            bucketArr.get(num).add(arr[i]);
        }

        //對每個桶進行排序
        for (int i = 0; i < bucketArr.size(); i++) {
            Collections.sort(bucketArr.get(i));
        }

        int index = 0;
        for (int i = 0; i < bucketNum; i++)
        {
            for (int j = 0; j < bucketArr.get(i).size(); j++)
            {
                arr[index] = bucketArr.get(i).get(j);
                index++;
            }
        }
    }
  1. 二叉樹排序

利用二叉查找樹進行排序,二叉查找樹的中序遍歷就是樹中元素的升序排列,所以我先構造一顆二叉查找樹,然后把元素添加進去,得到中序遍歷,在依次放入數組中即可

//二叉樹排序
    public static void binaryTreeSort(int []data)
    {
        LinkedBinarySearchTree<Integer> bsTree = new LinkedBinarySearchTree<>();
        int length = data.length;
        for(int i=0;i<length;i++)
            bsTree.add(data[i]);

        ArrayList<Integer> list = (ArrayList<Integer>) bsTree.inorder();
        for(int j=0;j<length;j++)
            data[j] = list.remove(0);
    }

5.測試截圖

(三) 代碼鏈接

返回目錄

五、查找與排序-5

(一) 實驗目的

(二) 實驗過程

課上沒完成,課后補博客,詳見博客《20162311 編寫Android程序測試查找排序算法》

返回目錄

六、遇到的問題和解決辦法

  • 問題1:做第二個實驗時,用命令行編譯始終無法通過,明明已經導入相關的包,但總是找不到一些類
  • 解決辦法:問王老師,老師看了代碼也說沒問題,找不出什么毛病,后來用了同學的電腦上的Linux bash,clone了我的項目再到命令行下編譯運行才成功

返回目錄

七、總結

本次的實驗主要目的是加強對排序和查找算法的理解,之前已經學過一些算法,也做過相關測試,但都是書上實現好的,這次是要自己補充實現一些書上沒有的查找和排序的算法。通過自己去實現,我們能更好的理解和掌握這些算法,而不是僅僅停留在使用的層面上

返回目錄

八、參考資料

返回目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM