数据结构中,排序的方法

首先是快速排序:

package sorting;

import java.util.Arrays;

public class Sort1 {
    public static void quickSort(long[] array) {
        quickSortRange(array, 0, array.length - 1);
    }

    // 为了代码书写方便,我们选择使用左闭右闭的区间表示形式
    // 让我们对 array 中的从 from 到 to 的位置进行排序,其他地方不用管
    // 其中,from,to 下标的元素都算在区间的元素中
    // 左闭右闭的情况下,区间内的元素个数 = to - from + 1;
      /*
    时间复杂度: partition 的时间复杂度 O(n) * 树的高度
    最好/平均: O(n * log(n))
    最坏: O(n * n)

    (快排是唯一一个空间复杂度也分情况讨论的排序算法)
    空间复杂度:partition 的空间复杂度 O(1) * 树的高度
    最好/平均: O(log(n))
    最坏: O(n)

    稳定性:保证不了,因为没有可以在做到 O(n) 的情况下,还能保证稳定性的 partition 算法。
     */
    private static void quickSortRange(long[] array, int from, int to) {
//        if (to <= from) {
//        if (to - from < 1) {
//        if (to - from <= 0) {
        if (to - from + 1 <= 1) {
            // 区间中元素个数 <= 1 个
            return;
        }

        // 挑选中区间最右边的元素 array[to]
        // array[to] 还是 array[to - 1] 还是 array[array.length] 还是 array[array.length - 1] 呢?
        int[] pi = partitionMethodD(array, from, to);
        // 小于等于 pivot 的元素所在的区间如何表示 array, from, pi - 1
        // 大于等于 pivot 的元素所在的区间如何表示 array, pi + 1, to

        // 按照分治算法的思路,使用相同的方式,处理相同性质的问题,只是问题的规模在变小
        quickSortRange(array, from, pi[0]);    // 针对小于等于 pivot 的区间做处理
        quickSortRange(array, pi[1], to);   // 针对大于等于 pivot 的区间做处理
    }

    /**
     * 以区间最右边的元素 array[to] 最为 pivot,遍历整个区间,从 from 到 to,移动必要的元素
     * 进行分区
     * @param array
     * @param from
     * @param to
     * @return 最终 pivot 所在的下标
     */
    private static int partitionMethodA(long[] array, int from, int to) {
        // 1. 先把 pivot 找出来
        long pivot = array[to];
        // 2. 通过定义 left 和 right 两个下标,将区间划分出来
        int left = from;
        int right = to;
        // [from, left)   都是 <= pivot 的
        // [left, right)  都是未参与比较的
        // [right, to]    都是 >= pivot 的

        // 循环,保证每个元素都参与了和 pivot 的比较
        // 也就是,只要 [left, right) 区间内还有元素,循环就应该继续
        while (left < right) {
//        while (right - left > 0) {
            // 先让左边进行比较

            // 随着 left 在循环过程中一直在 left++,请问 left < right 的条件能一定保证么
            // 不一定,所以,我们时刻进行 left < right 条件的保证
            // 并且,只有在 left < right 成立的情况下,array[left] 和 pivot 的比较才有意义
            // left < right && array[left] <= pivot 的顺序不能交换
            while (left < right && array[left] <= pivot) {
                left++;
            }
            // 循环停止时,说明 array[left] > pivot

            while (left < right && array[right] >= pivot) {
                right--;
            }
            // 循环停止时,说明 array[right] < pivot

            // 两边都卡住时,交换 [left] 和 [right] 位置的元素
            long t = array[left];
            array[left] = array[right];
            array[right] = t;
        }

        // 说明 left == right,说明 [left, right) 区间内一个元素都没有了
        // 所有元素都和 pivot 进行过比较了,然后都在各自应该的位置上了
        // 并且 array[left] 一定是 >= pivot 的第一个元素(不给大家证明了)
        long t = array[to];
        array[to] = array[left];
        array[left] = t;

        // 返回 pivot 最终所在下标
        return left;
    }
    public  static  int partitionMethodB(long[]array,int form,int to){
        long pi=array[to];
        int left=form;
        int right=to;
        while (left<right){
            while (left<right&&array[left]<pi) {
                left++;
            }
                array[right]=array[left];
            while (left<right&&array[right]>pi) {
                right--;
            }
            array[left]=array[right];
        }
        array[to]=array[left];
        array[left]=pi;
        return left;
    }
    /**
     * 对 array 的 [from, to] 区间进行分区
     * 分区完成之后,区间被分割为 [<= pivot] pivot [>= pivot]
     * 分区过程中,始终保持
     * [from, s)    小于 pivot
     * [s, i)       大于等于 pivot
     * [i, to)      未比较过的元素
     * [to, to]     pivot
     * @param array
     * @param from
     * @param to
     * @return pivot 最终所在下标
     */
    private static int partitionMethodC(long[] array, int from, int to) {
        int s = from;
        long pivot = array[to];
        for (int i = from; i < to; i++) {   // 遍历 [from, to)
            // 这里加 == 号也保证不了稳定性,有交换操作
            if (array[i] < pivot) {
                // TODO: 可以进行简单的优化:如果 i == s,就不交换
                long t = array[i];
                array[i] = array[s];
                array[s] = t;

                s++;
            }
        }

        array[to] = array[s];
        array[s] = pivot;

        return s;
    }
public static int[]  partitionMethodD(long[]array,int form,int to){
        int i=form;
        int g=to;
        int s=form;
        long pri=array[to];
        while (i<g){
            if(array[i]==pri){
                i++;
            }else if(array[i]>pri){
                long e=array[i];
                array[i]=array[g];
                array[g]=e;
                g--;
            }
            else {
                long a=array[i];
                array[i]=array[s];
                array[s]=a;
                i++;
                s++;
            }
        }
        return new int[]{s-1,i};
}
public static void insertSortRange(long[]array,int form,int to){
        int size=to-form;
        for(int i=0;i<size;i++){
            long key=array[form+1+i];
            int j;
            for(j=form+i;j>=form&&array[j]>key;j--){
                array[j+1]=array[j];
            }
            array[j+1]=key;
        }
}

    public static void main(String[] args) {
    // long[] array = {-1, -1, -1, -1, 3, 5, 2, 7, 6, 4, 9, 8, -1, -1, -1 };
//       long[] array = {-1, -1, -1, -1, 3, 5, 2, 7, 6, 4, 9, 4, -1, -1, -1 };
     long[] array = {-1, -1, -1, -1, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1 };
       // long[] array = {-1, -1, -1, -1, 8, 7, 6, 5, 4, 3, 2, 1, -1, -1, -1 };

        insertSortRange(array,4,11);
        System.out.println(Arrays.toString(array));
    }
}

冒泡排序:

public static void sort(long[] array, int size) {
    for (int i = 0; i < array.length - 1; i++) {
        for (int j = 0; j < array.length - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                swap(array, j, j + 1);
            }

        }
    }
}

public static void swap(long[] array, int a, int b) {
    long e = array[a];
    array[a] = array[b];
    array[b] = e;
}

插入排序:

public static void swap(long[] array, int a, int b) {
    long e = array[a];
    array[a] = array[b];
    array[b] = e;
}

public static void inserSort(long[] array) {
    // 一共要取多少个元素来进行插入过程(无序区间里有多少个元素)
    for (int i = 0; i < array.length - 1; i++) {
        // 有序区间 [0, i]  至少在 i == 0 的时候得有一个元素
        // 无序区间 [i + 1, n)

        // 先取出无序区间的第一个元素,记为 k
        long k = array[i + 1];

        // 从后往前,遍历有序区间{
        // 找到合适的位置退出
        // 所谓合适的位置,就是第一次 k >= array[j] 的位置
        int j;
        for (j = i; j >= 0 && k < array[j]; j--) {
            array[j + 1] = array[j];        // 将不符合条件的数据往后般一格
        }

        array[j + 1] = k;
    }
}

希尔排序:

public static void inserSortWithGap(long[] array, int gap) {
    // 外围的循环次数是 n - gap 次
    for (int i = 0; i < array.length - gap; i++) {
        // 一共有 gap 个分组
        // 认为一开始 [0, i + gap) 有序
        // [i + gap, n) 无序
        long k = array[i + gap];

        int j;
        // j 下标只去找同一组的元素比较,所以每次跳过 gap
        for (j = i; j >= 0 && k < array[j]; j = j - gap) {
            array[j + gap] = array[j];
        }

        array[j + gap] = k;
    }
}

public static void shellSort(long[] arry) {
    int gap = arry.length / 2;
    while (gap > 1) {
        inserSortWithGap(arry, gap);
        gap = gap / 2;
    }
    inserSort(arry);
}

选择排序:

public static void selectSort(long[] array) {
    // 每次选择出最大的数,放到最后去
    // 一共要选择出 n - 1 个数
    for (int i = 0; i < array.length - 1; i++) {
        // 通过遍历无序区间,只需要找到最大的数最在的位置就行(以下标的形式体现)不要做交换
        // 无序 [0, n - i)
        int maxIndex = 0;   // 假设最大的数放在一开始
        for (int j = 1; j < array.length - i; j++) {
            if (array[j] > array[maxIndex]) {
                // 说明,找到了无序区间的新的最大的数
                // 所以,记录最大数的下标
                maxIndex = j;
            }
        }

        // 遍历完成之后,无序区间的最大的数就放在 maxIndex 所在下标处
        // array[maxIndex] 是最大数

        // 交换 [maxIndex] 和 无序区间的最后一个元素 [n - i - 1]
        swap(array, maxIndex, array.length - i - 1);
    }
}

public static void swap(long[] array, int a, int b) { long e = array[a]; array[a] = array[b]; array[b] = e; }

堆排序:

 public static void heapSort(long[]array,int index){
        // 1. 建立大堆
        createBigHeap(array);

        // 2. 遍历 n - 1 次
        for (int i = 0; i < array.length - 1; i++) {
            // 2.1 交换之前的无序区间 [0, n - i)
            swap(array, 0, array.length - i - 1);
            // 交换之后的无序区间 [0, n - i - 1),元素个数 n - i - 1 个
            // 2.1 对堆的 [0] 进行向下调整,堆里的元素个数就是无序区间的元素个数
            shiftDown(array, array.length - i - 1, 0);
        }
    }

    private static void shiftDown(long[] array, int size, int index) {
        while (2 * index + 1 < size) {
            int maxIdx = 2 * index + 1;
            int right = maxIdx + 1;
            if (right < size && array[right] > array[maxIdx]) {
                maxIdx = right;
            }

            if (array[index] >= array[maxIdx]) {
                return;
            }

            swap(array, index, maxIdx);

            index = maxIdx;
        }
    }

    private static void createBigHeap(long[] array) {
        // 从最后一个元素的双亲开始
        for (int i = (array.length - 2) / 2; i >= 0; i--) {
            shiftDown(array, array.length, i);
        }
    }

}

归并排序:

 public static void mergeSort(long[] array) {
        mergeSortRange(array, 0, array.length);
    }

    // 这里的 [from, to) 是左闭右开来表示区间的
    private static void mergeSortRange(long[] array, int from, int to) {
        int size = to - from;
        // 情况1:如果区间内元素个数 <= 1 个,什么都不需要做
        if (size <= 1) {
            return;
        }

        // 其他情况
        // 1. 找到区间的中间位置的下标
        int mid = from + (size / 2);

        // 2. 优先对左 [from, mid) 和右 [mid, to) 两个小区间先进行排序(使用同样的方式处理:分治思想)
        mergeSortRange(array, from, mid);
        mergeSortRange(array, mid, to);

        // 3. 有了两个分别各自有序的小区间 [from, mid) 和 [mid, to)
        //    通过一定的方式,将 [from, mid) [mid, to) 合并到 [from, to) 都是有序的
        //    两个有序数组区间合并到一个有序数组区间(需要结果放回原地去)
        merge(array, from, mid, to);
    }

    private static void merge(long[] array, int from, int mid, int to) {
        // 先计算出来额外空间需要多个,计算两个区间加起来多少个元素
        int size = to - from;
        // 申请一个额外的数组作为临时保存的地方
        long[] other = new long[size];

        int left = from;    // 左边小区间的下标
        int right = mid;    // 右边小区间的下标
        int dest = 0;       // 临时空间的下标

        // 只要左右两个小区间还有元素要参与比较
        while (left < mid && right < to) {
           if (array[left] <= array[right]) {
                other[dest++] = array[left++];
            } else  {
                other[dest++] = array[right++];
            }
        }

        // 其中一个区间的元素取完了,另一个区间里一定还有元素,再把剩余的元素,统统放入 other
        // 看起来左右两个都写了,但执行起来的时候一定只有一个会执行
        while (left < mid) {
            other[dest++] = array[left++];
        }
        while (right < to) {
            other[dest++] = array[right++];
        }

        // 把 other 中的有序元素,复制回 array 中,要注意下标的问题
        for (int i = 0; i < size; i++) {
            array[from + i] = other[i];     // array 下标的基准是从 [from] 开始,other 下标的基准是从 [0] 开始
            // offset(偏移)是一致的,都是 i
        }
    }

    public static void main(String[] args) {
        long[] array = { 9, 5, 8, 7, 1, 6, 0, 4, 2, 3 };
        mergeSort(array);
        System.out.println(Arrays.toString(array));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值