zoukankan      html  css  js  c++  java
  • 算法设计思想

    程序=数据结构+算法+程序设计语言

    项目=程序+项目管理+质量控制+代码规范

        首先根据输入输出数据设计数据结构,再次设计相应的算法,最后使用某种编程语言。数据结构是算法实现的基础,算法依赖数据结构快速实现,二者相辅相承。

    1.递推思想

      顺推法

        假设有一对兔子,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?即斐波那契数列。

        使用顺推法计算,F(n)=F(n-1)+F(n-2)。最后F(n-1)/F(n)=>0.618即黄金分割比。

         逆推法

        例如:假设父亲为小龙一次性存入大学的生活费,只容许其每个月初取出1000供下个月使用,考虑月利息为0.71%,且银行在每月最后一天才计算利息计入本金。问开学时应该存多少钱。思想:

           第48个月,月初时,账户剩余F(48)=1000
             第47个月,月初时,账户剩余F(47)=(1000/(1+0.0071))+1000=F(48)/(1+0.0071)+1000
             第46个月,月初时,账户剩余F(46)=F(47)/(1+0.0071)+1000
             ............

        使用逆推,最终得到F(1)

    2.穷举思想

       满足2个条件:

            ⑴ 候选答案可数
            ⑵ 候选答案有确定的集合(便于使用条件判断语句和循环语句)

       例如:任意输入A,B,C,D,E,遍历加减乘除运算符,使“A( )B( )C( )D( )E =F”等式成立。思想:设定left,rigtht保存临时结果。left保存上次运算的结构,即下次运算的左侧,right保存下次将参加运算的数据,即下次运算的右侧内容。倘若为乘或除,直接运算right,倘若为加减,保存当前运算符,然后检查下一个运算符。若下一运算符为乘除,直接计算j结果保存至right,反之,计算left=left+flag*right。Operator[i]=0-4分别表示加减乘除。代码如下:

    #include"iostream"
    using namespace std;
    void print(int num[5],int Operator[4],int answer)//输出某个解决方案
    {
    	int i=0;
    	char index[4]={'+','-','*','/'};
    	cout<<num[0];
    	while(i<4)
    	  {cout<<index[Operator[i]];cout<<num[i+1];i++;}
    	cout<<"="<<answer<<endl;
    }
    void findSolution(int num[5],int answer)//嵌套循环遍历所有的解决方案
    {
    	int count=0;//计算器
    	int flag;//1=+,-1=-
    	int Operator[4];
    	double left,right;//切记此处一定定义为double
    
    	//嵌套遍历
    	for(Operator[0]=0;Operator[0]<4;Operator[0]++) 
    		if(Operator[0]<3||num[1]!=0) //当为除的时候,运算数不为0
    		  for(Operator[1]=0;Operator[1]<4;Operator[1]++)
    			if(Operator[1]<3||num[2]!=0)
    			  for(Operator[2]=0;Operator[2]<4;Operator[2]++)
    				if(Operator[2]<3||num[3]!=0)							
    				 for(Operator[3]=0;Operator[3]<4;Operator[3]++)
    					if(Operator[3]<3||num[4]!=0)
    					  {left=0;right=num[0];flag=1;
    						for(int i=0;i<4;i++)
    						{
    							switch(Operator[i])
    							{
    							    case 0:
    									 left=left+flag*right;//运算的是当前符号之前2个数
    									 right=num[i+1];
    									 flag=1;//保存当前符号
    									 break;
    								case 1:
    									left=left+flag*right;//运算的是当前符号的前2个数
    									right=num[i+1];
    									flag=-1;//保存当前符号,在后序操作有优先运算符的时候起作用
    									break;
    								case 2:
    									right=right*num[i+1];
    									break;
    								case 3:
    									 right=right/num[i+1];
    									 break;
    							}
    						}
    					    if(left+flag*right==answer)
    					          {count++;cout<<count<<":";print(num,Operator,answer);}
    	               }
    	if(count)
    	     cout<<"总共有"<<count<<"种方案"<<endl;
    	else
    	     cout<<"无有效解决方案,请重新输入"<<endl;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        cout<<"输入6个数,最后一个数为目标结果"<<endl;
        int num[5],answer;
        for(int i=0;i<5;i++) cin>>num[i];
        cin>>answer;
        findSolution(num,answer);
        return 0;
    }

    3.递归思想

          使用递归算法应该注意:

           ⑴必须有明确的递归结束条件即递归出口
           ⑵递归显得简洁但是运行效率低,主要反映在占存储空间
           ⑶递归过程中次数过多可能导致栈溢出(系统将每一次递归调用的返回点、局部变量等存入系统的栈中,      

       例如:10进制转换为任意进制。

    void Convert(vector<int> &ans,int number,int n)
    {
            if(!number) return;
    	else
    	   {ans.push_back(number%n);
    	   Convert(ans,number/n,n);}
    }

    4.分治思想

               分治算法和递归算法常常是紧密结合使用的。将一个问题分成几个子问题,然后将几个子问题的答案合并起来就是上一级的答案。一般具有以下特征的问题,可用分治法解答:

           ⑴可以分解成若干小的问题
           ⑵当分界到某种规模时,极易求解
           ⑶合并子问题的解可以得到求解问题的解
           ⑷子问题之间的解相互独立

       例如:在《算法导论》第三版的40页提到这样的问题,重新解释该问题,即为求解某个一维矩阵中,相加值最大的子矩阵。这里使用分治法基于以下策略思考。谭若在矩阵中设立标号mid。那么这个子矩阵要么完全在mid之前或者之后,要么横跨mid,每一级分治中,都寻找这样的3个子数组然后比较大小进行决策。代码如下:

    #include"iostream"
    #include"cmath"
    using namespace std;
    
    typedef struct submax
    {int low,high,maxsum;}SubArray;
    
    SubArray FindCrossingMax(int data[],int length,int low,int middle,int high)//寻找跨越middle的最大子数组
    {
        int present_left_sum=data[middle],left_max=data[middle];//寻找最大左子数组
    	int left_label=middle;
    	for(int j=middle-1;j>=low;j--)
    	 {present_left_sum+=data[j];
    	   if(left_max<present_left_sum)
    	     {left_max=present_left_sum;left_label=j;}}
    
        int present_right_sum=data[middle+1],right_max=data[middle+1];//寻找最大右子数组
        int right_label=middle+1;
        for(int j=middle+2;j<=high;j++)
    	{present_right_sum+=data[j];
    	if(right_max<present_right_sum)
    	{right_max=present_right_sum;right_label=j;}}
    	
        SubArray max={left_label,right_label,left_max+right_max};//跨越最大子数组=左子数组(含mid)+最大右子数组(含mid+1)
    	return max;
    }
    SubArray FindMaxSubArray(int data[],int length,int low,int high)//寻找最大子数组
    {
    	if(high==low)
    		{SubArray max={low,high,data[low]};return max;}
    	else
    		{int mid=floor(((double)high+low)/2);
    	        SubArray leftmax=FindMaxSubArray(data,length,low,mid);//寻找最大左(相对middle)子数组
    		SubArray rightmax=FindMaxSubArray(data,length,mid+1,high);//寻找最大右(相对middle)子数组
    		SubArray crossingmax=FindCrossingMax(data,length,low,mid,high);//寻找跨越middle的最大子数组
    		  
    		  if(leftmax.maxsum>=rightmax.maxsum&&leftmax.maxsum>=crossingmax.maxsum)
    			  return leftmax;
    		  else if(rightmax.maxsum>=leftmax.maxsum&&rightmax.maxsum>=crossingmax.maxsum)
    			  return rightmax;
    		  else
    			 return crossingmax;
    	    }
    }
    
    SubArray FindMaxSubArray(int data[],int length)//非递归法计算最大子数组
    {
    	int max=data[0];
    	int low=0,high=0;//初始化
    	for(int i=1;i<length;i++)
    	    {int present_sum=data[i],present_max=data[i];
    	     int label=i;
    		  for(int j=i-1;j>-1;j--)
    		      {present_sum+=data[j];
    			   if(present_max<present_sum)
    			      {present_max=present_sum;label=j;}}
    		  if(present_max>max)
    		       {max=present_max;low=label;high=i;}  
    	     }
    	SubArray maxarray={low,high,max};
    	return maxarray;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int  data[]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
    	SubArray max=FindMaxSubArray(data,sizeof(data)/sizeof(int),0,sizeof(data)/sizeof(int)-1);//分治法
    	SubArray max=FindMaxSubArray(data,sizeof(data)/sizeof(int));//非分治法
    	for(int i=max.low;i<=max.high;i++)
    	  cout<<data[i]<<" ";
    
    	return 0;
    }

    5.贪婪思想

          解为局部最优解。但对大部分问题,该解基本上近似整体最优解。贪婪算法基于当前初始解,采取逐步逼近的策略,不需要回溯。该算法存在以下问题

          ⑴不能保证解最优
          ⑵不能用来求最大最小解问题
          ⑶只能满足某些约束条件可行解的范围。

        一般实现策略如下:

          解初始化;
          while(尚未达到可行解总目标)
             求可行解的一个解元素;
          所有元素组成一个初始解;

        

    6.试探思想

    7.模拟思想


  • 相关阅读:
    H5图片上传、压缩
    数据库基本操作
    数组遍历
    CURL
    获取IP
    Memcached的实战笔记
    修bug总结 (基于java语言)
    java开发工作的总结
    多线程测试类
    可清除的单例对象获取类
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5393120.html
Copyright © 2011-2022 走看看