常见的七种排序算法

具体思想可看视频理解
排序舞蹈

1. 插入排序

  • 原理
    整个区间被分为
    1. 有序区间
    2. 无序区间
      每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入
  • 实现
	private void insertsort(int[] arr){
        for (int i = 1; i < arr.length; i++){
            int j = i-1;
            int num = arr[i];
            for (;j>=0;j--){
                if (arr[j] <= num){  //如果比i位置的值大或等于就退出
                    break;
                }
                arr[j+1] = arr[j];  //都是比i位置小的情况
            }
            arr[j+1] = num;
        }
    }

2. 希尔排序

  • 原理

先选定一个整数gap,把待排序文件中所有记录分成gap个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,取重复上述分组和排序的工作。当到达gap=1时,所有记录在统一组内排好序。

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  • 实现
	public void hillsort(int[] arr){ 
	//插入的进阶版,实质和插入差不多,但效率明显不一样
        for (int gap = arr.length/2;gap>0;gap/=2){
            for (int i = 1;i < arr.length;i++){
                int j = i - gap;
                int num = arr[i];
                for (; j >= 0&& arr[j] > num; j -= gap){
                    arr[j + gap] = arr[j];
                }
                arr[j + gap] = num;
            }
        }
    }

3. 选择排序

  • 原理

    每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完

  • 实现
	//了解了思想,这儿就别跟我扯你看不懂代码
	private void selectsort(int[] arr){
        for (int i = 0;i < arr.length; i++){
            int min = i;
            for (int j = i;j<arr.length;j++){
                if (arr[j] < arr[min]){
                    min = j;
                }
            }
            exange(arr,i,min);
        }
    }
    
    private void exange(int[] arr,int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

4. 堆排序

  • 原理

基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆。

  • 实现
	private void heapsort(int[] arr){
        createheap(arr);		//先建个大堆
        for (int i=0;i<arr.length;i++){
            exange(arr,0,arr.length-i-1);
            shiftdown(arr,arr.length-i-1,0);
        }
    }

    private void createheap(int[] arr) {
        for (int i=(arr.length-1)/2;i>=0;i--){	//一个一个往上走
            shiftdown(arr,arr.length,i); //一层一层往下走
        }
    }

    private void shiftdown(int[] arr, int size, int index) {
        int left = 2 * index+1;
        while (left<size){
            int max = left;
            int right = 2*index+2;
            if (size>right){
                if (arr[right] > arr[left]){
                    max = right;
                }
            }
            if (arr[index]>=arr[max]){ //大堆的子树肯定没有根的值大
                break;
            }
            exange(arr,index,max);	//有必要时就找最大子根节点与其替换
            index=max;
            left=2*index+1;
        }
    }
    private void exange(int[] arr,int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

5. 冒泡排序

  • 原理

在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序

  • 实现
	private void bubblesort(int[] arr){
        for (int i = 0; i < arr.length; i++){
            boolean issorted = true;
            for (int j = 0; j < arr.length-i-1; j++){
                if (arr[j]>arr[j+1]){
                    exange(arr,j,j+1);
                    issorted = false;
                }
            }
            if (issorted){
                return;
            }
        }
    }

6. 快速排序

  • 原理
  1. 从待排序区间选择一个数,作为基准值(pivot);
  2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
  3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。
  • 实现
递归版本
	private void quicksort(int[] arr){
        quickedhead(arr,0,arr.length-1);
    }

    private void quickedhead(int[] arr, int left, int right) {
        if (left>=right){
            return;
        }
        int i = left;
        int j = right;
        int par = arr[right];
        while (i<j){
            while (i<j&&arr[i]<=par){
                i++;
            }
            while (i<j&&arr[j]>=par){
                j--;
            }
            exange(arr,i,j);
        }
        exange(arr,i,right); //前面有
        quickedhead(arr,left,i-1);
        quickedhead(arr,i+1,right);
    }

//--第二种
  public static void quickSort(int[] array){
        quickSortHelp(array,0,array.length-1);
    }
    public static void quickSortHelp(int[] a,int left,int right){
        if (left >= right)//如果左边的值大于右边的值说明已经完成了一组的执行
        {
            return;
        }
        int temp = a[left];//将第一个元素设为对比元素(参考值)
        int i = left;
        int j = right;
        while (i < j)
        {
            while (i<j && a[j]>temp)//如果这个参考值小于右边所比较的元素,则j--,进行右边的下一个元素与参考值比较
            {
                j--;
            }
            a[i] = a[j];  //找到了一个比参考值小的数,把这个数放在前面  (原来取参考值的位置)
            while (i <j && a[i] <= temp)//如果参考值大于等于右边所比较的元素,则i++;进行下一个元素与参考值的比较
            {
                i++;
            }
            a[j] = a[i];  //找到了一个比参考值大的数,把这个数放到后面(刚刚放再前面那个数的位置)
        }
        a[i] = temp;   //当在当组内找完一遍以后就把中间数temp回归
        quickSortHelp(a, left, i - 1);  //最后用同样的方式对分出来的左边的小组进行同上的做法
        quickSortHelp(a, j + 1, right);  //用同样的方式对分出来的右边的小组进行同上的做法

    }

非递归版本
	private void quickheap(int[] arr){
        Stack<Integer> stack = new Stack<>();
        stack.push(arr.length-1);
        stack.push(0);
        while (!stack.isEmpty()){
            int left = stack.pop();
            int right = stack.pop();
            if (left>=right){
                continue;
            }
            int mid = parheap(arr,left,right);
            stack.push(mid-1);
            stack.push(left);
            stack.push(right);
            stack.push(mid+1);
        }
    }

    private int parheap(int[] arr, int left, int right) {
        if (left>=right){
            return -1;
        }
        int i = left;
        int j = right;
        int par = arr[right];
        while (i<j){
            while (i<j&&arr[i]<=par){
                i++;
            }
            while (i<j&&arr[j]>=par){
                j--;
            }
            exange(arr,i,j);
        }
        exange(arr,i,right);
        return i;
    }

7. 归并排序 (外部排序)

  • 原理

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

  • 实现
递归版本
	private void addarray(int[] arr, int left, int mid, int right){
        if (left>=right){
            return;
        }
        int i = left, j=mid, k=0;
        int length = right-left;
        int[] extra = new  int[length];
        while (i < mid && j < right){
            extra[k++] = (arr[i]<=arr[j])?arr[i++]:arr[j++];
        }
        while (i<mid){
            extra[k++] = arr[i++];
        }
        while (j<right){
            extra[k++] = arr[j++];
        }
        for (int t=0;t<extra.length;t++){
            arr[t+left] = extra[t];
        }
    }

    private void mergesort(int[] arr){
        mergesortheap(arr,0,arr.length);
    }
    private void mergesortheap(int[] arr, int left, int right) {
        if (right-left<=1){
            return;
        }
        int mid = (left+right)/2;
        mergesortheap(arr,left,mid);
        mergesortheap(arr,mid,right);
        addarray(arr,left,mid,right);
    }
非递归版本
	private void addarray(int[] arr, int left, int mid, int right){
        if (left>=right){
            return;
        }
        int i = left, j=mid, k=0;
        int length = right-left;
        int[] extra = new  int[length];
        while (i < mid && j < right){
            extra[k++] = (arr[i]<=arr[j])?arr[i++]:arr[j++];
        }
        while (i<mid){
            extra[k++] = arr[i++];
        }
        while (j<right){
            extra[k++] = arr[j++];
        }
        for (int t=0;t<extra.length;t++){
            arr[t+left] = extra[t];
        }
    }
    
	private void mergeedsort(int[] arr){
        for (int i = 1;i<arr.length;i*=2){
            for (int j = 0;j<arr.length;j=j+2*i){
                int left = j;
                int mid =left+i;
                if (mid>=arr.length){
                    continue;
                }
                int right = mid+i;
                if (right>arr.length){
                    right = arr.length;
                }
                addarray(arr,left,mid,right);
            }
        }
    }

排序总结 (重点)

排序方法最好平均最坏空间复杂度稳定性
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
插入排序O(n)O(n^2)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
希尔排序O(n)O(n^1.3)O(n^2)O(1)不稳定
堆排序O(n * log(n))O(n * log(n))O(n * log(n))O(1)不稳定
快速排序O(n * log(n))O(n * log(n))O(n^2)O(log(n)) ~ O(n)不稳定
归并排序O(n * log(n))O(n * log(n))O(n * log(n))O(n)稳定
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值