剑指Offer----面试题28:字符串的排列 & 去重

题目:


输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印输出字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab、cba。

分析:

首先求得所有可能出现在第一个位置上的字符,即把第一个字符和后边所有的字符交换;
固定第一个字符,求后面所有字符的排列。

如abc:
第一步:将第一行数据分别和第二行和第三行数据交换
abc
bac
cab
   

第二步:保持第一列不变,将第二行数据分别和第三行数据交换
acb
bca
cba

交换完毕,显然,要用到递归的知识。

方法一:


源代码如下:
#include<iostream>

using std::endl;
using std::cout;

void Permutation(char *str);
void Permutation(char *str, char *begin);

void Permutation(char *str)
{
	if (str == NULL)
	{
		cout << "Thr array is empty" << endl;
		return;
	}

	cout << str << "的排列为:";
	Permutation(str, str);
}

void Permutation(char *str, char *begin)
{
	if (*begin == '\0')
		cout << str << "  ";
	else
	{
		for (char *pCh = begin; *pCh != '\0'; ++pCh)
		{
			//交换第一个字符和当前所指向的字符
			char temp = *pCh;
			*pCh = *begin;
			*begin = temp;

			Permutation(str, begin + 1);

			//变回原来的形式
			temp = *pCh;
			*pCh = *begin;
			*begin = temp;
		}
	}
}

void test11()
{

	cout << "\t=======测试空指针========" << endl;
	Permutation(NULL);
}

void test12()
{

	cout << "\t=======测试只有分隔符的指针========" << endl;
	char ch[] = "";
	Permutation(ch);
}

void test13()
{
	cout << "\t=======测试只有一个元素的指针========" << endl;
	char ch[] = "a";
	Permutation(ch);
}

void test14()
{
	cout << "\t=======测试只有多个元素的指针========" << endl;
	char ch[] = "abc";
	Permutation(ch);
}

void test15()
{
	cout << "\t=======测试只有多个元素的指针========" << endl;
	char ch[] = "abcd";
	Permutation(ch);
}


int main()
{
	test11();
	cout << endl;

	test12();
	cout << endl;

	test13();
	cout << endl;

	test14();
	cout << endl;

	test15();
	cout << endl;

	system("pause");
	return 0;
}

运行结果:

        =======测试空指针========
Thr array is empty

        =======测试只有分隔符的指针========
的排列为:
        =======测试只有一个元素的指针========
a的排列为:a
        =======测试只有多个元素的指针========
abc的排列为:abc  acb  bac  bca  cba  cab
        =======测试只有多个元素的指针========
abcd的排列为:abcd  abdc  acbd  acdb  adcb  adbc  bacd  badc  bcad  bcda  bdca
bdac  cbad  cbda  cabd  cadb  cdab  cdba  dbca  dbac  dcba  dcab  dacb  dabc
请按任意键继续. . .



方法二:


原理和方法一相同,但是代码的实现方式略有不同。

源代码如下:
#include<iostream>
#include<cstdlib>
#include<cstdio>

using std::cout;
using std::endl;

int num = 1;  //局部静态变量,用来统计全排列的个数  

//k表示当前选取到第几个数,m表示公有多少个数
void Permutation3(char *pStr, int k, int m)
{
	if ((pStr == NULL) || (m == 0))
		return;

	if (k == m)
	{
		printf("第%d个排列\t%s\n", num++, pStr);
	}
	else
	{
		for (int i = k; i < m; i++)
		{
			std::swap(*(pStr + k), *(pStr + i));
			Permutation3(pStr, k + 1, m);
			std::swap(*(pStr + k), *(pStr + i));
		}
	}
}

void test1111()
{

	cout << "\t=======测试空指针========" << endl;
	Permutation3(NULL, 0, 0);
}

void test1222()
{

	cout << "\t=======测试只有分隔符的指针========" << endl;
	char ch[] = "";
	Permutation3(ch, 0, 0);
}

void test1333()
{
	cout << "\t=======测试只有一个元素的指针========" << endl;
	char ch[] = "a";
	Permutation3(ch, 0, 1);
}

void test1444()
{
	cout << "\t=======测试只有多个元素的指针========" << endl;
	char ch[] = "abc";
	Permutation3(ch, 0, 3);
}

void test1555()
{
	cout << "\t=======测试只有多个元素的指针========" << endl;
	char ch[] = "abcd";
	Permutation3(ch, 0, 4);
}


int main()
{
	test1111();
	cout << endl;
	num = 1;

	test1222();
	cout << endl;
	num = 1;

	test1333();
	cout << endl;
	num = 1;

	test1444();
	cout << endl;
	num = 1;

	test1555();
	cout << endl;
	num = 1;

	system("pause");
	return 0;
}

运行结果如下:
        =======测试空指针========

        =======测试只有分隔符的指针========

        =======测试只有一个元素的指针========
第1个排列       a

        =======测试只有多个元素的指针========
第1个排列       abc
第2个排列       acb
第3个排列       bac
第4个排列       bca
第5个排列       cba
第6个排列       cab

        =======测试只有多个元素的指针========
第1个排列       abcd
第2个排列       abdc
第3个排列       acbd
第4个排列       acdb
第5个排列       adcb
第6个排列       adbc
第7个排列       bacd
第8个排列       badc
第9个排列       bcad
第10个排列      bcda
第11个排列      bdca
第12个排列      bdac
第13个排列      cbad
第14个排列      cbda
第15个排列      cabd
第16个排列      cadb
第17个排列      cdab
第18个排列      cdba
第19个排列      dbca
第20个排列      dbac
第21个排列      dcba
第22个排列      dcab
第23个排列      dacb
第24个排列      dabc

请按任意键继续. . .


考虑去重问题:

由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。

这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。

源代码:

#include<iostream>
using namespace std;
#include<assert.h>

//在[nBegin,nEnd)区间中是否有字符与下标为pEnd的字符相等
bool IsSwap(char* pBegin, char* pEnd)
{
	char *p;
	for (p = pBegin; p < pEnd; p++)
	{
		if (*p == *pEnd)
			return false;
	}
	return true;
}
void Permutation4(char* pStr, char *pBegin)
{
	assert(pStr);

	if (*pBegin == '\0')
	{
		static int num = 1;  //局部静态变量,用来统计全排列的个数
		printf("第%d个排列\t%s\n", num++, pStr);
	}
	else
	{
		for (char *pCh = pBegin; *pCh != '\0'; pCh++)   //第pBegin个数分别与它后面的数字交换就能得到新的排列   
		{
			if (IsSwap(pBegin, pCh))
			{
				swap(*pBegin, *pCh);
				Permutation4(pStr, pBegin + 1);
				swap(*pBegin, *pCh);
			}
		}
	}
}

int main(void)
{
	char str[] = "abab";
	Permutation4(str, str);

	system("pause");
	return 0;
}


运行结果如下:
第1个排列       abab
第2个排列       abba
第3个排列       aabb
第4个排列       baab
第5个排列       baba
第6个排列       bbaa
请按任意键继续. . .
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值