C&C++实现算法习题第一部分—递归分治算法(一)

6 篇文章 0 订阅

一. 整数划分问题

整数划分问题是指把一个正整数n写成多个大于等于1且小于等于其本身的整数的和,则其中各加数所构成的集合为n的一个划分。这是一个典型的递归算法。
含最大化分数的整数划分问题是指为被化分数设置最大化分数,例如当最大化分数为4时,进行整数划分时就从4往下逐次划分

代码
#include<iostream>
using namespace std;
#define maxn 1024

void display(int *result, int length)
{
	cout << result[0];//6
	for (int i = 1; i < length; i++)
	{
		cout << '+' << result[i];
	}
	cout << endl;
}
int q(int n, int m, int *result, int length)
{
	if (n >= 0 && m == 1)
	{   //只有一种划分形势,即1+1+1+1......
		if (n == 0)
			display(result, length);
		else
		{
			result[length] = 1;
			q(n - 1, m, result, length + 1);
		}
		return 1;
	}
	else if (n == 1 && m > 1)
	{
	    //只有一种情况,输出的是n本身
		result[length] = n;
		display(result, length + 1);
		return 1;
	}
	else if (n < m)
        //从n本身开始往下递归,一直到输出1+1+1+1......
		return q(n, n, result, length);
	else if (n == m)
	{
        //划分由n1=n和n1<=n-1
		result[length] = m;
		display(result, length + 1);
		return q(n, n - 1, result, length) + 1;
	}
	else
	{
	    //n>m
	    //由n1=m的划分和n1<=m-1的划分组成
		result[length] = m;//5  4
		return  q(n - m, m, result, length + 1) + q(n, m - 1, result, length);
	}
}
int main(void)
{
	//const int maxn = 1010;
	int n,m;
	int buf[maxn];//存储拆分的数
	int length = 0;//
	//输入整数和最大化分数
	cin >> n >> m;
	if (n == 0){
        cout<<0;
	} else {
        q(n, m, buf, length);
	}

	return 0;
}

运行结果

图一- 整数划分

二. 二分查找

二分查找,又称折半查找,二分查找要求数据是有序的。
在二分查找的过程中,首先找到中间元素,R[mid],然后与待查找元素相比较,若相等,则查找成功,返回该位置,若R[mid]>k,则由有序性知,k位于R[mid]左侧的子表中,递归查找R[low,…,mid-1]的部分,若R[mid]<k则同理。

代码
#include<stdio.h>
//2 4 6 8
int binSearch(int a[],int low,int big,int k){//此方法为递归算法
    int mid;//定义指向中间的指针
    if(low>big){
        return -1;
    }else{
        mid=(big+low)/2;//为mid赋值
        if(a[mid]==k){      //如果中间的是要返回的数
            return mid;//当找到到的时候返回该数的下标
        }
        else if(a[mid]>k){//如果当前的数的值大于k
            binSearch(a,low,mid-1,k);//调用递归算法
        }
        else if(a[mid]<k){//如果当前的数的值小于k
            binSearch(a,mid+1,big,k);//调用递归算法
        }
    }
}

int main(){
    int a[10001];//定义一个数组用来存放元素
    int n,i=0;//定义一个存放个数和计数器
    printf("请输入你要输入的元素的个数:");
    scanf("%d",&n);
    printf("请输入要存入的元素");
    for(;i < n;i++)
    {
        scanf("%d",&a[i]);
    }
    //n=i;//n是数组里存放元素的个数
    printf("请输入你要查找的元素的数值:\n");
    int k;
    scanf("%d",&k);
    i=binSearch(a,0,n-1,k);//递归算法
    printf("查找到的元素是第%d个\n",i+1);//打印输出得到的下标的数
    return 0;
}

运行结果

图二. 二分查找

三. 二分法求a^n

给定a和n,求a的n次方
值得注意的是,当n为负数时,得到a相反数n次方分之一

代码
#include<stdio.h>

double compute(double x, int n) {
    if (n == 1)return x;
    else{
        double root = compute(x, n / 2);
        if(n%2==1)return root*root*x;
        else return root*root;
    }
}
int main() {
    float x = 0.0;
    int n = 0;
    float result = 0.0;
    printf("输入a和n的值");
    scanf("%f%d",&x,&n);
    if (n > 0) {
        result = compute(x, n);
    }
    else if (n < 0) {
        //负数,取相反数n次方分之一
        result = 1 / compute(x, -n);
    }
    else result = 0;
    printf("%f",result);
    result = 0;
}

图三 a^n

四. 归并排序

合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并操作

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},
第二次归并后:{6,100,202,301},{1,8,38},
第三次归并后:{1,6,8,38,100,202,301}。

代码
#include<stdio.h>
#define MAXSIZE 30
//124 42 433 568 8989 77 45 43 21 1
void Merge(int R[],int R1[],int left,int middle,int right)
{   //合并R2两部分到R1数组中
   int i,j,k;
   i=left;  //用来遍历R数组前半截
   j=middle+1;  //用来遍历R数组后半截
   k=left;      //用来从第一个开始存到R1数组中
   while(i<=middle&&j<=right)
    //将两个有序表放到表R1中使表R1也为有序表
      if(R[i]<=R[j])
         R1[k++]=R[i++];
      else
         R1[k++]=R[j++];
   while(i<=middle)		  
      R1[k++]=R[i++];
   while(j<=right)		  
      R1[k++]=R[j++];
}
void MSort(int R[],int R1[],int left,int right)
{
   int middle;          //保存中点
   int R2[MAXSIZE];
   if(left==right)
      R1[left]=R[left];    //已经遍历到最里头了
   else
   {
      middle=(left+right)/2;	  //找到无序表R[s]~R[t]的中间位置
      MSort(R,R2,left,middle);
      MSort(R,R2,middle+1,right);
      Merge(R2,R1,left,middle,right);
   }
}
void main()
{
   int i=1,j,num;
   int R[MAXSIZE],R1[MAXSIZE];
   printf("输入一组数字按回车结束:\n");
   while(1){
        scanf("%d",&num);
        char c=getchar();
        R[i++]=num;
        if(c=='\n'){
            break;
        }
   }

   MSort(R,R1,1,i-1);                             //进行归并排序
   printf("\n排序后的结果:\n");           //输出归并排序后的结果
   for(j=1;j<i;j++)
	  printf("%d    ",R1[j]);
   printf("\n");
}

运行结果

图4

五. 快速排序

快速排序算法通过多次比较和交换来实现排序,以升序为例,其执行过程为,首先选一个关键字(通常选择第一个关键字)作为关键字,将子序列中比关键字小的移动到关键字前边,比关键字大的移动到关键字后边,本趟划分完毕后对关键字左右两侧的两个子序列再进行递归操作

代码

#include<stdio.h>
#define MAXSIZE 30
int Partition(int R[],int i,int j)    //划分算法
{           //对R[i]~R[j],以R[i]为基准记录进行划分,并返回R[i]在划分后的正确位置
   R[0]=R[i];		                    //暂存基准记录R[i]
   while(i< j)		                    //从表(即序列R[i]~R[j])的两端交替向中间扫描
   {
      while(i<j&&R[j]>=R[0])
         //从右向左扫描查找小于R[0]的R[j]
         j--;
      if(i< j)              //当i<j时则R[j]小于R[0]将R[j]交换到表的左端
	  {
         R[i]=R[j];
         i++;
	  }
      while(i<j&&R[i]<=R[0])
         //从左到右扫描查找第一个关键字大于R[0]的R[i]
         i++;
      if(i<j)               //当i<j时则R[i].key大于R[0].key将R[i]交换到表的右端
	  {
         R[j]=R[i];
         j--;
	  }
   }
   R[i]=R[0];               //将基准记录R[0]送入最终(指排好序时)应放置的位置
   return i;		        //返回基准记录R[0]最终放置的位置
}
void QuickSort(int R[],int left,int right)    //进行快速排序
{
   int i;
   if(left<right)
   {
      i=Partition(R,left,right);
      //i为基准记录的位置并由此将表分为两部分
      QuickSort(R,left,i-1);		            //对表R[s]~R[i-1]进行快速排序
      QuickSort(R,i+1,right);                  //对表R[i+1]~R[t]进行快速排序
   }
}
void main()
{
   int i=1,j,num;
   int R[MAXSIZE];
   printf("输入一组数字按回车结束:\n");
   while(1){
        scanf("%d",&num);
        char c=getchar();
        R[i++]=num;
        if(c=='\n'){
            break;
        }
   }

   QuickSort(R,1,i-1);                           //进行快速排序
   printf("\n排序后的结果:\n");         //输出快速排序后的结果
   for(j=1;j<i;j++)
	   printf("%d  ",R[j]);
   printf("\n");
}
/*
124 42 433 568 8989 77 45 43 21 1
*/

运行结果

图5
循环赛日程表问题,大数乘法问题,棋盘覆盖问题见第二篇递归分治算法点击跳转

参考文献 《计算机算法设计与分析(第四版)》 王晓东 编著

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值