一、实验目的
1. 理解递归算法基本思想。
2. 掌握递归算法设计的步骤。
3. 掌握使用递归算法分析问题并编写代码的能力。
二、实验环境
1. 操作系统:Windows 10及以上。
2. 程序设计软件:Microsoft Visual C++、Microsoft Visual Studio或DEV C++等。
三、实验要求
1. 完整记录实验过程,包括算法设计思想描述、代码实现和程序测试。
2. 按照实验内容编写相应功能的程序,记录完成步骤、结果及在实验过程中所遇到的问题。
四、实验内容
1. 使用递归算法求解汉诺塔问题。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
2. 使用递归算法求解斐波那契数列前30项。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
3. 逆置单链表。
对于不带头结点的单链表L,设计一个递归算法逆置所有结点。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
4. 求二叉树中最大和的路径。
假设二叉树中的所有结点值为int类型,采用二叉链存储。设计递归算法求二叉树bt中从根节点到叶子结点路径和最大的一条路径。例如,对于下图所示的二叉树,路径和最大的一条路径是5->4->6,路径和为15。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
5. 对于给定的含有n个元素的数组a,使用递归算法分别采用简单选择排序和冒泡排序按元素值递增排序。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
1. 使用递归算法求解汉诺塔问题。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
算法设计思路:
根据课本13页例1.8的对应算法函数来写,已假设n为盘片值,x、y、z柱子分别为初始位置,中转位置,目的位置,定义n的类型为int,x、y、z为char类型,分n为1和不为1的情况。如果n为1时,只有把盘片从初始位置x移向目的位置z 的一次情况;如果n不为1时,则分多次移动盘片情况,例如:n=2,将盘片1从x搬到y,将盘片2从x搬到z,将盘片1从y搬到z,主函数写输入输出。
实验程序代码:
#include<iostream>
using namespace std;
void Hanoi(int n,char x,char y,char z)//n为盘片值,x,y,z为串盘片的柱子 ,x为初始位置,y为中转位置,z为目的位置
{
if(n==1)
printf("将盘片%d从%c搬到%c\n",n,x,z);//如果盘片值n为1,即只用做一次,把盘片从初始位置x移向目的位置z
else
{
Hanoi(n-1,x,z,y);//当n大于1时,x盘片移依次移向z,y
printf("将盘片%d从%c搬到%c\n",n,x,z);//打印此时盘片位置
Hanoi(n-1,y,x,z);//y盘片移依次移向x,z
}
}
int main()
{
int n;
cout<<"请输入汉诺塔盘片值:";//输出 请输入汉诺塔盘片值:
cin>>n; //输入n
Hanoi(n,'x','y','z');//x,y,z为char类型,需加单引号
}
测试截图:
2. 使用递归算法求解斐波那契数列前30项。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
算法设计思路:
根据课本49页例2.3的对应算法函数来写,已假设n为斐波那契数列值,定义n的类型为int,分n=1或n=2和n不为1、2时候的返回数值。如果n为1或者2时,都返回1。即当n=1或n=2时,Fib(n)=1;如果n大于2时,则调用公式Fib(n)= Fib(n-1)+Fib(n-2),即返回Fib(n-1)+Fib(n-2),主函数写输入输出。
主函数写输入输出。
实验程序代码:
#include<iostream>
using namespace std;
int Fib(int n)
{
if(n==1||n==2)//如果 n=1或2,即返回值为1
return 1;
else
return Fib(n-1)+Fib(n-2);//如果n不等于1,即返回公式
}
int main()
{
int n;
cout<<"请输入n的值:";//输出 请输入n的值:
cin>>n; //输入n
cout<<Fib(n)<<endl;//输出
}
测试截图:
3. 逆置单链表。
对于不带头结点的单链表L,设计一个递归算法逆置所有结点。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
算法设计思路:
利用上个学期学的数据结构部分思想构思实验,根据单链表尾插法来写:先声明结点,定义结构体;第二步创建单链表,创建数据结点和空间存放数组,r结点开始指向头结点,后头结点利用尾插法变成尾结点,r结点始终指向尾结点,构建循环体,不断将s结点插在r尾结点之后;第三步头结点处理完后,逆置链表,p为首结点,即不断逆置p结点以及它的next,参考数据结构部分代码加上输出、销毁链表的代码,主函数代码即输入输出销毁,构成完整代码。
实验程序代码:
#include <stdio.h>
#include <malloc.h>
typedef char ElemType; //ElemType类型为char类型
typedef struct LNode {//定义结构体
ElemType data; //存放元素值
struct LNode *next; //指向后继结点
}LinkNode; //单链表结点类型
void CreateListR(LinkNode *&L, ElemType a[], int n)// 指针的引用
{
LinkNode *s, *r;//定义链表结点s、r
L = (LinkNode *)malloc(sizeof(LinkNode)); // 创建头结点
r = L; // r始终指向尾结点,开始时指向头结点
for(int i = 0; i < n; i++)//循环建立数据结点
{
s = (LinkNode *)malloc(sizeof(LinkNode));// 创建数据结点s
s->data = a[i];//创建空间存放数组
r->next = s; // 将结点s插入r结点之后
r = s;
}
r->next = NULL; // 将尾结点的next域置为NULL
}
void ReverseList(LinkNode *p, LinkNode *&L)//逆置不带头结点的单链表L
{
if(p->next == NULL) // 当以p为首结点指针的单链表只有一个结点时
{
L->next = p; // 把p结点变为尾结点
return;
}
ReverseList(p->next, L); //将p的下一个结点作为逆置单链表L的参数
p->next->next = p; // 将结点链接在尾结点之后
p->next = NULL; // 尾结点next域置为NULL
}
void DispList(LinkNode *L)//输出线性表
{
LinkNode *p = L->next; // p指向首结点
while(p != NULL)//当p结点不为空时
{
printf("%c ", p->data); // p不为NULL,输出p结点的数据域
p = p->next;// p移向下一个结点
}
printf("\n");//跳一格
}
void DestroyList(LinkNode *&L)//删除线性表
{
LinkNode *pre = L, *p = pre->next;//pre指向结点p的前驱结点
while(p != NULL)//遍历单链表L
{
free(pre);//释放pre结点
pre = p;// pre、p 同步后移一个结点
p = pre->next;
}
free(pre); // 循环结束时,p为NULL,pre指向尾结点,释放它
}
int main()
{
LinkNode *L;//定义链表头结点
char a[] = "123456789";//输入数组元素
int n = 9;//数组元素个数
CreateListR(L, a, n);//创建线性单链表
printf("L:");//打印数组元素
DispList(L);//输出单链表L
printf("逆置单链表L\n");//打印逆置单链表L
ReverseList(L->next, L);//逆置单链表
printf("L:");//打印逆置单链表的结果
DispList(L);//输出单链表L
DestroyList(L);//删除单链表L
return 0;
}
测试截图:
4. 求二叉树中最大和的路径。
假设二叉树中的所有结点值为int类型,采用二叉链存储。设计递归算法求二叉树bt中从根节点到叶子结点路径和最大的一条路径。例如,对于下图所示的二叉树,路径和最大的一条路径是5->4->6,路径和为15。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
算法设计思路:
参考数据结构和算法的部分代码,逻辑思路为先定义二叉树节点,开始时把左右子树置空,构建最大路径和代码,分别算出左右子树的路径和,以及把左右子树节点当成当前根节点,算出两者的路径和,最后比较,返回最大路径和;设置递归出口当前节点为0时,即跳出。主函数代码写出题目要求的二叉树,以及输出最后结果。
实验程序代码:
#include <iostream>
using namespace std;
struct TreeNode {// 二叉树节点的定义
int val;//val是一个把字符串转为数值的函数,它返回包含于字符串内的数字,字符串中是一个适当类型的数值。
TreeNode* ltree;//左子树
TreeNode* rtree;//右子树
TreeNode(int x) : val(x), ltree(NULL), rtree(NULL) {}//用 TreeNode node(int x)的法式把val赋值为x,left,right为NULL。
};
int maxPathSum(TreeNode* root, int& maxSum) {//记录最大路径和
if (root == NULL) return 0;// 递归出口:如果当前节点为空,则返回0
int leftSum = maxPathSum(root->ltree, maxSum);// 递归计算左子树的最大路径和
int rightSum = maxPathSum(root->rtree, maxSum);// 递归计算右子树的最大路径和
int currentSum = max(0, leftSum) + max(0, rightSum);// 计算以当前节点为根节点的路径和,包括左子树最大路径和+右子树最大路径和
maxSum = max(maxSum, currentSum); // 更新全局最大路径和
return root->val + max(0, max(leftSum, rightSum)); // 返回根节点的最大路径和(只能选择左子树或右子树的路径)
}
int maxPathSum(TreeNode* root) {
if (root == NULL) return 0; // 如果根节点为空,则返回0
int maxSum = INT_MIN; // 初始化最大路径和为负无穷
maxPathSum(root, maxSum);//有参函数
return maxSum;
}
int main() {
TreeNode* root = new TreeNode(5);//根节点值为5
root->ltree = new TreeNode(2);//左子树第一个结点值为2
root->rtree = new TreeNode(4);//右子树第一个结点值为4
root->ltree->rtree = new TreeNode(3);//左子树第一个右结点值为3
root->rtree->ltree = new TreeNode(1);//右子树第一个左结点值为1
root->rtree->rtree = new TreeNode(6);//右子树第一个右结点值为6
int maxPath = maxPathSum(root);// 求解最大路径和
cout << "二叉树的最大路径和为:" << maxPath << endl;// 输出
return 0;
}
测试截图:
5. 对于给定的含有n个元素的数组a,使用递归算法分别采用简单选择排序和冒泡排序按元素值递增排序。描述算法设计思想,编写完整的实验程序,并采用相应数据进行测试。
①算法设计思路:
根据课本64页代码思路,简单选择排序通过不断把最小元素放开头来排序,分有序、无序情况:当i=n-1时所有元素有序,即递归出口;无序时,不断比较哪个是最小元素,把最小元素放开头,先设置记录最小元素的下标,在a[i…n-1]循环体中,比较,找最小元素,主函数写输入的元素值,输出的结果。
实验程序代码:
#include<stdio.h>
void swap(int &x,int &y)//&引用变量,对应存储空间
{
int temp = x;//temp临时变量
x = y;
y = temp;
}
void disp(int a[],int n)//disp输出函数,n个元素, a[]数组a
{
int i;//定义i
for(i=0; i<n; i++) //for循环,记录i下标
printf("%d",a[i]);//打印数组
printf("\n");//转义字符,换行
}
void SelectSort(int a[],int n,int i) //递归,简单选择排序,SelectSort选择排序
{
int j,k;//定义j,k
if(i==n-1)
return;//在void函数中,无序写return,递归出口
else
{
k=i;//k记录a[i...n-1]中最小元素下标
for(j=i+1; j<n; j++) //循环体,在a[i...n-1] 中找最小元素a[k]
if(a[j]<a[k])//如果k下标的元素大于j下标的元素
k=j;//交换两者,使k记录a[i...n-1]中最小元素下标
if(k!=i) //如果k下标的元素大于i下标的元素
swap(a[i],a[k]);//交换两者的值,使k记录a[i...n-1]中最小元素下标
SelectSort(a,n,i+1);//选择排序 ,n个元素, a[]数组a,i+1个下标
}
}
int main()
{
int n=10;
int a[]= {3,2,9,6,4,1,5,7,10,8};
printf("排序前:\n");
disp(a,n);//输出数据
SelectSort(a,n,0);//0排序完成
printf("排序后:\n");
disp(a,n);
}
测试截图:
②算法设计思路:
根据课本65页思路,冒泡排序法是通过不断交换方式把最小元素放开头,处于有序时,i=n-1,即满足递归出口条件;无序时,在循环体里面,不断比较相邻元素的大小,把最小的放前面,相邻反序就交换,如果发生交换就继续递归调用,没有发生交换就跳出,主函数写输入的元素值,输出的结果。
实验程序代码:
#include<stdio.h>
void swap(int &x,int &y)//交换x、y的地址
{
int temp = x;//temp临时变量,交换x、y值
x = y;
y = temp;
}
void disp(int a[],int n)//disp输出函数,n个元素, a[]数组a
{
int i;//定义i
for(i=0;i<n;i++)//for循环,记录i下标
printf("%d",a[i]);//打印数组
printf("\n");//转义字符,换行
}
void BubbleSort(int a[],int n,int i)//递归冒泡排序
{
int j;//定义j变量
bool exchange;//bool布尔值exchange用来判断内层循环是否交换过值
if(i==n-1)//如果i等于n-1
return;//满足递归出口
else//如果i不等于n-1情况则
{
exchange=false;//置exchange为false,跳出循环
for(j=n-1;j>i;j--)//将a[i...n-1]的最小元素放到a[i]处
if(a[j]<a[j-1])//当相邻元素反序时
{
swap(a[j],a[j-1]);//交换a[j]、a[j-1],将无序区中最小元素往前移
exchange=true;//发生交换置exchange为true
}
if(exchange==false)//如果没有发生交换直接返回
return;
else//发生交换时继续递归调用
BubbleSort(a,n,i+1);
}
}
int main()
{
int n=10;//定义n,且设置n值为10
int a[]= {3,2,9,6,4,1,5,7,10,8};//输入数组a的元素值
printf("排序前:\n");//打印排序前的元素
disp(a,n);//输出数据
BubbleSort(a,n,0);//0排序完成
printf("排序后:\n");//打印排序后的元素
disp(a,n);//输出数据
}
测试截图:
如需源文件,请私信作者,无偿。