八大排序算法

排序算法分为: 三大基本排序---冒泡排序,选择排序,插入排序

                          高级排序---------希尔排序,快速排序,归并排序,堆排序

                          非比较排序------基数排序

参考资料:https://www.runoob.com/w3cnote/sort-algorithm-summary.html

1.冒泡排序

  基本思想:两个数比较大小,较大的数下沉,较小的数冒起来,依次把最小,次小的排在前面,最后完成排序

  平均时间复杂度:O(n2)

public class BubbleSort {
    public static void sort(int[] arr) {
        int temp;//临时变量
        for (int i = 0; i < arr.length; i++) {
            for (int j = arr.length - 1; j > i; j--) {
                if (arr[j] < arr[j - 1]) {
                    temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                }
            }
        }
    }

    public static void qrSort(int[] arr) {
        int temp;//临时变量
        boolean flag;//标志位
        for (int i = 0; i < arr.length; i++) {
            flag = true;
            //只有交换时把flag设置为false,不交换证明后面的数组是有序的,直接跳出循环
            for (int j = arr.length - 1; j > i; j--) {
                if (arr[j] < arr[j - 1]) {
                    temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                    flag = false;
                }
            }
            //后面数组有序跳出循环
            if (false) {
                break;
            }
        }
    }
}

 

2.选择排序

基本思想:
在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;
第二次遍历n-2个数,找到最小的数值与第二个元素交换

平均时间复杂度:O(n2)

public class SelctionSort {
    public static void sort(int[] arr) {
        int temp;//临时变量
        int index;//临时位置
        for (int i = 0; i < arr.length; i++) {
            index = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[index]) {
                    index = j;
                }
            }
            //判断交换
            if (i != index) {
                temp = arr[i];
                arr[i] = arr[index];
                arr[index] = temp;
            }
        }
    }
}

3.插入排序

基本思想:
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

平均时间复杂度:O(n2)

public class InsertionSort {
    public static void sort(int[] arr) {
        int temp;//临时变量
        for (int i = 0; i < arr.length-1; i++) {
            for (int j = i + 1; j > 0; j--) {
                if (arr[j] < arr[j - 1]) {
                    temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                } else {//不需要交换
                    break;
                }
            }
        }
    }
}

 

4.希尔排序

  • 前言:
    数据序列1: 13-17-20-42-28 利用插入排序,13-17-20-28-42. Number of swap:1;
    数据序列2: 13-17-20-42-14 利用插入排序,13-14-17-20-42. Number of swap:3;
    如果数据序列基本有序,使用插入排序会更加高效。

  • 基本思想:
    在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
    然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。

    public class ShellSort {
        /**
         * 希尔排序就是对于数组进行分组排序
         */
        public static void sort(int[] arr) {
            int incre = arr.length;//表示有多少组
            int temp;//临时变量
            while (incre > 1) {
                incre /= 2;//每次分组,每个子分组扩大,排序
                for (int k = 0; k < incre; k++) {//对于每一组进行遍历排序
                    //使用插入排序对子数组进行排序
                    for (int i = k + incre; i < arr.length; i += incre) {
                        for (int j = i; j > k; j -= incre) {
                            if (arr[j] < arr[j - incre]) {
                                temp = arr[j];
                                arr[j] = arr[j - incre];
                                arr[j - incre] = temp;
                            } else {
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    

    5.快速排序

  • 基本思想:(分治)

    • 先从数列中取出一个数作为key值;
    • 将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;
    • 对左右两个小数列重复第二步,直至各区间只有1个数。
  • 辅助理解:挖坑填数

    • 初始时 i = 0; j = 9; key=72
      由于已经将a[0]中的数保存到key中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。
      从j开始向前找一个比key小的数。当j=8,符合条件,a[0] = a[8] ; i++ ; 将a[8]挖出再填到上一个坑a[0]中。
      这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。
      这次从i开始向后找一个大于key的数,当i=3,符合条件,a[8] = a[3] ; j-- ; 将a[3]挖出再填到上一个坑中。
      数组:72 - 6 - 57 - 88 - 60 - 42 - 83 - 73 - 48 - 85
            0   1   2    3    4    5    6    7    8    9
    • 此时 i = 3; j = 7; key=72
      再重复上面的步骤,先从后向前找,再从前向后找。
      从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;
      从i开始向后找,当i=5时,由于i==j退出。
      此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将key填入a[5]。
      数组:48 - 6 - 57 - 88 - 60 - 42 - 83 - 73 - 88 - 85
            0   1   2    3    4    5    6    7    8    9
    • 可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。
      数组:48 - 6 - 57 - 42 - 60 - 72 - 83 - 73 - 88 - 85
            0   1   2    3    4    5    6    7    8    9
public class QuickSort {
    public static void sort(int[] arr, int start, int end) {
        if (start >= end) {
            return;
        }
        int i = start;
        int j = end;
        int key = arr[i];
        while (i < j) {
            while (i < j && key <= arr[j]) {
                j--;
            }
            if (i < j) {
                arr[i] = arr[j];
                i++;
            }
            while (i < j && key > arr[i]) {
                i++;
            }
            if (i < j) {
                arr[j] = arr[i];
                j--;
            }
        }
        //i == j
        arr[i] = key;
        sort(arr, start, i-1);
        sort(arr, i+1, end);
    }
}

6.归并排序

基本思想:  可以将A,B组各自再分成2组。依次类推,当分出来的小组只有1个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的2个小组就可以了。这样通过先递归的分解数列再合并数列就完成了归并排序。

平均时间复杂度:O(NlogN)

public class MergeSort {
    public static void sort(int[] arr, int start, int end, int[] temp) {
        if (start < end) {
            int middle = (start + end) / 2;
            sort(arr, start, middle, temp);
            sort(arr, middle + 1, end, temp);
            merageArray(arr, start, middle, end, temp);
        }
    }

    //合并数组
    public static void merageArray(int[] arr, int start, int middle, int end, int[] temp) {
        int i = start;
        int j = middle;
        int k = middle + 1;
        int l = end;
        int m = start;
        while (i <= j && k <= l) {
            if (arr[i] < arr[k]) {
                temp[m++] = arr[i++];
            } else {
                temp[m++] = arr[k++];
            }
        }
        while (i <= j) {
            temp[m++] = arr[i++];
        }
        while (k <= l) {
            temp[m++] = arr[k++];
        }

        for (int ii = start; ii <= end; ii++) {
            arr[ii] = temp[ii];
        }
    }
}

 

7.堆排序

基本思想:建立二叉树,其中i的子节点为2*i+1,和2*i+2。然后建立最小(大)树,可以保证每次最小(大)的值在0位置,取出放置数组后端,最后调整二叉树的长度,重新恢复堆,重复操作获取有序的数组,最小树构建有大到小的数组

平均时间复杂度:O(NlogN)

public class HeapSort {
    //构建最小树
    public static void buildMinHeap(int[] arr, int length) {
        for (int i = (length - 1) / 2; i >= 0; i--) {
            minHeapFixdown(arr, i, length);
        }
    }

    //最小树,大数下沉
    public static void minHeapFixdown(int[] arr, int start, int end) {
        int i = start;
        int j = 2 * i + 1;
        int temp;
        while (j < end) {
            if ((j + 1) < end && arr[j] > arr[j + 1]) {
                j++;
            }
            if (arr[j] >= arr[i]) {
                break;
            }

            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;

            i = j;
            j = 2 * j + 1;
        }
    }

    public static void sort(int[] arr) {
        buildMinHeap(arr, arr.length);
        int temp;
        for (int i = arr.length - 1; i > 0; i--) {
            temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            minHeapFixdown(arr, 0, i);
        }
    }

}

8.基数排序

BinSort想法非常简单,首先创建数组A[MaxValue];然后将每个数放到相应的位置上(例如17放在下标17的数组位置);最后遍历数组,即为排序后的结果

基本思想: 基数排序是在BinSort的基础上,通过基数的限制来减少空间的开销。

过程:先判断最大数的位数,有个位数直到最大位数,依次进行分组,在对应的数组,由于是对基数分组,分好后依次取出就是相应位数的排序,所以由个位数到最大位数循环操作后就得到有序序列。如下图

public class RadixSort {
    public static void sort(int[] arr, int radix, int n, int k, int[] temp, int[] cnt) {
        //arr:原数组
        //temp:临时数组
        //n:序列的数字个数
        //k:最大的位数2
        //radix:基数10
        //cnt:存储bin[i]的个数,基数对应数组的数量
        for (int i = 0, rtok = 1; i < k; i++, rtok *= radix) {
            //初始化cnt数组
            for (int j = 0; j < radix; j++) {
                cnt[j] = 0;
            }
            //计算每一个数组的大小
            for (int j = 0; j < n; j++) {
                cnt[(arr[j] / rtok) % radix]++;
            }
            /**
             * 每一个cnt数组的大小等于前面数组的和加上本数组的大小
             * 这样是为了方便计算对于数的位置,等于直接在temp中划分出对应的位置放置数
             * 直接把temp划分为对应位置,例如cnt[j]的数组划分了为   cnt[j-1]-1 到 cnt[j]-1  之间
             */
            for (int j = 1; j < radix; j++) {
                cnt[j] += cnt[j - 1];
            }
            for (int j = 0; j < n; j++) {
                cnt[(arr[j] / rtok) % radix]--;
                temp[cnt[(arr[j] / rtok) % radix]] = arr[j];
            }
            //放置原数组
            for (int j = 0; j < n; j++) {
                arr[j] = temp[j];
            }
        }
    }
}

 

你可能感兴趣的:(排序)