快速排序
排序思想:通过一趟排序,将倒排序的序列分成两部分,其中一部分均比另一部门小,然后再对这两部分进行下一趟排序,以达到整个序列有序
平均时间复杂度: O(nlog2(n)),n*log以2为底的n
最坏情况下是:O(n^2) ,n的平方
排序流程
- 首先设定一个分界值,通过该分界值将数组分成左右两部分
- 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值(具体看怎么分)
- 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理
- 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了
排序步骤
- 设置两个变量i、j,排序开始的时候:i=0,j=N-1
- 以第一个数组元素作为关键数据,赋值给key,即key=A[0]
- 从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换
- 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换
- 重复第3、4步,直到i==j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)
一趟快速排序算法演示
初始关键字序列:49、38、65、97、76、13、27、49
取数组第一位为快排的参照:49
- 第一次排序:49跟右边49比较,49=49,然后找49>27,将27放到49左边(交换位置)
- 27、38、65、97、76、13、49、49
- 第二次排序:从左边找到65>49,交换位置
- 27、38、49、97、76、13、65、49
- 第三次排序:从右开始找打13<49,交换位置
- 27、38、13、97、76、49、65、49
- 第四次排序:从左找到97>49,交换位置
- 27、38、13、49、76、97、65、49
- 最后一趟排序:从右找76,然后找49,下标一致,第一趟结束,继续排序分别排左边和右边数组
- {27、38、13}、49、{76、97、65、49}
JAVA实现快速排序算法
按快速排序定义实现
public static void main(String[] args) {
int[] array = {
49,38,65,97,76,13,27,49};
int low = 0;
int high = array.length - 1;
quicksort(array, low, high);
}
/**
* 快速排序 ,循环执行 step1 和 step2
* @param array 数组
* @param low 每次比较的最低位
* @param high 每次比较的最高位
*/
private static void quicksort(int[] array,int low,int high) {
//记录开始位置,是下次左边数组(比分解值小的一边)排序的最低位。
int start = low;
//记录此次排序的最高位,是下次右边数组(比分界值大的一边)排序的最高位
int end = high;
//取数组第一位作为分界值
int ref = array[low];
//如果低位和高位重合则结束此趟排序
while (low != high){
//step1 先顺高位寻找小于分界值
while (high > low){
//如果array[high] 小于 分界,交换位置 high不指向下一位
if(array[high] < ref){
int temp = array[high];
array[high] = array[low];
array[low] = temp;
printArray(array);
break;
}
high --;
}
//step2 低位寻找高于分界值
while (low < high){
//如果array[low]或等于分界值 交换位置,low不指向下一个位置,继续比较高位
if(array[low] >= ref){
int temp = array[high];
array[high] = array[low];
array[low] = temp;
printArray(array);
break;
}
low ++;
}
}
// low的值和最开始进来的值相等,说明只有一位结束循环
if(low != start){
quicksort(array,start,low - 1);
}
// low - 1 和 high + 1。因为分解值必然比右边小,必然比左边大,所有一个位置减1一个加1
if(high != end){
quicksort(array,high + 1,end);
}
}
/**
* 输出数组内容
*/
private static void printArray(int[] array){
Arrays.stream(array).forEach(value -> {
System.out.print(value + " ");
});
System.out.println();
}
另一种实现百度百科上的
public static void main(String[] args) {
int[] array = {
49,38,65,97,76,13,27,49};
int len = array.length-1;
qsort(array,0,len);
}
/**
* 递归调用,第二次用的子数组,注意子数组下标位置
* @param arr
* @param start
* @param end
*/
private static void qsort(int[] arr,int start,int end) {
//开始位置的值,标记值 ,大的放右边,小的放左边
int pivot = arr[start];
//初始的开始和结束位置赋值给i和j,变动i和j,初始值不变下次递归判断用。
int i = start;
int j = end;
//i < j
while (i < j) {
//当arr[j]最高位大于标记值,则继续寻找下一位,直到找到小于或等于标记值的元素,不超过最低位 i
while ( i < j && arr[j] > pivot) {
j--;
}
//arr[i]小于标记值,一直向上寻找,直到找到大于标记值或者找到最高位j结束循环
while ( i < j && arr[i] < pivot) {
i++;
}
//如果 arr[i]==arr[j] 则i进一位,且i不能超过j,也就是查找j找过的元素
if (arr[i]==arr[j] && i < j ) {
i++;
} else {
//交换j找到的小于标记值的元素 和 i找到大于标记值的元素
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
printArray(arr);
}
}
//递归调用
if (i-1>start) {
qsort(arr,start,i-1);
}
if (j+1<end) {
qsort(arr,j+1,end);
}
}
/**
* 输出数组内容
*/
private static void printArray(int[] array){
Arrays.stream(array).forEach(value -> {
System.out.print(value + " ");
});
System.out.println();
}