快速排序之数组子集划分问题

本文介绍了如何运用快速排序的思想解决一个数组子集的划分问题,目标是使得两个子集的元素数量差最小且它们的和的差值最大。通过寻找数组的中位数并以此为界,将数组分为两个部分,实现了题目的要求。代码展示了一个具体的实现过程,最终将数组分为两个部分,一部分包含较小的元素,另一部分包含较大的元素。
摘要由CSDN通过智能技术生成

1.问题描述:已知由n(n≥2)个正整数构成的集合A={ak}(0≤k<n),将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2,A1和A2中元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足|n1-n2|最小且|S1-S2|最大。

2.设计思路:根据题目的要求,如果要满足n1-n2最小,那么两个子集中的元素个数是平均的,也就是n1=n/2;在此基础下,去满足S1-S2最大,那么就需要把n中的元素分为两部分:较大的一部分和较小的一部分;分别把这两个部分放入n1、n2中;把n分成两部分就需要中位数,这里可以使用快速排序完成每一次的中位数求解;如果基准在靠近左边,说明中位数应该在右边,那么在右边递归函数;如果基准在右边,说明中位数应该在左边,那么在左边递归函数;直到基准的位置为n/2时结束,此时找到整个数组的中位数;最后以n/2为边界输出两个子集即可;

3.代码:

/*数组子集划分问题*/
#include <stdio.h>
#define N 10
int a[N]={9,8,7,6,5,4,3,2,1,0};
int FindMidPosision(int a[],int low,int high)	//利用快速排序思想查找中间位置 
{
	int temp=a[low];		//基准
	 while(1)
	 {
	 	while(low<high&&a[high]>=temp)//从右边开始与基准进行比较,小于基准时把数转移到基准位置 
	 	{
	 		high--;
		 }
		if(low>=high)		//如果从右到左已经比较完成,就退出循环 
		{
			break;
		}
		a[low++]=a[high];		//a[high]<temp时,把a[high]转移到a[low]位置
		while(high>low&&a[low]<=temp)//从左边开始与基准进行比较,大于基准时把数转移到基准位置
		{
		 	low++;
		} 
		if(low>=high)		//如果从右到左已经比较完成,就退出循环
		{
			break;
		}
		a[high--]=a[low];   
	  } 
	a[low]=temp;		//把temp的值放到大小子集的中间位置,刚刚好把子集分为一边大,一边小 
	return low;
 } 
void Divide(int a[],int low,int high,int n)	//递归调用快速排序,找到整个数组的中间位置 
{
	int mid;
	while(low<high)
	{
		mid=FindMidPosision(a,low,high);
		if(mid==n/2)
		{
			break; 
		}
		else if(mid<n/2)	//基准小于n/2时,在右边继续递归 
		{
			low=mid+1; 
		}
		else{
			high=mid-1;
		}
	}
}
int main()
{
	Divide(a,0,N-1,N);		//调用函数把数组从中间位置分为一边大和一边小
	printf("n1=%d	:",N/2); 
	for(int i=0;i<N/2;i++)
	{
		printf("%d ",a[i]); 
	}
	printf("\nn1=%d	:",N-N/2);
	for(int i=N/2+1;i<N;i++)
	{
		printf("%d ",a[i]); 
	}
	return 0;
}

4.运行结果:

5.总结:

数组子集划分问题,使用快速排序的思路把数组分为两个部分,并且这两个部分是一边大、一边小,这样就可以满足题目的要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值