七大排序

排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
平时的上下文中,如果提到排序,通常指的是排升序(非降序)。
通常意义上的排序,都是指的原地排序(in place sort)
在这里插入图片描述

插入排序

每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入。

定义两个指针 i j,在定义一个temp存储遍历到i的值,从第二个数开始排序,默认第一个为有序,i控制第一个数字,j控制向前比较,当i的值<j的值时,把j的值向后挪动,j–,如果大于则break内循环,将j+1的值变成temp。

/**
     * 时间复杂度:最坏:O(n^2),最好O(n)。
     * 空间复杂度:O(1)
     */
public static void main(String[] args) {
    for (int i = 1; i < elem.length; i++) {
        int temp = elem[i];
        int j = i-1;
        for (; j >= 0 ; j--) {
            if (elem[j] > temp){
                elem[j + 1] = elem[j];
            }else {
                break;
            }
        }
        elem[j + 1] = temp;
    }
    System.out.println(Arrays.toString(elem));
}

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所
有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1
时,所有记录在统一组内排好序。

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
 public static void shell(int[] array,int gap) {

        for (int i = gap; i < array.length; i ++) {
            int temp = array[i];
            int j = i - gap;
            for (; j >= 0 ; j -= gap) {
                if (array[j] > temp){
                    array[j + gap] = array[j];
                }else {
                    break;
                }
            }
            array[j + gap] = temp;
        }
    }

    public static  void  shellSort(int[] array) {

        int[] drr = {5,3,1};
        for (int i = 0; i < drr.length; i++) {
            shell(array,drr[i]);
        }

    }

选择排序

每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完 。
选择排序是从前往后排序,选定一个元素,在从前往后挨个比较,只要小于当前元素,就交换,一次遍历确定当前元素为当前元素到数组尾中的最小值。遍历数组长度的变数,数组就有序了。

/**
     * 时间复杂:O(n^2)
     * 空间复杂度O(1)
     * 不稳点,交换是跨越式交换
     * @param array
     */
    public void sort(int[] array){
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = i + 1 ; j < array.length; j++) {
                if (array[i] > array[j]){
                    int temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }

    }

堆排序

基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆
堆排序是使用了堆这个数据结构,给定一个数组,先通过这个数组构建一个大顶堆或者小顶堆。(从小到大排序时是大顶堆,反之亦然)。构建好了以后堆顶就是当前数组最大或最小的元素,然后进行排序。
定义一个end,标记当前堆的堆底元素,先把堆顶的元素与end互换位置,当前堆底就是有序的了(虽然只有一个元素,但从小到大排序,最大的在最后,所以是有序的了),然后将end–。
只要重复将堆顶元素与end交换,交换后堆会自动将最大或最小的放到堆顶,所有从后往前数组就变的有序了。

这里有一个模拟堆排序动画
堆排序

/**
 * 时间复杂度:O(N*logn)
 * 空间复杂度:O(1)
 */
public static void creatHeap(int[] array,int len){
    for (int i = (len -2) / 2; i >= 0; i--) {
        adjustDown(array,i,len);
    }
}

private static void adjustDown(int[] array, int i, int len) {
    int parent = i;
    int child = parent *2 +1;
    while (child < len){
        if (child + 1 < len && array[child] > array[child + 1])
            child++;
        if (array[child] < array[parent]){
            int temp = array[child];
            array[child] = array[parent];
            array[parent] = temp;
            parent = child;
            child = child * 2+1;
        }else {
            break;
        }
    }
}
private static void sort(int[] array){
    int end = array.length - 1;
    while (end > 0){
        int temp = array[0];
        array[0] = array[end];
        array[end] = temp;
        adjustDown(array,0,end);
        end--;
    }
}

冒泡排序

在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
双重循环:定义两个指针,一个控制数组当前当前第几个数字,一个控制需要比较元素,通过比较把当前最大的值冒泡到数组的最后,通过多次冒泡,数组就可变的有序。
冒泡排序是可以优化的:当某一次循环过后数组已经有序了,就可以直接退出,不用继续冒泡。

public void sort(int[] array){
    boolean isSort;
    for (int i = 0; i < array.length-1; i++) {
        isSort = false;
        for (int j = 0; j < array.length-1-i; j++) {
            if (array[j] > array[j + 1]){
                int temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
                isSort = true;
            }
        }
        if (!isSort)break;
    }
}

冒泡排序时间复杂度:O(n^2)最好情况为O(n)。
空间复杂度为O(n)。

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

递归把原数组分成一个一个小数组,在拼回来的时候进行排序,新建一个数组存储排序后的数组,遍历两个数组按序放入新建的数组,然后将排好序的数组元素复制到原有数组,就完成了排序。

/**
 * 先将数组分成一个一个的数组,在把分开的一个一个排序,然后往上返回,最后成有序的
 * 时间复杂度:O(nlogn)
 * 空间复杂度:O(n)
 * 稳点的排序。
 */
public static void spilit(int[] array,int left,int right){
        if (left >= right)return;
        int mid = (left + right) / 2;
        spilit(array,left,mid);
        spilit(array,mid+1,right);
        merge(array, left, mid, right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        int s1 = left;
        int s2 = mid + 1;
        int[] res = new int[right - left + 1];
        int i = 0;
        while (s1 <= mid && s2 <= right){
            if (array[s1] <= array[s2]){
                res[i++] = array[s1++];
            }else {
                res[i++] = array[s2++];
            }
        }

        while (s1 <= mid){
            res[i++] = array[s1++];
        }

        while (s2 <= right){
            res[i++] = array[s2++];
        }

        for (int j = 0; j < res.length; j++) {
            array[left + j] = res[j];
        }
    }
     /**
     * 非递归归并排序
     * @param array
     */
    public static void mergeSort(int[] array) {
        for (int gap = 1; gap < array.length; gap *= 2) {
            mergeNor(array,gap);
        }
    }

    public static void mergeNor(int[] array,int gap) {
        int[] ret = new int[array.length];
        int k = 0;//ret的下标
        int s1 = 0;
        int e1 = s1 + gap - 1;
        int s2 = e1 + 1;
        int e2 = s2 + gap-1 < array.length ? s2+gap-1 :  array.length-1;

        //1、肯定是有两个归并段的
        while (s2 < array.length) {
            //2、对应的s1位置和s2位置进行比较
            while (s1 <= e1 && s2 <= e2) {
                if(array[s1] <= array[s2]) {
                    ret[k++] = array[s1++];
                }else {
                    ret[k++] = array[s2++];
                }
            }
            //3、上述第2步在比较的过程当中,肯定会有一个下标先走完一个归并段

            //4、判断是谁没走完,把剩下的数据拷贝到结果数组当中
            while (s1 <= e1) {
                ret[k++] = array[s1++];
            }

            while (s2 <= e2) {
                ret[k++] = array[s2++];
            }

            //5、接着确定新的s1,e1,s2,e2
            s1 = e2 + 1;
            e1 = s1 + gap - 1;
            s2 = e1 + 1;
            e2 = s2+gap-1 < array.length ? s2+gap-1 :  array.length-1;
        }

        while (s1 < array.length){
            ret[k++] = array[s1++];
        }

        for (int j = 0; j < ret.length; j++) {
            array[j] = ret[j];
        }



    }

快速排序

  1. 从待排序区间选择一个数,作为基准值(pivot);
  2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
  3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。
    定义左右两个指针。先分别指向数组的头尾,然后将左指针的元素当做tep,从右指针开始,不停–,碰到小于tep的就停止,和左指针交换,左指针不停++,遇到大于tep的停止,将值赋予当前右指针的位置,直到两个指针相遇,将tep的值放到相遇的地方。然后继续递归,分为左右两边,左边的right为相遇的-1,右边的left为相遇的+1.最后直至有序。
    快速排序在原数组就趋于有序时,会进入到多层递归,可能会产生栈溢出,所以可以在多次排序后直接使用插入排序,因为数组趋于有序时,插入排序的时间复杂度可以趋于O(N)。也可以使用几数取中(例如三数取中):array[left], array[mid], array[right] 大小是中间的为基准值,将mid的值小于left和right的值,这样在递归的时候,就可以在数组偏中间分隔,不会递归很多层。
 /**
     * 时间复杂度:O(nlogn)最坏:O(n^2)
     * 空间复杂度:O(longn)最坏:O(n)
     * @param array
     * @param low
     * @param high
     * @return
     */
public static int partition(int[] array,int low,int high) {
    if (low >= high)return low;
    int temp = array[low];
    while (low < high){
        while (array[high] > temp && low < high){
            high--;
        }
        array[low] = array[high];
        while (array[low] <= temp && low < high){
            low++;
        }
        array[high] = array[low];

    }
    array[low] = temp;

    return low;
}

public static void three_num_mid(int[] array,int left,int right) {
    //array[mid] <= array[left] <= array[high]
    int mid = (left + right) / 2;
    if (array[mid] <= array[left]
            && array[left] <= array[right])return;


}

public static void quick(int[] array,int left,int right) {
    if(left >= right) {
        return;
    }
    int partition = partition(array,left,right);
    quick(array,left,partition - 1);
    quick(array,partition+1,right);

}
public static void quickSort1(int[] array) {
    quick(array,0,array.length-1);
}

七种排序的比较

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值