Java實現基本的排序算法

排序算法的基本分類

 1.插入排序

插入排序 時間複雜度是O(n^2) 不穩定
插入排序介紹:對於欲排序的元素以插入的方式尋找該元素的適當位置,以達到排序的目的
* 插入排序思想:把n個待排序的元素看成未一個有序表和一個無序表,開始時有序表中只包含一個元素,
*              無序表中取出第一個元素,把它的排序碼一次與有序表中的排序碼進行比較,
*              將它插入到有序表中的適當位置,使之成爲新的有序表
* 插入排序的缺點:當需要插入的數是較小的數時,數據向後移動的次數明顯增多
public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int insertValue = array[i];
            int insertIndex = i - 1;
            while (insertIndex >= 0 && insertValue < array[insertIndex]) {
                //將當前被比較過的有序表中的元素向後移動一位
                array[insertIndex + 1] = array[insertIndex];
                //繼續往前找
                insertIndex--;
            }
            //如果待插入的數insertValue小於有序列表中的所有數,
            // 或者待插入的數大於當前數組下標爲insertIndex的數
            //當退出循環時說明待插入的位置找到了,爲insertIndex+1
            if (insertIndex + 1 != i) {
                array[insertIndex + 1] = insertValue;
            }
            //System.out.println("第"+(i+1)+"輪後的順序是:"+ Arrays.toString(array));
        }
        System.out.println("插入排序後的順序是:" + Arrays.toString(array));
    }

2.希爾排序

希爾排序 時間複雜度是O(n log n) 不穩定
希爾排序是簡單插入排序的更高效版本,也稱爲縮小增量排序
* 希爾排序的思想:就是把記錄數按下標的一定增量分組,對每組使用世界插入排序算法排序,
*               隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減少至一時,
*                整個文件恰被分成一組,算法便終止
* 希爾排序時,對有序序列在插入時採用交換法,ShellSortExchange()
* 希爾排序時,對有序序列在插入時採用交換法,ShellSortMove()
public static void shellSortExchange(int[] array) {
        int temp = 0;//用於臨時存放交換的元素
        int count = 0;//用於記錄當前是第幾輪排序
        //gap即數組被分爲gap數個小組,
        for (int gap = array.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < array.length; i++) {
                //按照插入排序的方法 遍歷各個組中的所有元素,共有gap組
                for (int j = i - gap; j >= 0; j -= gap) {
                    //如果當前元素大於加上步長後的那個元素,說明交換
                    if (array[j] > array[j + gap]) {
                        temp = array[j];
                        array[j] = array[j + gap];
                        array[j + gap] = temp;
                    }
                }
            }
            // System.out.println("希爾排序第"+(++count)+"輪排序後的順序是:"+Arrays.toString(array));
        }
        System.out.println("希爾交換式排序後的順序是:" + Arrays.toString(array));
    }

public static void shellSortMove(int[] array) {
        //增量gap,並逐步縮小增量
        for (int gap = array.length / 2; gap > 0; gap /= 2) {
            //從第gap個元素,逐個對其所在的組進行直接插入排序
            for (int i = gap; i < array.length; i++) {
                int insertIndex = i;
                int insertValue = array[insertIndex];
                if (array[insertIndex] < array[insertIndex - gap]) {
                    while (insertIndex - gap >= 0 && insertValue < array[insertIndex - gap]) {
                        //移動
                        array[insertIndex] = array[insertIndex - gap];
                        insertIndex -= gap;
                    }
                    //當退出while後,就給insertValue找到了插入的位置
                    array[insertIndex] = insertValue;
                }
            }
        }
        System.out.println("希爾移位式排序後的順序是:" + Arrays.toString(array));
    }

 3.選擇排序

選擇排序 時間複雜度是O(n^2) 不穩定
* 選擇排序介紹:選擇式排序是從欲排序的數據中,按指定的規則選出某一元素,再依規定交換位置後達到選擇排序的目的
* 選擇排序思想:第一次從arr[0]~ar[n-1]中選取最小值與arr[0]交換,
*              第i次從arr[i-1]~arr[n-1]中選最小值與arr[i-1]交換,
*              第n-1次從arr[i-2]~arr[n-1]中選最小值與arr[n-2]交換,
*              得到一個按從小到大排序的有序序列
public static void selectSort(int[] array) {
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;//每次存放找到的最小值的下標
            int min = array[i];//存放最小值
            for (int j = i + 1; j < array.length; j++) {
                if (min > array[j]) {
                    min = array[j];//重置min
                    minIndex = j;//重置minIndex
                }
            }
            //將最小值與當前遍歷到的值交換
            if (minIndex != i) {
                array[minIndex] = array[i];
                array[i] = min;
            }
            //System.out.println("第"+(i++)+"輪後");
        }
        System.out.println("選擇排序排序後的順序是:" + Arrays.toString(array));
    }

 4.冒泡排序

冒泡排序 時間複雜度是O(n^2) 穩定
冒泡排序思想:通過對待排序序列從前向後,依次比較相鄰元素的值,
*              如發現逆序則交換,使值較大的元素從前移動到後面,
*              就像水底下的氣泡一樣組件向上冒
* 冒泡排序優化:如果一趟比較下來沒有進行過交換,說明序列有序,
*              因此再排序過程中設置一個標誌flag,判單是否進行過交換,
*              從而減少不必要的比較
 public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {//這裏注意 -1因爲每趟排序都是把最大的數交換到了當前排序數的最後面
            int temp = 0;
            boolean flag = true;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag = false;
                }
            }
            //System.out.println("第"+(i+1)+"趟排序後端數組:"+Arrays.toString(array));
            if (flag = true) {
                //代表沒有發生交換,數組有序,就不用交換了
                break;
            }
        }
        System.out.println("冒泡排序排序後的順序是:" + Arrays.toString(array));
    }

5.快速排序

快速排序 時間複雜度是O(n log n) 不穩定
* 快速排序是對冒泡排序的一種改進,也屬於交換排序
* 快速排序思想:通過一趟排序將要排序的數據分割成獨立的兩部分,
*              其中一部分的所有數據都比另外一部分的所有數據都要小,
*              然後再按照此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,
*              以此達到整個數據編程有序序列
public static void quickSort(int[] array, int left, int right) {
        int l = left;//左下標
        int r = right;//右下標
        int pivot = array[(left + right) / 2];//中軸值
        int temp = 0;//臨時變量
        //while循環的目的是讓比pivot值小的都在pivot的左邊,比pivot值大的都在pivot的右邊
        while (l < r) {
            //從左向右找大於pivot值
            while (array[l] < pivot) {
                l += 1;
            }
            //從右向左找小於pivot的值
            while (array[r] > pivot) {
                r -= 1;
            }
            //左右兩邊的值符合規則
            if (l >= r) {
                break;
            }

            //交換找到的值
            temp = array[l];
            array[l] = array[r];
            array[r] = temp;

            //如果arr[l]==pivot 前移 r--
            if (pivot == array[l]) {
                r--;
            }
            //如果arr[r]==pivot 後移 l++
            if (pivot == array[r]) {
                l++;
            }
        }
        if (l == r) {
            //防止棧溢出
            l += 1;
            r -= 1;
        }
        //向左遞歸
        if(left<r){
            quickSort(array,left,r);
        }
        //向右遞歸
        if(l<right){
            quickSort(array,l,right);
        }

    }

6.桶排序

桶排序 時間複雜度O(n+k) 穩定
* 桶排序思想:將數據 分組放到桶中,遞歸排序桶中的數據,
*            當桶中的bucketSize爲1時將數據加入到返回集合中,bucketSize爲大桶中每個小桶的大小
*            當桶中的bucketCount爲1時 將bucketSize的值減1.bucketCount爲大桶中小桶的個數
 public static List<Integer> bucketSort(List<Integer> list, int bucketSize){
        //遞歸的出口
        if(list==null||list.size()<2){
            return list;
        }
        //遍歷list找到最大與最小的值
        int max=list.get(0);
        int min=list.get(0);

        for (int num: list) {
            if(num<min){
                min=num;
            }
            if(num>max){
                max=num;
            }
        }

        //定義返回結果集
        List<Integer> retList = new ArrayList<Integer>();

        //求出bucketCount
        int bucketCount=(max-min)/bucketSize+1;

        //定義大桶
        List<List<Integer>> buckets = new ArrayList<>(bucketCount);
        //初始化小桶
        for (int i = 0; i < bucketCount; i++) {
            buckets.add(new ArrayList<>());
        }
        //遍歷原始的集合將數據放入到桶中
        for (int i = 0; i < list.size(); i++) {
            int value=list.get(i);
            int index=(value-min) /bucketSize;
            buckets.get(index).add(value);
        }
        //依次將桶中的數據進行排序
        for (int i = 0; i < buckets.size(); i++) {
            if(bucketSize==1){
                //說明要麼只有一個元素,要麼桶中的元素都相同
                for (int j = 0; j < buckets.get(i).size(); j++) {
                    retList.add(buckets.get(i).get(j));
                }
            }else {
                if(bucketCount==1){
                    bucketSize--;
                }
                //當bucketSize不等於1說明存在兩個以上不同種類的數據
                //將小桶中的集合看成原集合再次遍歷
                List<Integer> temp = bucketSort(buckets.get(i), bucketSize);
                //依次將遞歸返回的集合存放到結果集中
                for (Integer num : temp) {
                    retList.add(num);
                }
            }
        }
        return retList;
    }

7.歸併排序

歸併排序 時間複雜度O(n log n) 穩定
* 歸併排序介紹:一開始先把數組從中間劃分成兩個子數組,一直遞歸地把子數組劃分成跟小的數組,
*              直到子數組裏面只有一個元素,纔開始排序
* 歸併排序思想:把一個複雜問題分成兩個或多個相同或相似的子問題,然後把子問題分成更小的子問題,
*              直到子問題可以簡單的直接請求,最原問題的解就是子問題的合併,
*              歸併排序將分治的思想體現得淋漓盡致
 public static void mergeSort(Integer[] array, int lo, int hi){
        //判斷是否只剩下最後一個元素
        if(lo>=hi){
            return;
        }
        //從中間將數組分成兩部分
        int mid=lo+(hi-lo)/2;
        //分別遞歸的將左右兩半排好序
        mergeSort(array,lo,mid);
        mergeSort(array,mid+1,hi);
        //將排好序的兩半合併
        merge(array,lo,mid,hi);
    }
private static void merge(Integer[] array, int lo, int mid, int hi) {
        //複製一份原來的數組
        Integer[] copy=array.clone();

        //定義k指針表示從什麼位置開始修改原來的數組
        int k=lo;
        //i指針表示左半邊的起始位置
        int i=lo;
        //j指針表示右半邊的起始位置
        int j=mid+1;
        while (k<hi){
            if(i>mid){
                //當左半邊的數都處理完畢,只需要將右半邊的數逐個拷貝過去
                array[k++]=copy[j++];
            }else if(j>hi){
                //當右半邊的數都處理完畢,只需要將左半邊的數逐個拷貝過去
                array[k++]=copy[i++];
            }else if(copy[j]<copy[i]){
                //當右半邊的數小於左半邊的數,將右半邊的數拷貝,j指針向後移動一位
                array[k++]=copy[j++];
            }else {
                //當左半邊的數小於右半邊的數,將左半邊的數拷貝,i指針向後移動一位
                array[k++]=copy[i++];
            }
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章