网易邮箱事业部笔试题:给定一个数字n,求其全排列的和

今天班里同学分享了网易的笔试题,其中的一道题觉得挺有价值的,自己参考了网上的一些相关的资料做出了一种解法,发在这里与大家分享下,有不正确的地方请大家批评指正。

题目:

给定一个数字 n (1<n<99999),例如123,那么组成这个数字的排序可能是 123,132,213,231,312,321,求这些排序的和。简单起见,n中不含数字0

 

引子:

看到这个题目,我的第一反应就是字符串(数组元素)的全排列问题,如果能求出给定数n所有位的全部排列,直接相加就可以了。

这种全排列问题网上一搜一大把,其中何海涛老师的博客里讲解较为详细,在此我就不赘述了,直接贴出代码(代码求的是字符串的排列,其他类型数组的思路是一样的),详细的分析请看何海涛老师的博客文章http://zhedahht.blog.163.com/blog/static/254111742007499363479/

void Permute1(char *pStr, char *pBegin)
{
if(NULL == pStr || NULL == pBegin)
return;

if(*pBegin == '\0')
{
printf("%s\n", pStr);
}
else
{
char *pCh = pBegin;
while(*pCh != '\0')
{
char temp = *pCh;
*pCh = *pBegin;
*pBegin = temp;

Permute1(pStr, pBegin + 1);

temp = *pCh;
*pCh = *pBegin;
*pBegin = temp;
++pCh;
}
}
}

void Permute1(char *pStr)
{
Permute1(pStr, pStr);
}


去除重复的排列:

上面的解法没有考虑字符串中含有重复字符的情况,比如输入bab,会输出两次bab,abb等等,问题出在哪呢?出在交换的地方,请看以下分析

固定第一个字符b,递归求ab的排列,这里没问题,能够输出bab,bba;

交换第一个字符b和第二个字符a,此时为abb,递归求bb的排列,这时会输出两次abb,问题出现;

交换第一个字符b和第三个字符b,此时为bab,与没交换时一样,递归求ab排列,又输出bab,bba。

好了,分析到这里,已经能说明问题了。在交换pBegin与pCh的时候:

(1)假如*pCh == *pBegin,那么交换后与交换前是一样的,所以会产生重复的输出。

(2)在pBegin与pCh之间,如果存在某个字符p与*pCh相等,由于这个p在*pCh之前,所以,它肯定已经和pBegin交换过了,那么如果*pCh再与*pBegin交换,就会和p与pBegin交换的结果一样了,从而会产生重复。

终上所述,在交换pBegin与pCh之前,要先判断一下从pBegin开始到pCh - 1这一段区域里,是否存在与*pCh相等的字符。存在,则不需要交换,否则交换pBegin与pCh。

基于以上的分析,修改后的permute函数如下

void Permute2(char *pStr, char *pBegin)
{
if(NULL == pStr || NULL == pBegin)
return;

if(*pBegin == '\0')
{
printf("%s\n", pStr);
}
else
{
for(char *pCh = pBegin; *pCh != '\0'; ++pCh)
{
bool flag = false;
char *pTemp = pCh - 1;
while(!flag && pTemp >= pBegin)
{
if(*pTemp-- == *pCh)
flag = true;
}
if(flag)
continue;
char temp = *pCh;
*pCh = *pBegin;
*pBegin = temp;

Permute2(pStr, pBegin + 1);

temp = *pCh;
*pCh = *pBegin;
*pBegin = temp;

}
}
}

void Permute2(char *pStr)
{
Permute2(pStr, pStr);
}


重回全排列的和问题

回到本文问题,思路应该就比较明朗了。

我的思路是,利用上述求不重复全排列的方法,求出所有的排列,然后相加。具体实现代码如下

void PermuteSum(std::vector<int>& v_a, int beg, int& sum)
{
if(v_a.size() < 1)
return;

if(beg == v_a.size())
{
//每找到一个排列就累加进sum
int factor = 1;
for(int i = v_a.size() - 1; i >= 0; --i)
{
sum += v_a[i] * factor;
factor *= 10;
}
}
else
{
for(int i = beg; i < v_a.size(); ++i)
{
bool flag = false;
int j = i - 1;
while(!flag && j >= beg)
{
if(v_a[j--] == v_a[i])
{
flag = true;
break;
}
}

if(flag)
continue;

int temp = v_a[beg];
v_a[beg] = v_a[i];
v_a[i] = temp;

PermuteSum(v_a, beg + 1, sum);

temp = v_a[beg];
v_a[beg] = v_a[i];
v_a[i] = temp;

}
}
}

int PermuteSum(int a)
{
int sum = 0;
std::vector<int> v_a;
while(a != 0)
{
//这里把a的每一位放入vector中,虽然逆序了,但不影响排列,所以不需要再做一次逆序
v_a.push_back(a % 10);
a = a / 10;
}
PermuteSum(v_a, 0, sum);
return sum;
}

 

以上就是我的解法,不知道还有没有更好的解法,欢迎大家来讨论批评指正

posted @ 2012-04-05 01:33  粗暴的香蕉  阅读(494)  评论(2编辑  收藏  举报