快速排序及寻找区间第k大
快速排序基本思想:分治法
快速排序基本流程:
选择序列的一个元素为枢轴(程序中选择第1个元素),然后设计两个指针(start和end),将所有剩余元素依次和枢轴进行比较,根据比较结果,将比枢轴大的元素都排在它的后面,比枢轴小的元素都排在它的前面,这样一趟排序以后(start==end),序列就以枢轴为界划分成了两个部分。然后,递归地对这两个部分再进行上述操作,直到每一部分都只有一个数据元素为止(递归结束条件)。
特性:
(1)平均时间复杂度最快的排序算法(),最好时间复杂度也是
。
(2)当初始序列已经有序时,快速排序蜕化成了冒泡排序,时间复杂度为。
稳定性:不稳定(如上图,交换元素时会改变相等元素的相对位置)。
有关的改进:
快速排序算法的性能取决于划分的对称性。通过修改算法quickSort,可以设计出采用随机选择策略的快速排序算法。在快速排序算法的每一步中,当数组还没有被划分时,可以在a[p:r]中随机选出一个元素作为划分基准,这样可以使划分基准的选择是随机的,从而可以期望划分是较对称的。
代码1:
#include<stdio.h>
void Qsort(int arr[],int low,int high)
{
if(low>=high) return;//结束条件
int start=low;
int end=high;
int key=arr[start];//用第1个元素作为关键值
while(start<end)
{
while(start<end && arr[end]>=key)
{
end--;
}
arr[start]=arr[end];//将比关键值小的元素放到序列低端
while(start<end && arr[start]<=key)
{
start++;
}
arr[end]=arr[start];//将比关键值大的元素放到序列高端
}
arr[start]=key;//还原
Qsort(arr,low,start-1);
Qsort(arr,start+1,high);//递归
}
int main(void)
{
int length;
while(scanf("%d",&length)!=EOF)
{
int m[10000];
for(int i=0;i<length;i++){
scanf("%d",&m[i]);
}
Qsort(m,0,length-1);
for(int i=0;i<length;i++){
printf("%d ",m[i]);
}
printf("\n");
}
return 0;
}
代码2:
//快速排序
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
void swap(int a[],int elem1,int elem2)
{
int tmp = a[elem1];
a[elem1]=a[elem2];
a[elem2]=tmp;
}
void quickSort(int a[],int low,int high){
if(low>=high) return;
int start = low;
int end = high;//避免待会儿递归时污染数值
int key = a[start];//以第一个元素为枢轴
while(start<end)
{
while(start<end && a[end]>=key)
{
end--;
}
swap(a,start,end);//交换
while(start<end && a[start]<=key)
{
start++;
}
swap(a,start,end);//交换
}
quickSort(a,low,start-1);
quickSort(a,start+1,high);//递归
}
void quickSortHelp(int a[],int n){
quickSort(a,0,n-1);
}
int main(void)
{
int n = 10;
int arr[10] = {0};
for(int i=0;i<n;i++)
{
arr[i]=rand()%100;
}
for(int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
quickSortHelp(arr,n);
for(int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
return 0;
}
基于以上,可以在(平均)时间复杂度内寻找第K大的元素,当然最坏的时间复杂度依然是
。
思路是与快速排序一致的,只是在查找时,根据查找的元素(下标K)与枢轴位置的关系,只查找其中的一部分。
#include<iostream>
#include<stdlib.h>
using namespace std;
int arr[1000001];
//区间习惯:左开右闭
void partition(int arr[],int p,int q,int k)//寻找第k大
{
if(p>=q) return;
int random = (rand() % (q-p))+ p;//随机选择枢轴
int key = arr[p];
int start = p;
int end = q-1;
while(start<end)
{
while(start<end&&arr[end]>=key)//等号
{
end--;
}
arr[start]=arr[end];
while(start<end&&arr[start]<=key)
{
start++;
}
arr[end] = arr[start];
}
arr[start]=key;//还原
//当start==end
if(k==start) return;//此时start是枢轴元素在最终有序序列的下标,找到直接返回
if(k>start) //比寻找的比枢轴大,只需要在比枢轴大的区间继续查找
{
partition(arr,start+1,q,k);
}
if(k<start)//反之,只需要在比枢轴小的区间继续查找
{
partition(arr,p,start,k);
}
}
int main(void)
{
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++)
{
scanf("%d",&arr[i]);
}
partition(arr,0,n,n-k);
cout<<arr[n-k]<<endl;
return 0;
}
图片来源:https://blog.****.net/hellozhxy/article/details/79911867