左神算法2:排序(快排、堆排、桶排、计数与基数排序简单介绍) 荷兰国旗问题 大根堆小根堆 排序稳定性 比较器 相邻两数的最大差值问题

1.荷兰国旗问题

定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。

要求时间复杂度为O(N)、额外空间复杂度为O(1)。

分析:三个指针法:一个指向前头less,一个指向尾部more,一个是当前下标cur。当前下标由指向前面的指针推着前进。

在这里插入图片描述

import java.util.Arrays;

public class FlagOfHolland {
    public static int[] partition(int[] array,int L,int R,int num){
        int less=L-1;
        int more=R+1;
        int cur=L;
        while(cur<more){
            if(array[cur]<num){
                swap(array,cur++,++less);
            }else if(array[cur]>num){
                swap(array,cur,--more);
            }else{
                cur++;
            }
        }
        return array;
    }
    public static void swap(int[] array,int m,int n){
        int temp=array[m];
        array[m]=array[n];
        array[n]=temp;
    }

    public static void main(String[] args) {
        int[] arr={4,2,2,9,1,5,8,7,6,3,0};
        System.out.println("原数组为:"+ Arrays.toString(arr));
        partition(arr,0,arr.length-1,6);
        System.out.print("6之前比6小,之后比6大,partition之后的数组为");
        System.out.println(Arrays.toString(arr));
    }
}

运行结果:
在这里插入图片描述

2.快速排序

根据荷兰国旗问题,变形,让num=arr[arr.length-1],相当于R= arr.length-2;

在这里插入图片描述
经典快排缺点:如[1,2,3,4,5,6,7…N],快排就成了O(N2);
改进:随机快排:即是选择num = arr[]中随机的元素。时间复杂度O(N*logN);额外空间复杂度O(logN);【最常用的】

2.1经典快排:

 public static void quickSort(int[] arr, int L, int R) {
        if (L<R) {
            int[] p = partition(arr, L, R);
            quickSort(arr, L, p[0]-1);
            quickSort(arr, p[1], R);
        }
    }

    public static int[] partition(int[] arr, int L, int R) {
        int less = L-1;
        int more = R;
        int cur = L;
        while (cur<more) {
            if (arr[cur]<arr[R]) {
                swap(arr,++less,cur++);
            }else if (arr[cur]>arr[R]) {
                swap(arr,--more,cur);
            }else {
                cur++;
            }
        }
        swap(arr, more, R);
        return new int[] {less+1, more};
    }
    
    private static void swap(int[] arr, int i, int j) {
        // TODO Auto-generated method stub
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;        
    }

2.2随机快排

public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    public static void quickSort(int[] arr, int l, int r) {
        if (l < r) {
            swap(arr, l + (int) (Math.random() * (r - l + 1)), r);   //随机选择一个数作为比较对象
            int[] p = partition(arr, l, r);
            quickSort(arr, l, p[0] - 1);
            quickSort(arr, p[1] + 1, r);
        }
    }

    public static int[] partition(int[] arr, int l, int r) {
        int less = l - 1;
        int more = r;
        while (l < more) {
            if (arr[l] < arr[r]) {
                swap(arr, ++less, l++);
            } else if (arr[l] > arr[r]) {
                swap(arr, --more, l);
            } else {
                l++;
            }
        }
        swap(arr, more, r);
        return new int[] { less + 1, more };
    }

    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

3.堆排序

时间复杂度O(N*logN),额外空间复杂度O(1)

如果只是建立堆的过程,时间复杂度为O(N);
优先级队列结构就是堆结构。

堆: 完全二叉树。每一层从左到右依次补齐,满二叉树属于完全二叉树。
当一个数组满足左:2i+1,右:2i+2,父:(i-1)/2就是完全二叉树结构。

大根堆:任何子树的最大值都是子树的根节点,
小根堆:任何子树的最小值都是子树的根节点。
用处:比如实时求所输入数据的中位数。

大根堆的建立:有任何一个子树变成大根堆。复杂度=log1+ log2+… +logN=O(N)

import java.util.Arrays;

public class HeapSort {
    public static void heapSort(int[] array){
        if(array==null||array.length<2){
            return;
        }
        //建立大根堆
        for (int i=0;i<array.length;i++){
            heapInsert(array,i);
        }
        int heapSize=array.length;
        swap(array,0,--heapSize);
        while(heapSize>0){
            heapify(array,0,heapSize);//调整大根堆
            swap(array,0,--heapSize);//将大根堆的根和最后一个元素交换,然后size缩小1个
        }
    }
    public static void heapInsert(int[] array,int index){
        while(array[index]>array[(index-1)/2]){
            swap(array,index,(index-1)/2);
            index=(index-1)/2;
        }
    }
    public static void swap(int[] array,int m,int n){
        int temp=array[m];
        array[m]=array[n];
        array[n]=temp;
    }
    public static void heapify(int[] array,int index,int heapSize){
        //找到left
        int left=index*2+1;
        //进行循环
        while (left<heapSize){
            //确定left和right中最大的位置
            int largest=left+1>heapSize&&array[left+1]>array[left] ? left+1:left;
            //确定孩子结点与父节点中最大的位置
            largest=array[largest]>array[index] ? largest:index;
            //如果最大位置和父节点位置相同,则跳出循环
            if(largest==index){
                break;
            }
            //否则交换最大值和父节点的值,变量更新
            swap(array,index,largest);
            index=largest;
            left=2*index+1;
        }
    }
    public static void main(String[] args) {
        int[] arr={4,2,2,9,1,5,8,7,6,3,0};
        System.out.println("原数组为:"+ Arrays.toString(arr));
        heapSort(arr);
        System.out.print("堆排序后的数组为");
        System.out.println(Arrays.toString(arr));
    }
}

4. 排序的稳定性:

稳定性:排序外,相同元素保持出现的先后顺序。

复杂度是O(N2):

  • 冒泡排序:当遇到相同数时,该数不交换,将后面的数往下沉。可以稳定;
  • 插入排序:当遇到相同数时,该数不交换;可以稳定;
  • 选择排序:做不到稳定性。因为你要从后面的所有数中找到最小的,然后将前面的某一个a与该最值交换,如果有多个a存在,那么,a的先后顺序将无法保证。故做不到。

复杂度是O(N*logN);

  • 归并排序:merge时,当相同时先拷贝左边(小区域)的数;可以稳定
  • 快排:做不到稳定性;
  • 堆排:做不到稳定性。在建大根堆的时候,就都已经不能保证稳定性了。

工程中的排序:

  • 基础类型:快排
  • 自定义类型:归并排序(稳定性)
  • 如果数组长度较短:不管什么类型,都用插排(时间复杂度O(N^2)劣势显示不出来,反而额外空间复杂度O(1)较快)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值