随便敲敲算法(三)

            昨天下午打乒乓球了,爽爽的,各种抽球,以后多运动下,结果导致不想吃饭,吃个半个西瓜,结果肚子坏了,可悲,晚上看了两个算法,一个是关于重复数字的问题,统计出来,另一个是全排列。那就先说第一个吧,我也查了很多博客,各有各的想法,我整理了一下,1.给定数组A,大小为n,数组元素为1到n的数字,不过有的数字出现了多次,有的数字没有出现。请设计算法和程序,统计哪些数字没有出现,哪些数字出现了多少次。能够在O(n)的时间复杂度,O(1)的空间复杂度要求下完成么?

            关于详细的分析请参考:http://www.cnblogs.com/codershell/p/3288013.html 人家的解释的很清楚,这个里面设计到了面试常考的问题,你到底要牺牲哪一个,时间还是空间,很多算法习题,要不是时间换取空间,要不空间换取时间,一般很难做到二者兼得。下面我给出几种方法:

             

#include<iostream>

using namespace std;

int a[10000];

void repetitions(int a[], int n)  
{  
    int i = 1;  
	for(i = 1; i <= n; ++i)  
		a[i] *= n;  
	for(i = 1; i <= n; ++i){  
		++a[a[i]/n];  
        
	}  
	for(i = 1; i <= n; ++i){  
		cout << i << "出现了" << a[i] % n << "次" << endl;  
	}  
}
int main()
{
    int n=10;
	int i;
	for(i=1;i<=n;i++)
		scanf("%d",a+i);

	repetitions(a,n);
	for(i=1;i<=n;i++)
	printf("%d ",a[i]);
	return 0;
}


 

上面的思路是:

首先,我们介绍一种三次遍历数组的方法,我们都考虑数组从0开始:
?第一次遍历:对于每一个a[i] = a[i] * n
?第二次遍历:对于每一个i,++a[a[i]/n]
?第三次遍历:对于每一个i,a[i] % n就是出现次数
a[i]应该出现在a中的a[i]位置,乘以n、再除以n,很容易的来回变换;第二次遍历,对于a[i]本来所在的位置不断增1,但绝对不对超出n的,那每一个i出现的次数,就是a[i]对n取余。

 

  这个人里面也有一种做法 http://blog.csdn.net/theone10211024/article/details/14226393

 

include<iostream>

using namespace std;

int a[10000];

void GetNums(int a[],int n)
{
    if(a==NULL||n<=0)
    {
        return;
    }
    else
    {
        for(int i=0;i<n;i++)
        {
            //a[a[i]%n]+=n;//仔细研读处理技巧
		a[a[i]%n]=a[a[i]%n]+n;
	    printf("%d ",a[a[i]%n]);
		
        }
        for(i=1;i<n;i++)
        {
           cout<<i<<":"<<(a[i]-1)/n<<endl;//思考为什么要减1(提示:数组中数据全部是N)
        }
         cout<<n<<":"<<(a[0]-1)/n<<endl;
    }
}
int main()
{
    int n=10;
	int i;
	for(i=0;i<n;i++)
		scanf("%d",a+i);

	 GetNums(a,n);
for(i=0;i<n;i++)
	printf("%d ",a[i]);
	return 0;
}


上面的两句注释是他没有解释,我来解释一下,因为自己在那里确实遇到了问题,取余的话防止数组越界,对于一数如果已经出现了一次,那么对她加n,那么下次在a[a[i]]中不取余就会出问题了,还有那个为什么要-1,因为假如这个数没有出现,并且不-1,那么算出来的结果,就会记录她出现了一次。

 

第二道题是全排列的问题,这个真的很重要,面试题也经常出现,因为难度适中,而且也用到了递归。详细的解释看这个人的博客http://blog.csdn.net/morewindows/article/details/7370155/  这个里面也涉及到了重复值的问题,考虑的很周到,值得一看。自己也差不多看懂。

#include <stdio.h>
#include <string.h>

void Swap(char *a,char *b)
{
	char t;
	t=*a;
	*a=*b;
	*b=t;
}
bool IsSwap(char *pszStr,int nBegin,int nEnd)
{
	for(int i = nBegin;i<nEnd;i++)
	     if(pszStr[i]==pszStr[nEnd])
	     	return false;
    return true;
}
void AllRange(char *pszStr,int k,int m)
{
	if(k==m)
	{
		static int s_i=1;
		printf("第%3d个排列\t%s\n",s_i++,pszStr);
	}
	else
	{
		for(int i=k;i<=m;i++)
		{
			if(IsSwap(pszStr,k,i))
			{
				Swap(pszStr+k,pszStr+i);
				AllRange(pszStr,k+1,m);
				Swap(pszStr+k,pszStr+i);
			}
		}
	}
}
void Foo(char *pszStr)
{
	AllRange(pszStr,0,strlen(pszStr)-1);
}
int main(int argc, char const *argv[])
{
	printf("去重全排列的递归实现\n");
	char szTextStr[]="122";
	printf("%s的全排列如下:\n",szTextStr);
	Foo(szTextStr);
	return 0;
}


 

 

 

 

 

 

           

 

       

         

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值