八大排序总结和java实现

八大排序分类

按照稳定性分类:
稳定性指的是,在等待排序的一堆数当中,有几个值相同的元素,排序之后,如果相等元素之间的先后顺序不会发生改变,排序就是稳定的。 如果先后顺序发生改变,排序就是不稳定的。
稳定的排序算法有:冒泡排序,插入排序,归并排序,基数排序。
不稳定的排序算法有:希尔排序,选择排序,堆排序和快速排序。

按照时间复杂度分类:
O(n^2)级别:插入排序,选择排序,冒泡排序 ,希尔排序
O(nlogn)归并排序,堆排序和快速排序

插入排序

每次都把当前元素插入到左边已经排好序的数组当中,保证插入之后,左边的数组仍然有序。
插入元素和数组的初始顺序有关,如果数组已经部分有序了,需要交换的元素少。
最好情况下,数组已经有序,只需要N-1次比较,0次交换。
最坏情况下,数组是逆序的,需要n*(n-1)/2次交换,是N^2级别额交换次数。
平均时间复杂度是O(n^2)
稳定性分析:如果插入元素和前面的元素相同,会把插入元素放到相同元素的后面,相等元素的先后顺序没有发生改变,所以是插入排序是稳定的排序。

插入排序的算法步骤:
1、从第一个元素开始,认为这个元素已经被排序了
2、取出1下一个元素,在已经排序的元素里,从后往前扫描
3、在已经排序的数组中,如果这个元素大于新元素,就把这个元素移动到下一个位置。
4、一直往前找,如果找到一个元素小于等于新元素,就停止。
5、把新元素插入到这个位置。然后重复以上操作。

package com.company;
import java.util.Arrays;
public class Sort {
    public static void main(String[] args) {
        int[] arr = {2, 4, 6, 7, 1, 3};
        System.out.println("Before sort" + Arrays.toString(arr));
        insertSort(arr);
        System.out.println("After sort" + Arrays.toString(arr));

    }
    public static void insertSort(int[] arr) {
        for(int i = 1; i < arr.length; i++) {
            //取出下一个元素,已经排序的序列中从后前移动
            int temp = arr[i];
            for(int j = i; i >= 0; j--) {
                if((j > 0) && (arr[j - 1] > temp)) {
                    arr[j] = arr[j - 1];//把元素移动到后面的元素
                    System.out.println("Temping" + Arrays.toString(arr));
                } else {
                    //把新元素插入到这个位置之后
                    arr[j] = temp;
                    System.out.println("sorting" + Arrays.toString(arr) );
                    break;//停止这次循环
                }
            }
        }
    }
}

运行结果如下

Before sort[2, 4, 6, 7, 1, 3]
sorting[2, 4, 6, 7, 1, 3]
sorting[2, 4, 6, 7, 1, 3]
sorting[2, 4, 6, 7, 1, 3]
Temping[2, 4, 6, 7, 7, 3]
Temping[2, 4, 6, 6, 7, 3]
Temping[2, 4, 4, 6, 7, 3]
Temping[2, 2, 4, 6, 7, 3]
sorting[1, 2, 4, 6, 7, 3]
Temping[1, 2, 4, 6, 7, 7]
Temping[1, 2, 4, 6, 6, 7]
Temping[1, 2, 4, 4, 6, 7]
sorting[1, 2, 3, 4, 6, 7]
After sort[1, 2, 3, 4, 6, 7]

归并排序

归并排序是分治思想的典型应用,把一个问题分解成多个子问题,然后把子问题再次分解成更小的子问题。
把等待排序的序列分成很多个子序列,每一个子序列是有序的,然后把有序的子序列合并成整体有序的序列。
时间复杂度分析。对于长度为n的数组,拆分数组的时间复杂度是logn,合并子数组的过程时间复杂度是O(n),所以整体的时间复杂度是O(nlogn)。

空间复杂度分析:递归过程中拆分的数组需要保存在内存中,空间复杂度是O(n)

从排序的流程可以看出,归并排序的性能和数据的状态无关,时间复杂度始终都是O(nlogn)。

package com.company;
import java.util.Arrays;
public class Sort {
    public static void main(String[] args) {
        int[] arr = {12, 11, 10, 2, 4, 6, 7, 1, 3};
        System.out.println("Before sort" + Arrays.toString(arr));
        //insertSort(arr);
        mergeSort(arr, 0, arr.length - 1);
        System.out.println("After sort" + Arrays.toString(arr));

    }
    

    public static void mergeSort(int[] arr, int left, int right ) {
        int mid = left + (right - left)/2;
        if(left < right) {
            //左半边归并排序
            mergeSort(arr, left, mid);
            //右半边归并排序
            mergeSort(arr, mid+1, right);
            merge(arr, left, mid, right);
        }
    }
    public static void merge(int[] arr, int left, int mid , int right) {
        //创建临时数组
        int[] temp = new int[right - left + 1];
        int i = left;
        int j = mid + 1;
        int k = 0;

        //把比较小的数先移入数组
        while((i <= mid) && (j <= right)) {
            if (arr[i] < arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }

            //把左边的元素移入数组
            while(i <= mid) {
                temp[k++] = arr[i++];
            }
            //把右边的元素移入数组
            while(j <= right) {
                temp[k++] = arr[j++];
            }


        for(int k2 = 0; k2 <temp.length; k2++) {
            arr[left + k2] = temp[k2];
        }

    }
}

运行结果是

Before sort[12, 11, 10, 2, 4, 6, 7, 1, 3]
After sort[1, 2, 3, 4, 6, 7, 10, 11, 12]

快速排序

package com.company;
import java.util.Arrays;
public class Sort {
    public static void main(String[] args) {
        int[] arr = {12, 11, 10, 2, 4, 6, 7, 1, 3};
        System.out.println("Before sort" + Arrays.toString(arr));
        //insertSort(arr);
        //mergeSort(arr, 0, arr.length - 1);
        quickSort(arr, 0, arr.length - 1);
        System.out.println("After sort" + Arrays.toString(arr));

    }
    


    public static void quickSort(int[] arr, int start, int end) {

        if(arr == null || start >= end) {
            return ;
        }

        int partitionIndex = divide(arr, start, end);
        quickSort(arr, start, partitionIndex - 1);
        quickSort(arr, partitionIndex + 1, end);

    }
    public static int divide(int[] arr, int start, int end) {
        //选择基准元素
        int base = arr[end];
        //两个指针不相遇,就判断
        while(start < end) {
            while(start < end && arr[start] <= base) {
                start++;
            }
            if(start < end) {
                int temp = arr[start];
                arr[start] = arr[end];
                arr[end] = temp;
                end--;
            }
            while(start < end && arr[end] >= base) {
                end--;
            }
            if(start < end) {
                int temp = arr[start];
                arr[start] = arr[end];
                arr[end] = temp;
                start++;
            }

        }
        //相遇就返回
        return end ;
    }
}

代码运行结果

Before sort[12, 11, 10, 2, 4, 6, 7, 1, 3]
After sort[1, 2, 3, 4, 6, 7, 10, 11, 12]

堆排序

堆排序需要两个过程,一是建立堆,另一个是调整堆。

初始化堆的时间复杂度是O(n),更改堆元素之后,建立堆,需要循环n-1次,每次是从根节点往下寻找,所以时间复杂度是O(logn),所以整体的时间复杂度是)O(nlogn) 没有用到辅助空间,堆排序是原地排序,时间复杂度是O(1)

package com.company;
import java.util.Arrays;
public class Sort {
    public static void main(String[] args) {
        int[] arr = {12, 11, 10, 2, 4, 6, 7, 1, 3};
        System.out.println("Before sort" + Arrays.toString(arr));
        //insertSort(arr);
        //mergeSort(arr, 0, arr.length - 1);
        //quickSort(arr, 0, arr.length - 1);
        heapSort(arr);
        System.out.println("After sort" + Arrays.toString(arr));

    }
    

    
    public static void heapSort(int[] arr) {
        int n = arr.length;
        //最后一个有子节点的元素,下标是n/2 - 1.。这些元素都是根节点,有孩子节点,所以heapify的最后一项根节点位置是根据i变化的
        for(int i = n/2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }
        for(int i = n-1; i >= 0; i--) {
            //大顶堆的0位置元素最大,把最大的放到数组的最后
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            //调整的堆的大小是不断变化的,堆顶下标始终是0,堆调整的范围在缩小
            heapify(arr, i, 0);
        }
    }
    public static void heapify(int[] arr, int n, int i) {
        int largest = i;//把最大值的下标,初始化为i
        int l = 2*i + 1;
        int r = 2*i + 2;

        //内部不做循环,外层调用者的来控制调用heapify的次数
            if(l < n && arr[l] > arr[largest]) {
                largest = l;
            }
            if(r < n && arr[r] > arr[largest]) {
                largest = r;
            }
            if(largest != i) {
                int temp = arr[i];
                arr[i] = arr[largest];
                arr[largest] = temp;

                heapify(arr, n, largest);
            }

    }
}

运行结果

Before sort[12, 11, 10, 2, 4, 6, 7, 1, 3]
After sort[1, 2, 3, 4, 6, 7, 10, 11, 12]

参考资料

八大排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值