一. 整数划分问题
整数划分问题是指把一个正整数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;
}
四. 归并排序
合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(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");
}
运行结果
五. 快速排序
快速排序算法通过多次比较和交换来实现排序,以升序为例,其执行过程为,首先选一个关键字(通常选择第一个关键字)作为关键字,将子序列中比关键字小的移动到关键字前边,比关键字大的移动到关键字后边,本趟划分完毕后对关键字左右两侧的两个子序列再进行递归操作
代码
#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
*/
运行结果
循环赛日程表问题,大数乘法问题,棋盘覆盖问题见第二篇递归分治算法点击跳转
参考文献 《计算机算法设计与分析(第四版)》 王晓东 编著