数据结构与算法---快速排序

快速排序的基本思想:

在这里插入图片描述

递归的实现上面的步骤,用归纳法证明是最终的结果一定是排序后的结果:如果左子数组和右子数组都是有序的,且切分元素在两者之间,那么最终的排序结果显而易见是有序的。

切分

递归前一个重要的步骤是要切分数组。
假设我们要排序的数组为[6,8,1,4,9,0,3,5,2,7],同时选定最左边的数为pivot,以pivot为基准不停的交换两侧的数:
从右到左,小于pivot的数标记为j,从左到右,大于pivot的数标记为i,然后交换i,j,不停的重复直到i>=j,最后交换pivot和i。
这个方法首先保证了pivot固定不动,但实际上pivot也可以参与到每次的交换中,只要最后的结果是切分的数组即可。
在这里插入图片描述

代码

package com.sort_algs;
import java.util.PriorityQueue;
import java.util.Scanner;
import static java.lang.Integer.parseInt;
import static java.lang.System.in;

import edu.princeton.cs.algs4.StdRandom;

//快速排序
public class Quicksort {
	public static void sort(int[] a) {
	StdRandom.shuffle(a); 	//打乱
		sort(a,0,a.length -1);
	}
	
	private static void sort(int[] a,int lo, int hi) {
		if (hi <= lo) return;          //只有一个元素时
		int j = partition1(a,lo,hi);   //切分
		sort(a, lo, j-1);				  //递归调用将左半部分a[lo,...,j-1]   排序 
		sort(a, j+1, hi);   			  //递归调用将右半部分a[j+1,...,hi] 排序
	}
	
	//pivot随着每次交换
	public static int partition(int[] a,int lo, int hi) {
		int i = lo, j = hi;
		int v = a[lo];    //pivot, 切分元素
		while(i < j) {
			while (i < j && a[j] > v) {
				j--;
			}
			if(i < j) {
				a[i] = a[j];  //v跟a[j]交换
				i++;
			}
			while(i < j && a[i] < v) {
				i++;
			}
			if(i < j) {
				a[j] = a[i];  //i
				j--;
			}	
		} 
		a[i] = v;
		return i;
	}
	
	//pivot先固定不动,最后交换
	public static int partition1(int[] a,int low, int high) {  
		int i = low, j = high+1;   //因为pivot选的左侧第一个数,因此从右到左时,最后一个数不能忽略,所以是j=high+1
		int v = a[low];    //pivot, 切分元素
		while(true) {
			//跟上面的partition区别是不用判断i<j,如果判断了[0,2,1]这样的情况就无法正确切分
			//
			while(a[++i] < v) {  
				if(i == high) {
					break;
				}
			}
			while(a[--j] > v) {
				if(j == low) {
					break;
				}
			}
			//
			if(i >= j) {   
				break;
			}
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;

		} 
		
		int temp = a[j];
		a[j] = v;
		a[low] = temp;
			
		return j;
	}
	
	public static void main(String[] args) {
//		Scanner sc = new Scanner(in);    
//        String[] str = sc.nextLine().replace("[", "").replace("]", "").split(",");
//        int[] data = new int[str.length];
//        for (int i = 0; i < data.length; i++) {
//            data[i] = parseInt(str[i]);    
//        }
		int[] a = {6,8,1,4,9,0,3,4,5,2,7};
        
        Quicksort.sort(a);
		for(int i = 0;i<a.length;i++) {
			System.out.println(a[i]);
		}       
//        sc.close();
	}
}

查找第K大的元素

  • 题目描述
    给定一个无序的整型数组A[n],数组大小大于等于3,允许有值相同的元素;请设计算法找到该数组排序后第三大的元素值并输出.

  • 输入描述:
    一个非空的整数数组(至少有3个元素,可正可负)

  • 输出描述:
    第三大的元素值

  • 示例1

    • 输入
      [3,2,1,5,6,4]
    • 输出
      3
  • 示例2

    • 输入
      [1,1,2,2,3]
    • 输出
      2

首先这道题要在O(n)的条件下做出来。
查找第K大的元素就意味着排序,因此上面介绍的快速排序的优势就体现出来了。
快速排序需要对整个数组进行递归搜索+切分元素,但这里只需要针对第K大的元素,因此基于选择的快速排序就能在最优的时间得到答案。

package com.sort_algs;
import java.util.Scanner;
import static java.lang.System.in;
import static java.lang.Integer.parseInt;

public class FindN {
	public static void main(String[] args) {
		Scanner sc = new Scanner(in);
//		String[] str = sc.nextLine().replace("[", " ").replace("]","").split(",");
//		int[] data = new int[str.length];
//		for (int i= 0;i<data.length;i++) {
//			data[i] = parseInt(str[i]);
//		}
//	    String str=sc.nextLine();
//        String str1=str.toString().substring(1,str.length()-1);
//        String[] str2=str1.split(",");
//        int[] data=new int[str2.length];
//        for(int i=0;i<str2.length;i++) {
//            data[i]=Integer.parseInt(str2[i]);
//        }
			        
		int[] data = {3,2,1,5,6,4};
		int k = data.length-3+1;
		//int I = findND(data,k);
		
		int high = data.length - 1;
		int I = findND_recursive(data, 0, high, k-1);
		System.out.println(I);		
		
		sc.close();
	
	}
	
	public  static int findND_recursive(int[] data, int begin, int high,int k) {	

		int pos = partition(data,begin,high);
		if (pos == k) {
			return data[pos];
		}else if (pos < k) {
			return findND_recursive(data, pos+1, high, k);  //选择性尾递归。保证遇到合适的就返回,不会做多余的工作;
		}else {
			return findND_recursive(data, begin, pos-1, k);
		}
	}
	
	public  static int findND(int[] data,int k) {	
		int begin = 0;
		int end = data.length-1;
		int pos = 0;
		while(begin <= end) {
			
			 pos = partition(data, begin , end);
			 if(pos == k-1) {
				 return data[pos];
			 }else if(pos > k-1) {
				 end = pos - 1;
			 }else {
				 begin = pos + 1;
			 }	 
		}
		return -1;
	}
	
	public static int partition(int[] data, int low, int high) {
		int i = low;
		int j = high + 1;
		int v = data[low];
		while(true) {
			while(data[++i]<v) {
				if(i==high) {
					break;
				}
			}
			while(data[--j]>v) {
				if (j == low) {
					break;
				}
			}			
			if(i>=j) {
				break;
			}
			int temp = data[i];
			data[i] = data[j];
			data[j] = temp;
		}
		int temp = data[j];
		data[j] = v;
		data[low] = temp;
		
		return j;
	}
}

一个循环版本,一个递归版本。

参考

  1. https://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html
  2. http://data.biancheng.net/view/117.html
  3. 算法第4版. Robert Sedgwewick
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值