递归与分治——子集问题

递归函数:以层次来想函数递归,以深度来想递归出口。

问题描述:
给一个集合,集合里面元素都不重复,输出这个集合的子集。
例如:
输入:
{1,2,3}
打印:
{1,2,3}
{1,2}
{1,3}
{2,3}
{1}
{2}
{3}
{}//代表空集
思路:
我们用一个等大小的集合,集合中对应的0代表不打印这个元素,集合中对应的1代表打印这个元素。建立两颗深度为3(集合个数)的满二叉树。可以看作哈夫曼编码,每一个码代表打印的一个子集。这样所有的码都代表一个子集。
比如:
{1,1,1}代表{1,2,3}
{1,1,0}代表{1,2}
{1,0,1}代表{1,3}

{0,0,0}代表{}
(注意,1代表打印,0代表不打印)
用代码解释会清晰一点,图下面先写出代码,然后再分析代码,这样思路会更清晰。
在这里插入图片描述
代码:

void Print_Subset(int* arr, int* brr, int i, int n)
{
	if (i == n)
	{
		for (int j = 0; j < n; j++)
		{
			if (brr[j] == 1)
			{
				printf("%2d", arr[j]);
			}
		}
		printf("\n");
	}
	else
	{
		brr[i] = 1;
		Print_Subset(arr, brr, i + 1, n);
		brr[i] = 0;
		Print_Subset(arr, brr, i + 1, n);
	}
}

int main()
{
	int arr[] = { 1,2,3 };
	int brr[] = { 0,0,0 };
	Print_Subset(arr, brr, 0, sizeof(arr)/sizeof(arr[0]));

	return 0;
}

运行结果:
在这里插入图片描述
(注意:3下面空两行,上面一个空行是代表空集,后面一个空行就是换行)
分析代码:
对于递归函数,我们一定要按照层次来分析来想代码,按照深度来分析,那是系统调用按照深度调用的。

注意递归出口是i == n,说明我们的树已经建立完成了,就可以根据brr的1或者0决定是否要打印arr的元素。

再注意这几行代码,我们来分析一下:

else
	{
		brr[i] = 1;
		Print_Subset(arr, brr, i + 1, n);
		brr[i] = 0;
		Print_Subset(arr, brr, i + 1, n);
	}

我们说过了,以深度调用分析,那是系统干的事情,我们只要以层次分析,一层分析完分析下一层,这样思路才清晰。下面代码竖着看。
第一层:
brr[0] = 1                        brr[0] = 0Print_Subset();               Print_Subset();

第二层(在第一层的基础上把第一层的函数展开),要知道每一次brr[i]负值后,后面都会有个函数需要执行:
在这里插入图片描述
第三层(在第二层的基础上把第一层的函数展开):
在这里插入图片描述
再到后面展开函数的时候,已经到了递归出口了,即最下面一行的函数递归进去都会打印出来一个子集,如下:
根据{1,1,1}打印{1,2,3}
根据{1,1,0}打印{1,2}
根据{1,0,1}打印{1,3}
根据{1,0,0}打印{1}
根据{0,1,1}打印 {2,3}

在这里插入图片描述
递归题,如果一段代码里出现两次或者两次以上递归进去函数,那么我们一定要以层次来想函数怎么递归,怎么执行,以深度来想函数递归出口。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟小胖_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值