快排属于分治算法,分治算法都有三步:
- 分成子问题
- 递归处理子问题
- 子问题合并
题目描述
给定你一个长度为n的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
第一种:以j为边界;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] arr = new int[n];
for(int i = 0 ;i < n ;i++) {
arr[i] = scan.nextInt();
}
quick_sort(arr,0,n-1);
for(int i = 0 ; i < n ;i++) {
System.out.print(arr[i]+" ");
}
}
public static void quick_sort(int[] arr,int l,int r) {
if(l >= r) return;
int x = arr[l+r>>1],i = l-1,j = r+1;
while(i<j) {
while(arr[++i]<x);
while(arr[--j]>x);
if(i<j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
quick_sort(arr,l,j);
quick_sort(arr,j+1,r);
}
}
本算法采用递归的思想;首先先确定左右的边界,然后确定其分界点(此题取中间值),(用的是右移运算符,>>运算符比+的优先级低,所以会先进行加法运算,再进行右移运算符,功能等同于除2);采用i,j两个指针来进行移动,若在满足i<j的前提时,若x左边的值小于x,则右移一位,循环执行;如果大于或者等于下,则退出循环,当右边也出现小于或者等于x的情况下,两者进行交换。
边界值分析易错:
防止出现n分成0和n,或 n分成n和0的情况,否则会造成死循环,无限划分。
如第二种当使用i为边界时,假如有两个数(6,3)进行快速排序,下标是0-1;若此时中间值取q[L+R>>2],时,下标为0,即分界值为6,i对应的内容为6,与边界值相同,不进行右移操作;j对应的内容为3,其小于x,故也不进行左移,此时i<j,进行交换;紧接着执行quick_sort(arr,l,i - 1);l=0,i-1=0;再执行quick_sort(arr,i,r),i=0,r=1;(符合n分成0和n,或 n分成n和0的情况,否则会造成死循环,无限划分这种情况)这与刚开始的范围没有变化,即使往下进行递归,也依然是这样的结果,永远也达不到递归终止的条件,造成死循环。
结论:此题分界值选取的为中间值,若题目的数据量并没有那么大时,边界值也可以在最右或者最左等都可以(若以i为边界,则不能使边界值x=arr[l],以为边界时,则不能使边界值x=[r])。
第二种:以i为边界,(注意对中间值x进行赋值与第一种的不同)
package com.zy.Acwing;
import java.util.*;
/**
* 快速排序模板
* @author zhaoyan
*
*/
public class P785 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] arr = new int[n];
for(int i = 0 ;i < n ;i++) {
arr[i] = scan.nextInt();
}
quick_sort(arr,0,n-1);
for(int i = 0 ; i < n ;i++) {
System.out.print(arr[i]+" ");
}
}
public static void quick_sort(int[] arr,int l,int r) {
if(l >= r) return;
int x = arr[l+r+1>>1],i = l-1,j = r+1;
while(i<j) {
while(arr[++i]<x);
while(arr[--j]>x);
if(i<j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
//quick_sort(arr,l,j);
//quick_sort(arr,j+1,r);
quick_sort(arr,l,i - 1);
quick_sort(arr,i,r);
}
}