快速排序及其扩展(你所知道的不知道的快排这里都有)

1.快速排序

定义:它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
代码实现:

public static int Partion(int[] arr,int left ,int right){
        int temp=arr[left];
        while(left<right) {
            while (left<right&&arr[right]>temp) --right;
            if (left < right) {
                arr[left] = arr[right];
            }
            while (left<right&&arr[left] <= temp) ++left;
            if (left < right) {
                arr[right] = arr[left];
            }
        }
        arr[left]=temp;
        return left;
        }
        public static void quickSort(int[] arr,int left,int right){
         if(left<right){
             int pos=Partion1(arr,left,right);
             quickSort(arr,left,pos-1);
             quickSort(arr,pos+1,right);
         }
        }
        public static void main(String[] args) {
        int[] arr={12,34,9,6,32,89,76,56};
        quickSort(arr,0,arr.length-1);
      System.out.println(Arrays.toString(arr));

由代码可以发现,快排即使用的是递归方法进行排序,使用非递归的代码如下:
非递归:

//非递归
        public static void NiceQuickSort(int[] arr,int left,int right){
            Queue<Integer> queue=new LinkedList<>();
            queue.offer(left);
            queue.offer(right);
            while (!queue.isEmpty()){
            left=queue.poll();
            right=queue.poll();
            int pos=Partion(arr,left,right);
            if(left<pos-1){
                queue.offer(left);
                queue.offer(pos-1);
            }
            if(pos+1<right){
                queue.offer(pos+1);
                queue.offer(right);
            }
            }
        }

**问题思考:**有时候会出现所要排序的数组其本身有序,此时用快排反而更耗时,采用随机排序,即不从0位开始找,从任意位置开始查找,这样既可提高时间复杂度。

  public static int RandomPartion(int[] arr,int left,int right) {
        Random random=new Random();
        int index=random.nextInt(right-left+1)+left;
        int temp=arr[left];
        arr[left]=arr[index];
        arr[index]=temp;
        return Partion(arr,left,right);
    }

**问题思考:**如果需要进行快排的不是数组而是单链表,如何进行排序?
单链表:即只有一个指针next,不能从后往前,所以需要从前到后单方向进行排序。可类比思考数组如何单方向进行快排?
数组单方向快排:
解释:
同时添加i和j,从i=left开始,j=i+1,即j比较是否有temp(arr[left])值大,如果没有则将i++,同时交换i下标和j下标所对应的数值。(此时i下标对应的值一定不会小于temp,符合逻辑 ),如果比temp值大,则j++直到j到right的位置。最后交换temp和arr[i]的值即可。

      //单方向排序
    public static int Partion1(int[] arr,int left ,int right) {
        int temp = arr[left];
        int i = left, j = i + 1;
        while(j<=right) {
            if(arr[j]<temp){
                i++;
                int x=arr[i];
                arr[i]=arr[j];
                arr[j]=x;
            }
            ++j;
        }
        arr[left]=arr[i];
        arr[i]=temp;
        return i;
    }

链表快排 (即单方向排序):

    public static ListNode ListPartion(ListNode left,ListNode right){
        int tmp=left.value;
        ListNode low=left,hight=low.next;
        while(hight!=right){
            if(hight.value<=tmp){
                low=low.next;
                if(low!=hight) swap(low,hight);
            }
            hight=hight.next;
        }
        swap(left,low);
        return low;
    }
    public static void ListQuickSort(ListNode left,ListNode right){
        if(left!=right){
            ListNode pos=ListPartion(left, right);
            ListQuickSort(left,pos);
            ListQuickSort(pos.Next,right);
        }
    }

**问题思考:**找出数组中的第一小和第二小元素

分析:将数组中的前两个值作为最小的两个值 min1,min2,从第三个元素开始进行比较,如果arr[i]小于min1,则min2=min1,min1=arr[i],;如果arr[i]小于min2,则min2=arr[i]

 //找出数组中的第一小和第二小元素
    public static void Select2Min(int[] arr ){
          if(arr.length<2)return;
          int min1=arr[0]<arr[1]?arr[0]:arr[1];
          int min2=arr[0]>arr[1]?arr[0]:arr[1];
        for (int i = 2; i < arr.length; i++) {
            if(arr[i]<min1){
                min2=min1;
                min1=arr[i];
            }
            else if(arr[i]<min2){
                min2=arr[i];
            }
        }
        System.out.println(min1+" "+min2);
    }

**问题思考:**找出数组中的第k小元素
分析:由于快排我们可以知道当第一次划分时,arr[left]应该对应的下标我们即可知道,同时根据他的下标值:pos-left+1即可知道他排行第几(即为第几小),此时判断要查找的第k位与该排名的大小关系,如果大于该排名,则调用该函数将要查找的k位改为k-pos位Select_K(arr,index+1,right,k-pos),如果小于等于当前位次,则调用该函数Select_K(arr,left,index,k)

public static int Select_K(int[] arr,int left,int right,int k){
            if(left==right&&k==1)return arr[left];
            int index=Partion(arr,left,right);
            int pos=index-left+1;//表示经过查找一次后对应的位数 即第几小
            if(k<=pos) return Select_K(arr,left,index,k);
            else return Select_K(arr,index+1,right,k-pos);
}

问题思考:如何找数组中差距最小的两个数的差值。
分析:开始将一个数组划分成两部分,利用Select_K(arr,left,right,k)将数组可划分成两段,分别查找出左边和右边部分对应的两个数差的最小值,还有一个差值即:第二部分的最小值减去第一部分的最大值,比较找出差值最小的值。

public static int MaxS1(int[] arr,int left,int right){
            return arr[right];
}
public static int Mins2(int[] arr,int left,int right){
            int temp=arr[left];
    for (int i = left+1; i <=right; i++) {
        if(temp>arr[i]) temp=arr[i];
    }
    return temp;
}
    public static int Min3d(int a,int b,int c){
            int min=a<b?a:b;
            return min<c?min:c;
    }
static final int maxint=0x7ffff;
public static int Cpair(int[] arr,int left,int right){
            if(right-left<1)return maxint;
            int k=(right-left+1)/2;
            Select_K(arr,left,right,k);
            int index=left+k-1;
            int d1=Cpair(arr,left,index);
            int d2=Cpair(arr,index+1,right);
            int maxs1=MaxS1(arr,left,index);
            int mins2=Mins2(arr,index+1,right);
            return Min3d(d1,d2,mins2-maxs1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值