十大經(jīng)典排序算法(java實(shí)現(xiàn))

前言

本文我們將以java代碼實(shí)現(xiàn)十大經(jīng)典排序算法,包括冒泡排序、選擇排序、插入排序、希爾排序、歸并排序、快速排序、堆排序、計(jì)數(shù)排序、桶排序、基數(shù)排序。

排序算法相關(guān)的時(shí)間復(fù)雜度和空間復(fù)雜度

1、冒泡排序(Bubble Sort)

算法描述

  1. 比較相鄰的元素。如果第一個(gè)比第二個(gè)大,就交換它們兩個(gè);
  2. 對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對,這樣在最后的元素應(yīng)該會(huì)是最大的數(shù);
  3. 針對所有的元素重復(fù)以上的步驟,除了最后一個(gè);
  4. 重復(fù)步驟1~3,直到排序完成。

代碼實(shí)現(xiàn)

public void sort(int[] nums) {
        for(int i = 0;i<nums.length -1;i++){
            for (int j = 0; j < nums.length - 1-i; j++) {
                int c;
                if (nums[j] > nums[j + 1]) {
                    c = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = c;
                }
            }
        }
    }

這里有個(gè)問題,冒泡排序的時(shí)間復(fù)雜度最好是n,但上述代碼不管如何時(shí)間復(fù)雜度都是n2,所以有了以下改進(jìn)算法。

public void bubbleSort(int arr[]) {
        boolean didSwap;
        for(int i = 0, len = arr.length; i < len - 1; i++) {
            didSwap = false;
            for(int j = 0; j < len - i - 1; j++) {
                if(arr[j + 1] < arr[j]) {
                    int temp = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                    didSwap = true;
                }
            }
            if(didSwap == false)
                return;
        }
    }

2、選擇排序(Selection Sort)

算法描述
選擇排序一種簡單直觀的排序算法。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續(xù)尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
代碼實(shí)現(xiàn)

public int[] sort(int[] nums) {
        int len = nums.length;
        int temp;
        for(int i = 0;i<len;i++){
            for(int j =i;j<len;j++){
                if(nums[i]>nums[j]){
                    temp = nums[i];
                    nums[i] = nums[j];
                    nums[j] = temp;
                }
            }
        }
        return nums;
    }

3、插入排序(Insertion Sort)

算法描述

  1. 從第一個(gè)元素開始,該元素可以認(rèn)為已經(jīng)被排序;
  2. 取下一元素,在已經(jīng)排序的元素序列中從后向前掃描;
  3. 如果該元素(已排序)大于新元素,將該元素移到下一位置;
  4. 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 將新元素插入到該位置后;
  6. 重復(fù)步驟2~5。

代碼實(shí)現(xiàn)

public void sort(int[] nums) {
        int len = nums.length;
        int preIndex,current;
        for(int i = 0;i<len;i++){
            preIndex = i - 1;
            current = nums[i];
            while(preIndex >= 0 && nums[preIndex] > current){
                nums[preIndex + 1] = nums[preIndex];
                preIndex--;
            }
            nums[preIndex + 1] = current;
        }
    }

4、希爾排序(Shell Sort)
算法排序
希爾排序(Shell's Sort)是插入排序)的一種又稱“縮小增量排序”,是直接插入排序算法的一種更高效的改進(jìn)版本。希爾排序是非穩(wěn)定排序算法。

  1. 選擇一個(gè)增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列個(gè)數(shù)k,對序列進(jìn)行k 趟排序;
  3. 每趟排序,根據(jù)對應(yīng)的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進(jìn)行直接插入排序。僅增量因子為1 時(shí),整個(gè)序列作為一個(gè)表來處理,表長度即為整個(gè)序列的長度。
    代碼實(shí)現(xiàn)
public void sort(int[] nums) {
        int len = nums.length;
        for (int gap = (int) Math.floor(len / 2); gap > 0; gap = (int) Math.floor(gap / 2)) {
            // 注意:這里和動(dòng)圖演示的不一樣,動(dòng)圖是分組執(zhí)行,實(shí)際操作是多個(gè)分組交替執(zhí)行
            for (int i = gap; i < len; i++) {
                int j = i; 
                int current = nums[i];
                while (j - gap >= 0 && current < nums[j - gap]) {
                    nums[j] = nums[j - gap];
                     j = j - gap;
                }
                nums[j] = current;
            }
        }
        
    }

5、歸并排序(Merge Sort)

算法描述
歸并排序(MERGE-SORT)是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個(gè)非常典型的應(yīng)用。將已有序的子序列合并,得到完全有序的序列;即先使每個(gè)子序列有序,再使子序列段間有序。若將兩個(gè)有序表合并成一個(gè)有序表,稱為二路歸并。歸并排序是一種穩(wěn)定的排序方法。實(shí)際上就是用的遞歸的思想。

  1. 把長度為n的輸入序列分成兩個(gè)長度為n/2的子序列;
  2. 對這兩個(gè)子序列分別采用歸并排序;
  3. 將兩個(gè)排序好的子序列合并成一個(gè)最終的排序序列。

代碼實(shí)現(xiàn)

public int[] sort(int[] nums) {
        int len = nums.length;
        if(len<2){
            return nums;
        }
        int middle = (int) Math.floor(len/2);
        int[] left = new int[middle],right = new int[len-middle];
        System.arraycopy(nums, 0, left, 0, middle);
        System.arraycopy(nums, middle, right, 0, len-middle);
        return merge(sort(left),sort(right));
    }
    
    private int[] merge(int[] left, int[] right) {
        // TODO Auto-generated method stub
          int[] result = new int[left.length+right.length];
          int i = 0, j = 0, k = 0;
            while ((left.length - j) > 0 && (right.length - k) > 0) {
                if (left[j] <= right[k]) {
                    result[i] = left[j];
                    j++;
                } else {
                    result[i] = right[k];
                    k++;
                }
                i++;
            }
            while (j < left.length){
                System.arraycopy(left, j, result, i, left.length-j);
                j++;
                i++;
            }
                
            while (k < right.length){
                System.arraycopy(right, k, result, i, right.length-k);
                k++;
                i++;
            }
            return result;
    }

6、快速排序(Quick Sort)

算法描述
快速排序(Quicksort)是對冒泡排序的一種改進(jìn)。

  1. 首先選擇一個(gè)分界值,通過該分界值將數(shù)組分成左右兩部分。
  2. 重新對數(shù)組進(jìn)行排序,比分界值小的集中到數(shù)組左邊,比分界值大的位于數(shù)組右邊。
  3. 然后左右兩邊數(shù)組再次進(jìn)行快速排序。
  4. 最后重復(fù)上述步驟,實(shí)現(xiàn)遞歸。

代碼實(shí)現(xiàn)

public void sort(int[] arr,int low, int high) {
        int i, j ,temp,snap;
        if(low > high){
            return;
        }
        i = low;
        j = high;
        // 基準(zhǔn)位置
        temp = arr[low];
        while (i<j) {
            while (temp<=arr[j] && i<j) {
                j--;
            }
            while (temp>=arr[i] && i<j) {
                i++;
            }
            if (i<j) {
                // 交換滿足條件的 i j位置的值
                snap = arr[i];
                arr[i] = arr[j];
                arr[j] = snap;
            }
        }
        // 交換基準(zhǔn)位置的值
        arr[low] = arr[i];
        arr[i] = temp;
        
        // 遞歸調(diào)用, 左右
        sort(arr,low,j-1);
        sort(arr,j+1,high);
    }

7、堆排序(Heap Sort)

算法描述
堆排序是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。堆是一個(gè)近似完全二叉樹的結(jié)構(gòu),并同時(shí)滿足堆積的性質(zhì):即子結(jié)點(diǎn)的鍵值或索引總是小于(或者大于)它的父節(jié)點(diǎn)。

  1. 將初始待排序關(guān)鍵字序列(R1,R2….Rn)構(gòu)建成大頂堆
  2. 將堆頂元素R[1]與最后一個(gè)元素R[n]交換。
  3. 由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對當(dāng)前無序區(qū)(R1,R2,……Rn-1)調(diào)整為新堆,然后再次將R[1]與無序區(qū)最后一個(gè)元素交換,得到新的無序區(qū)(R1,R2….Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過程直到有序區(qū)的元素個(gè)數(shù)為n-1,則整個(gè)排序過程完成。

代碼實(shí)現(xiàn)

public static void sort(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }
        buildMaxHeap(array);

        int temp;
        for(int arg:array){
            System.out.print(arg+" ");
        }
        for (int i = array.length - 1; i >= 1; i--) {

            temp = array[0];
            array[0] = array[i];
            array[i] = temp;
            
            maxHeap(array, i, 0);
            System.out.println(" ");
            System.out.println("-------"+i+"-------");
            for(int arg:array){
                System.out.print(arg+" ");
            }
        }
    }

    private static void buildMaxHeap(int[] array) {
        if (array == null || array.length <= 1) {
            return;
        }

        int half = array.length / 2;
        for (int i = half; i >= 0; i--) {
            maxHeap(array, array.length, i);
        }
    }

    private static void maxHeap(int[] array, int heapSize, int index) {
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        int largest = index;
        if (left < heapSize && array[left] > array[index]) {
            largest = left;
        }

        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }

        if (index != largest) {

            int temp;
            temp = array[index];
            array[index] = array[largest];
            array[largest] = temp;
            
            maxHeap(array, heapSize, largest);
        }
    }

8、計(jì)數(shù)排序(Counting Sort)

算法描述
計(jì)數(shù)排序是一個(gè)非基于比較的排序算法,犧牲空間換取時(shí)間。

  1. 得到數(shù)組的最大和最小元素。
  2. 統(tǒng)計(jì)出每個(gè)數(shù)據(jù)為i的值出現(xiàn)的次數(shù),將次數(shù)存入數(shù)組C中。
  3. 以數(shù)組C為模板重新填充目標(biāo)數(shù)組。

代碼實(shí)現(xiàn)

public void sort(int[] nums) {
        int min = nums[0],max = nums[0];
        for(int num: nums){
            max = Math.max(max, num);
            min = Math.min(min, num);
        }
        int[] counts = new int[max - min+1];
        for(int num: nums){
            counts[num - min]++;
        }
        int i = 0,j = 0;
        for(int count:counts){
            while (count > 0) {
                nums[i] = j + min;
                count --;
                i ++;
            }
            j++;
        }
    }

9、桶排序(Bucket Sort)

算法描述
將數(shù)組分到有限數(shù)量的桶子里。每個(gè)桶子再個(gè)別排序(有可能再使用別的排序算法)。

  1. 設(shè)置一個(gè)定量的鏈表當(dāng)作空桶。
  2. 遍歷輸入數(shù)據(jù),并且把數(shù)據(jù)一個(gè)一個(gè)放到對應(yīng)的桶里去。
  3. 對每個(gè)不是空的桶進(jìn)行排序。
  4. 從不是空的桶里把排好序的數(shù)據(jù)拼接起來。

代碼實(shí)現(xiàn)

public void sort(int[] nums) {
        int min = nums[0],max = nums[0];
        for(int num: nums){
            max = Math.max(max, num);
            min = Math.min(min, num);
        }
        int bucketBottom = (int) Math.floor((float)min/10);
        int bucketTop = (int) Math.ceil((float)max/10);
        ArrayList<ArrayList<Integer>> bucketsList = new ArrayList<ArrayList<Integer>>();
        for(int i = 0;i<(bucketTop-bucketBottom);i++){
            bucketsList.add(new ArrayList<Integer>());
        }
        for(int num: nums){
            int index = getBucketIndex(num);
            insertBucket(bucketsList.get(index),num);
        }
        int index = 0;
        for (ArrayList<Integer> list : bucketsList) {
            for(int data: list){
                nums[index++] = data;
            }
        }
    }
    
    // 將數(shù)據(jù)放入具體的桶內(nèi)
    private void insertBucket(ArrayList<Integer> arrayList, int num) {
        boolean insertFlag = true;
        ListIterator<Integer> it = arrayList.listIterator();
        while (it.hasNext()) {
            if (num <= it.next()) {
                it.previous(); // 把迭代器的位置偏移回上一個(gè)位置
                it.add(num); // 把數(shù)據(jù)插入到迭代器的當(dāng)前位置
                insertFlag = false;
                break;
            }
        }
        // 否則把數(shù)據(jù)插入到鏈表末端
        if (insertFlag) {
            arrayList.add(num); 
        }
    }
    // 判斷放入哪個(gè)桶內(nèi)的方法
    private int getBucketIndex(int data){
        return (int) Math.floor(data/10);
    }

10、基數(shù)排序(Radix Sort)

算法描述
基數(shù)排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。

  1. 取得數(shù)組中的最大數(shù),并取得位數(shù)。
  2. 使用鏈表對按照當(dāng)前位數(shù)大小存儲(chǔ)數(shù)據(jù),然后重新寫入數(shù)組。
  3. 根據(jù)位數(shù)的不同重復(fù)步驟2。

代碼實(shí)現(xiàn)

public void sort(int[] nums) {
         int max = 0;
         int exp = 1;
         for (int num:nums){
             max = Math.max(max,num);
         }
         while(max/Math.pow(10,exp) > 1){
             exp++;
         }
        //存儲(chǔ)待排元素
         ArrayList<ArrayList<Integer>> radixList = new ArrayList<ArrayList<Integer>>();
         for(int j = 0;j<10;j++){
             ArrayList<Integer> list = new ArrayList<Integer>();
             radixList.add(list);
         }
         
         for(int i = 0;i<=exp;i++){
             for(int num:nums){
                 int positionValue = getPositionValue(num, i);
                 radixList.get(positionValue).add(num);
             }
             int index = 0;
             for(ArrayList<Integer> list:radixList){
                 for(int value: list){
                     nums[index] = value;
                     index++;
                 }
                 list.clear();
             }
         }
    }
    // 獲取當(dāng)前位數(shù)對應(yīng)的值
    private int getPositionValue(int num,int exp){
        return (int) (num % Math.pow(10, exp)/Math.pow(10, exp-1));
    }

源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容