剑指Offer_面试题28.5_字符串的排列扩展_排列组合_八皇后问题

63 篇文章 1 订阅
40 篇文章 0 订阅

剑指Offer_面试题28_字符串的排列 实现了字符排列、去重的问题,由此可以扩展出很多问题如组合问题。

扩展1

字符组合问题。如输入三个字符a、b、c,则他们的组合有a、b、c、ab、ac、bc、abc。

分析:与排列解法一致,把问题分解。假设我们要在10个字符中取3个字符组合。

那么我们把这10个字符分为两部分:第一个字符和其余9个字符。如果组合里包含第一个字符,那么就在剩余9个字符中选取2个字符组合;如果不包含第一个字符,那么就从剩余9个字符中选取3个字符组合。于是乎得到问题的分解:

n个字符中取m个组合(1<= m <= n)可以分解成两个子问题:

①求n-1个字符中的m-1个字符组合

②求n-1个字符中的m个字符组合

这两个子问题都可以用递归的方式解决。

递归解决:

///剑指offer递归解决思路:
//字符str取number个组合
void combination(char *str, int number, vector<char>& cRes, vector<string>& res)
{
	if (str == NULL) return;
	if (number == 0)
	{
		//将字符vector连起来转换为string
		string tempStr(cRes.size(), ' ');
		for (size_t i = 0; i < cRes.size(); ++i)
			tempStr[i] = cRes[i];
		res.push_back(tempStr);
		return;
	}

	if (*str == '\0') return;    //这个必须在后
	cRes.push_back(*str);
	combination(str+1, number-1, cRes, res);
	cRes.pop_back();    //相当于还原初始状态
	combination(str+1, number, cRes, res);

}

vector<string> combination(char *str)
{
	vector<string> res;
	int len = strlen(str);
	if (len)
	{
		for (int i = 1; i <= len; ++i)
		{
			vector<char> cRes;
			combination(str, i, cRes, res);
		}
	}
	return res;
}


int main()
{
	char *str2 = "abc";
	vector<string> res = combination(str2);
	for (auto s : res)
		cout << s << endl;

	getchar();
	return 0;
}


结果:



说一说我之前的笨办法解法。我用了排列算法排出“10”的所有组合,比如10个字符选3个字符组合,那我先构造一个只含有10的字符串“1110000000”一共10位,含有3个1,进行去重全排列,然后下标对应字符串的下标,是1则取str的对应下标字符,0则不取,组成长度为3的字符串作为结果。只能是代码很复杂,真要碰到面试,可能写不完,只能大概说说想法。


扩展2

输入一个含有8个数字的数组,判断有没有可能把这8个数字分别放到正方体的8个顶点上,使得正方体的3组相对的面上的4个顶点的和都相等。


如上图,其实试求是否8个数的排列能满足a1+a2+a3+a4 = a5+a6+a7+a8且a1+a5+a7+a3 = a2+a6+a8+a4且a1+a2+a6+a5=a3+a4+a8+a7

#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <unordered_set>
#include <iostream>
using namespace std;

//判断排列是否满足
bool isSuccessful(vector<int> perm)
{
	if (perm.size() != 8) return false;
	bool b1 = ((perm[0] + perm[1] + perm[2] + perm[3]) == 
		(perm[4] + perm[5] + perm[6] + perm[7]));
	bool b2 = ((perm[0] + perm[2] + perm[4] + perm[6]) == 
		(perm[1] + perm[3] + perm[5] + perm[7]));
	bool b3 = ((perm[0] + perm[1] + perm[4] + perm[5]) == 
		(perm[2] + perm[3] + perm[6] + perm[7]));
	return b1 && b2 && b3;
}

bool permutation(vector<int> input, int k, vector<int> &res)
{
	if (k == input.size())
	{
		if (isSuccessful(input))
		{
			res = input;
			return true;
		}
	}
	unordered_set<int> record;
	for (int i = k; i < input.size(); ++i)
	{
		if (record.find(input[i]) == record.end())
		{
			record.insert(input[i]);
			int temp = input[i];
			input[i] = input[k];
			input[k] = temp;
			if (permutation(input, k + 1, res))
				return true;
			temp = input[i];
			input[i] = input[k];
			input[k] = temp;
		}
	}
	return false;
}

bool permutation(vector<int> input, vector<int>& res)
{
	if (input.size() != 8) return false;
	bool bRes = false;
	bRes = permutation(input, 0, res);
	return bRes;

}

int main()
{
	vector<int> input = {1,2,3,4,5,6,7,8};
	vector<int> res;
	if (permutation(input, res))
	{
		for (int i = 0; i < 8; ++i)
			printf("a%d\t", i+1);
		cout << endl;
		for (auto i : res)
			cout << i << "\t";
		cout << endl;
	}
	else
		cout << "no match" << endl;
	getchar();
	return 0;
}




边排列边判断,有符合的结果则返回。所有排列结果不必保存,只需要保存符合答案的那一个。


扩展3 八皇后问题


分析:8x8棋盘,8个皇后必定位于不同的行,每一行一个皇后,那么可变的就是每一行皇后的列号,而列肯定不相同,所以可以用一个数组array[i],i从0到7代表行,array[i]代表第i+1行皇后的列号,范围从0-7,只需要做0、1、2、3、4、5、6、7的全排列,然后判断全排列那一个符合要求,其实这里已经满足不同行不同列,那么只需要判断任意两个行的皇后他们不再一个对角线上,对角线判断 i - j == array[i] - array[j] 和 i - j == array[j]-array[i](正反对角)即可:

n皇后代码:

#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
using namespace std;

void perm(vector<int>input, int k, vector<vector<int> >&res)
{
	if (k == input.size())
	{
		res.push_back(input);
	}
	for (int i = k; i < input.size(); ++i)
	{
		int temp = input[i];
		input[i] = input[k];
		input[k] = temp;
		perm(input, k + 1, res);
		temp = input[i];
		input[i] = input[k];
		input[k] = temp;
	}
}

vector<vector<int> > perm(vector<int> input)
{
	vector<vector<int> >res;
	if (!input.empty())
	{
		perm(input, 0, res);
	}
	return res;
}

//判断列号组合是不是皇后问题的解(判断在不在对角线)
bool isMatched(vector<int> array)
{
	if (array.empty()) return false;
	int len = array.size();
	for (int i = 0; i < len; ++i)
	{
		for (int j = i + 1; j < len; ++j)
		{
			//判断在一条对角上
			if ((j - i) == (array[j] - array[i]) ||
				(j - i) == (array[i] - array[j]))
				return false;
		}
	}
	return true;
}

int Queens(unsigned int numbers, vector<vector<int> > &res)
{
	if (numbers == 0) return -1;

	vector<int>input(numbers, 0);
	vector<vector<int> > ans;
	vector<vector<int> > arrays;
	for (unsigned int i = 0; i < numbers; ++i)
		input[i] = i;
	arrays = perm(input);
	int count = 0;
	for (auto v : arrays)
	{
		if (isMatched(v))
		{
			ans.push_back(v);
			++count;
		}
	}
	res = ans;
	return count;
}

int main()
{
	vector<vector<int> > res;
	int n = Queens(8, res);
	if (n)
	{
		for (auto v : res)
		{
			for (auto i : v)
				cout << i << " ";
			cout << endl;
		}
		cout << "共" << n << "个" << endl;
	}

	getchar();
	return 0;
}



总结:剑指offer28字符串排列这一题涉及很多知识,扩展出很多东西,值得花时间去整理。包括排列组合、分解问题的思路、递归的思路、八皇后等等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值