常用排序算法整理

最近整理了一些常用的排序算法,主要是下面几个排序算法:

- 冒泡排序
- 快速排序
- 选择排序
- 插入排序
- 希尔排序
- 归并排序
- 堆排序
- 基数排序
- 桶排序

1.冒泡排序

每次确定一个元素放在最后,然后从头开始下一轮

 /**
     * 稳定排序
     * 冒泡排序
     * @param a
     */
    public static void buddleSort(int[] a) {
        System.out.print("排序前数组:");
        System.out.println(Arrays.toString(a));
        if (a == null) {
            return;
        }

        int len = a.length;
        for (int i = 0; i < len - 1; i++) {
            for (int j = 0; j < len - i -1; j++) {
                if (a[j] < a[j+1]) {
                    int temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }
        }
        System.out.print("排序后数组:");
        System.out.println(Arrays.toString(a));
    }

2.快速排序

同样每次确定一个元素,但是确定的是第一个元素所在的位置,然后递归对该位置左右两部分排序

/**
     * 不稳定排序
     * 快速排序
     * @param a
     * @param left
     * @param right
     */
    public static void quickSort(int[] a, int left, int right) {
        if (a == null || left > right) {
            return;
        }
        if (right - left <= 1) {
            return;
        }
        int temp = a[left];
        int start = left;
        int end = right;
        while (left != right) {
            while (a[right] > temp && right > left) {
                right--;
            }

            if (left < right) {
                a[left] = a[right];
                left++;
            }

            while (a[left] < temp && left < right) {
                left++;
            }

            if (left < right) {
                a[right] = a[left];
                right--;
            }

        }
        a[left] = temp;
        quickSort(a, start, left-1);
        quickSort(a, left+1, end);
    }

3.选择排序

每次遍历选择最小的元素放在第一个位置

/**
     * 不稳定排序
     * 选择排序
     * @param a
     */
    public static void selectSort(int[] a) {
        if (a == null || a.length == 0) {
            return;
        }
        System.out.print("排序前数组:");
        System.out.println(Arrays.toString(a));
        int len = a.length;
        for (int i = 0; i < len -1; i++) {
            for (int j = i + 1; j < len; j++) {
                if (a[i] > a[j]) {
                    int temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }

        System.out.print("排序后数组:");
        System.out.println(Arrays.toString(a));
    }

4.插入排序

第一个是每次往前插入都交换位置,第二个是先保存,然后后移元素,最后放入到合适位置。后面的希尔插入就是用的第二种

 /**
     * 稳定排序
     * @param a
     */
    public static void insertSort(int[] a) {
        if (a == null || a.length == 0) {
            return;
        }

        System.out.print("排序前数组:");
        System.out.println(Arrays.toString(a));
        int len = a.length;
        for (int i = 0; i < len - 1; i++) {
            int j = i;
            while(j >= 0 && a[j+1] < a[j]) {
                int temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
                j--;
            }
        }
        System.out.print("排序后数组:");
        System.out.println(Arrays.toString(a));
    }

    /**
     * 稳定排序
     * 每次插入先把元素后移,而不是每次交换
     * @param a
     */
    public static void insertSort2(int[] a){
        for(int i=1;i= 1 && temp

5.希尔排序

 /**
     * 不稳定
     * 希尔排序
     * @param a
     */
    public static void shellSort(int[] a) {
        if (a == null || a.length == 0) {
            return;
        }
        System.out.print("排序前数组:");
        System.out.println(Arrays.toString(a));

        int len = a.length;
        int inc = len / 2;
        while (inc > 0) {
            for (int i = inc; i < len-1; i++) {
                int temp = a[i];
                int j = i;
                while (j >= inc && a[j - inc] > temp) {
                    a[j] = a[j-inc];
                    j = j - inc;
                }
                a[j] = temp;
            }
            inc = inc / 2;
        }

        System.out.print("排序后数组:");
        System.out.println(Arrays.toString(a));
    }

6.归并排序

归并排序分为递归和迭代两个版本。递归版本比较好理解。迭代版本主要是要理解第二个for循环,是遍历每一对要合并的子数组

/**
     * 稳定
     * 递归 Top-to-Down(维基百科)
     * 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
     * 2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
     * 3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
     * 4.重复步骤3直到某一指针到达序列尾
     * 5.将另一序列剩下的所有元素直接复制到合并序列尾
     * @param a
     */
    public static void mergeSort1(int[] a) {
        System.out.print("归并排序前数组:");
        System.out.println(Arrays.toString(a));
        int len = a.length;
        int[] result = new int[len];
        mergeSortRecursive(a, result, 0, len-1);
        System.out.print("归并后数组:");
        System.out.println(Arrays.toString(a));
    }

    private static void mergeSortRecursive(int[] arr, int[] result, int start, int end) {
        if (start >= end) {
            return;
        }
        //int len = end - start;
        //int mid = (len >> 1) + start;
        int mid = (start + end) / 2;
        int start1 = start;
        int end1 = mid;
        int start2 = mid + 1;
        int end2 = end;
        mergeSortRecursive(arr, result, start1, end1);
        mergeSortRecursive(arr, result, start2, end2);
        int k = start;
        while (start1 <= end1 && start2 <= end2) {
            result[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
        }

        while (start1 <= end1) {
            result[k++] = arr[start1++];
        }

        while (start2 <= end2) {
            result[k++] = arr[start2++];
        }

        for (k = start; k <= end; k++) {
            arr[k] = result[k];
        }
    }

    /**
     * 迭代 Down-to-Top(维基百科)
     * 1.将序列每相邻两个数字进行归并操作,形成 {ceil(n/2)} {ceil(n/2)}个序列,排序后每个序列包含两/一个元素
     * 2.若此时序列数不是1个则将上述序列再次归并,形成 {ceil(n/4)} {ceil(n/4)}个序列,每个序列包含四/三个元素
     * 3.重复步骤2,直到所有元素排序完毕,即序列数为1
     * @param a
     */
    public static void mergeSort2(int[] a) {
        int len = a.length;
        int[] result = new int[len];
        int block;
        int start;
        System.out.print("归并排序前数组:");
        System.out.println(Arrays.toString(a));
        for (block = 1; block < len; block *= 2) {
            for (start = 0; start < len; start += 2 * block) {
                int low = start;
                int mid = (start + block) < len ? (start + block) : len;
                int high = (start + 2 * block) < len ? (start + 2 * block) : len;
                //
                int start1 = low;
                int end1 = mid;

                int start2 = mid;
                int end2 = high;

                while (start1 < end1 && start2 < end2) {
                    result[low++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
                }

                while (start1 < end1) {
                    result[low++] = a[start1++];
                }

                while (start2 < end2) {
                    result[low++] = a[start2++];
                }
            }
            int[] temp = a;
            a = result;
            result = temp;
        }
        System.out.print("归并后数组:");
        System.out.println(Arrays.toString(a));
    }

7.堆排序

/**
     * 不稳定
     * 堆节点的访问:通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:
     * 1.父节点i的左子节点在位置 {(2i+1)}
     * 2.父节点i的右子节点在位置 {(2i+2)}
     * 3.子节点i的父节点在位置 {floor((i-1)/2)}
     *
     * 堆的操作:在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
     * 1.最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
     * 2.创建最大堆(Build_Max_Heap):将堆所有数据重新排序
     * 3.堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
     */
    private static void heapSort(int[] a) {
        /*
         *  第一步:将数组堆化
         *  beginIndex = 第一个非叶子节点。
         *  从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
         *  叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
         */
        int len = a.length - 1;
        int beginIndex = (len -1) >> 1;
        for (int i = beginIndex; i >= 0; i--) {
            maxHeapify(a, i, len);
        }

        /*
         * 第二步:对堆化数据排序
         * 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
         * 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。
         * 直至未排序的堆长度为 0。
         */
        for (int i = len; i > 0; i--) {
            swap(a, 0, i);
            maxHeapify(a, 0, i -1);
        }
    }

    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }


    private static void maxHeapify(int[] a, int index, int len) {
        // 左子节点索引
        int li = (index << 1) + 1;
        // 右子节点索引
        int ri = li + 1;
        // 子节点值最大索引,默认左子节点。
        int cMax = li;

        if(li > len) {
            // 左子节点索引超出计算范围,直接返回。
            return;
        }
        if(ri <= len && a[ri] > a[li]) {
            // 先判断左右子节点,哪个较大。
            cMax = ri;
        }
        if(a[cMax] > a[index]){
            // 如果父节点被子节点调换,
            // 则需要继续判断换下后的父节点是否符合堆的特性。
            swap(a, cMax, index);
            maxHeapify(a, cMax, len);
        }
    }

另:如果在调用maxHeapify时不想递归调用,也可以换成迭代,只要控制好出口条件即可,如下代码所示:

private static void maxHeapify(int[] a, int index, int len) {
        /*// 左子节点索引
        int li = (index << 1) + 1;
        // 右子节点索引
        int ri = li + 1;
        // 子节点值最大索引,默认左子节点。
        int cMax = li;

        if(li > len) {
            // 左子节点索引超出计算范围,直接返回。
            return;
        }
        if(ri <= len && a[ri] > a[li]) {
            // 先判断左右子节点,哪个较大。
            cMax = ri;
        }
        if(a[cMax] > a[index]){
            // 如果父节点被子节点调换,
            // 则需要继续判断换下后的父节点是否符合堆的特性。
            swap(a, cMax, index);
            maxHeapify(a, cMax, len);
        }*/

        while (2 * index <= len) {
            // 左子节点索引
            int li = (index << 1) + 1;
            // 右子节点索引
            int ri = li + 1;
            // 子节点值最大索引,默认左子节点。
            int cMax = li;

            if(li > len) {
                // 左子节点索引超出计算范围,直接返回。
                return;
            }
            if(ri <= len && a[ri] > a[li]) {
                // 先判断左右子节点,哪个较大。
                cMax = ri;
            }
            if(a[cMax] > a[index]){
                // 如果父节点被子节点调换,
                // 则需要继续判断换下后的父节点是否符合堆的特性。
                swap(a, cMax, index);
                index = cMax;
            } else {
                break;
            }
        }
    }

8.基数排序

/**
     * 稳定排序
     * 基数排序
     * @param data
     * @param n
     */
    private static void radixSort(int[] data, int n) {
        System.out.print("基数排序前数组:");
        System.out.println(Arrays.toString(data));

        int d = maxbit(data, n);
        int[] tmp = new int[n];
        //计数器
        int[] count = new int[10];
        int i, j, k;
        int radix = 1;
        for(i = 1; i <= d; i++) {
            //进行d次排序
            for(j = 0; j < 10; j++) {
                //每次分配前清空计数器
                count[j] = 0;
            }
            for(j = 0; j < n; j++) {
                //统计每个桶中的记录数
                k = (data[j] / radix) % 10;
                count[k]++;
            }
            for(j = 1; j < 10; j++) {
                //将tmp中的位置依次分配给每个桶
                count[j] = count[j - 1] + count[j];
            }
            for(j = n - 1; j >= 0; j--) {
                //将所有桶中记录依次收集到tmp中
                k = (data[j] / radix) % 10;
                tmp[count[k] - 1] = data[j];
                count[k]--;
            }
            for(j = 0; j < n; j++) {
                //将临时数组的内容复制到data中
                data[j] = tmp[j];
            }
            radix = radix * 10;
        }

        System.out.print("基数排序后数组:");
        System.out.println(Arrays.toString(data));
    }

    private static int maxbit(int data[], int n) //辅助函数,求数据的最大位数
    {
        ///< 最大数
        int maxData = data[0];
        /// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
        for (int i = 1; i < n; ++i)
        {
            if (maxData < data[i]) {
                maxData = data[i];
            }
        }
        int d = 1;
        int p = 10;
        while (maxData >= p)
        {
            //p *= 10; // Maybe overflow
            maxData /= 10;
            ++d;
        }
        return d;
    }

9.桶排序

 /**
     * 稳定排序
     * 桶排序
     * @param arr
     */
    public static void bucketSort(int[] arr){
        System.out.print("桶排序前数组:");
        System.out.println(Arrays.toString(arr));
        int max = arr[0],min = arr[0];
        for (int a : arr) {
            if (maxa) {
                min=a;
            }
        }
        //该值也可根据实际情况选择
        int bucketNum = max/10 - min/10 + 1;
        List buckList = new ArrayList>();
        //create bucket
        for (int i = 1; i <= bucketNum; i++) {
            buckList.add(new ArrayList());
        }
        //push into the bucket
        for (int i = 0; i < arr.length; i++) {
            int index = indexFor(arr[i],min,10);
            ((ArrayList)buckList.get(index)).add(arr[i]);
        }
        ArrayList bucket = null;
        int index = 0;
        for (int i = 0;i < bucketNum; i++){
            bucket = (ArrayList)buckList.get(i);
            insertSort(bucket);
            for (int k : bucket) {
                arr[index++]=k;
            }
        }
        System.out.print("桶排序后数组:");
        System.out.println(Arrays.toString(arr));
    }

    /**
     * @param a 待排序数组元素
     * @param step 步长(桶的宽度/区间),具体长度可根据情况设定
     * @return 桶的位置/索引
     */
    private static int indexFor(int a, int min, int step) {
        return (a-min) / step;
    }

    /**
     * 把桶内元素插入排序
     */
    private static void insertSort(List bucket) {
        for (int i=1;i=0 && bucket.get(j)>temp; j--){
                bucket.set(j+1, bucket.get(j));
            }
            bucket.set(j+1, temp);
        }
    }

 

你可能感兴趣的:(常用排序算法整理)