思路1:基于快排每次都会确定一个最终元素位置,下面代码则采用这种思想编写。
思路2:同样基于快排,每次划分后看左边区间元素个数是否大于k个,若左边区间元素个数大于k个则在左区间找第k大元素,若左边区间个数小于k个,这时假设左边有len个元素,这时候在右边区间找第k-len+1个即可。
注:有不正当地方欢迎指出!
代码:
/*
快排思想求第k小,时间复杂度O(n),空间O(1)
测试数据:
5 3
2 1 3 5 4
5 3
1 2 3 1 4
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int a[maxn];
int ans,n;
int partion(int l,int r)
{
int i=l,j=r;
int temp=a[l];
while(i<j)
{
while(i<j&&a[j]>=temp) j--;
if(i<j) a[i]=a[j];
while(i<j&&a[i]<=temp) i++;
if(i<j) a[j]=a[i];
}
a[i]=temp;
return i;//返回每次划分元素位置
}
int quickSort(int l,int r,int k)
{
int mid=partion(l,r);
if(mid==k) return a[k];
if(mid>k)
return quickSort(l,mid-1,k);
if(mid<k)
return quickSort(mid+1,r,k);
}
int main()
{
int k;
while(scanf("%d%d",&n,&k)!=EOF)
{
ans=0;
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
ans=quickSort(1,n,k);
printf("%d\n",ans);
}
return 0;
}
下面依照上面思想O(n)将一个数组划分为这样的两个集合:设集合A元素个数为n1,集合内元素和为sum1,集合B元素个数为n2,集合内元素和为sum2,两集合满足|n1-n2|尽可能小的前提下|sum1-sum2|尽可能大。
代码:
/*
时间复杂度O(n),空间O(n)
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000;
int a[maxn],A[maxn],B[maxn];
int n;
int Partion(int l,int r)
{
int i=l,j=r;
int temp=a[l];
while(i<j)
{
while(i<j&&a[j]>=temp) j--;
if(i<j) a[i]=a[j];
while(i<j&&a[i]<=temp) i++;
if(i<j) a[j]=a[i];
}
a[i]=temp;
return i;
}
void quickSort(int l,int r,int k)
{
int mid=Partion(l,r);
if(mid==k)
{
for(int i=1;i<mid;i++) A[i]=a[i];
for(int i=mid;i<=n;i++) B[i]=a[i];
return;
}
if(mid>k) quickSort(l,mid-1,k);
if(mid<k) quickSort(mid+1,r,k);
}
int main()
{
int k;
while(scanf("%d",&n)!=EOF)
{
if(n%2==0) k=(n>>1)+1;
else k=(n+1)>>1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
quickSort(1,n,k);
for(int i=1;i<k;i++) printf("%d ",A[i]);
printf("\n");
for(int i=k;i<=n;i++) printf("%d ",B[i]);
printf("\n");
}
return 0;
}