上一篇我们讲了分治法最基本的形式——二分法,现在我们讲一讲非等分分治的情况。
非等分分治顾名思义就是在分的时候,子问题的规模不是相等的,也就是分点不在中心位置。非等分分治的一个应用就是快速排序算法。
现在我们看一个基于快拍算法的例子——求第k小的数。
如果我们使用二分法来解决这个问题。首先将问题一分为二,化成两个同等规模的子问题。从这两个自问题中递归求解,但是,这样做会很麻烦。因此,我们采取一种非等分分治来解决这个问题。它的解决思路有点想快速排序的解决思路。
我们将第一个数据作为分界数据,然后依据快排的思想对数组进行排序。比分界数据小的在分界数据的左面,比它大的在它的右面。然后查看左面子集有多少个数nleft,那他和我们的k进行比较:
1.nleft=k-1,则分界数据就是我们要找的值。
2.nleft<k-1,则我们要找的值在右子集,问题变成了找第k-nleft-1小的数,问题的规模缩小。
3.nleft>k-1,则我们要找的值在左子集。
希望各位看官将代码上机调试一下,这样理解会更加的深刻!本人的运行环境是vs2010
<span style="font-size:18px;">void jiaohuan(int &x,int &y)
{
int t;
t=x;x=y;y=t;
}
int select_min(int a[],int left,int right,int k)
{
int i,j,pivot;
if (left>=right) return a[left];
pivot=a[left];
i=left+1;
j=right;
while(1)
{<span> </span>//计算nleft
do
{
i++;
} while (a[i]<pivot);
do
{
j--;
} while (a[j]>pivot);
if (i>=j)break;
jiaohuan(a[i],a[j]);
}
if (j-left+1==k)//比较k-1和nleft的大小
{
return pivot;
}
a[left]=a[j];
a[j]=pivot;
if (j-left+1<k)
return select_min(a,j+1,right,k-j-1+left);
else
return select_min(a,left,j-1,k);
}
int xzwt(int a[],int n,int k)
{
if (k<1||k>n)
{
return -1;
}
return select_min(a,0,n-1,k);
}
int _tmain(int argc, _TCHAR* argv[])
{
//求第k小的数
int a[6]={5,3,2,7,6,9};
int k,mink;
scanf("%d",&k);
mink=xzwt(a,6,k);
if (-1==mink)
{
printf("ERROR\n");
}
else
{
printf("%d\n",mink);
}
return 0;
}
</span>