算法设计与分析:实验一 递归算法设计技术

一、实验目的

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);//输出数据

}

测试截图:

如需源文件,请私信作者,无偿。

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法设计与分析 作者-王红梅 出版社-清华大学出版社 出版日期-07 1 2006. 共262页 目录 第 1 章 绪论 1 .1 算法的基本概念 1 . 1 . 1 为什么要学习算法 1 . 1 . 2 算法及其重要特性 1 . 1 . 3 算法的描述方法 1 . 1 . 4 算法设计的一般过程 1 . 1 . 5 重要的问题类型 1 .2 算法分析 1 . 2 . 1 渐进符号 1 . 2 . 2 最好、 最坏和平均情况 1 . 2 . 3 非递归算法的分析 1 . 2 . 4 递归算法的分析 1 . 2 . 5 算法的后验分析 1 .3 实验项目— — —求最大公约数 阅读材料— — —人工神经网络与 BP 算法 习题 1 第 2 章 NP 完全理论 2 .1 下界 2 . 1 . 1 平凡下界 2 . 1 . 2 判定树模型 2 . 1 . 3 最优算法 2 .2 算法的极限 2 . 2 . 1 易解问题与难解问题 2 . 2 . 2 实际问题难以求解的原因 2 . 2 . 3 不可解问题 2 .3 P 类问题和 NP 类问题 2 .3 .1 判定问题 2 .3 .2 确定性算法与 P 类问题 2 .3 .3 非确定性算法与 NP 类问题 2 .4 NP 完全问题 2 .4 .1 问题变换与计算复杂性归约 2 .4 .2 NP 完全问题的定义 2 .4 .3 基本的 NP 完全问题 2 .4 .4 NP 完全问题的计算机处理 2 .5 实验项目— — —SAT 问题 阅读材料— — —遗传算法 习题 2 第 3 章 蛮力法 3 .1 蛮力法的设计思想 3 .2 查找问题中的蛮力法 3 .2 .1 顺序查找 3 .2 .2 串匹配问题 3 .3 排序问题中的蛮力法 3 .3 .1 选择排序 3 .3 .2 起泡排序 3 .4 组合问题中的蛮力法 3 .4 .1 生成排列对象 3 .4 .2 生成子集 3 .4 .3 0 / 1 背包问题 3 .4 .4 任务分配问题 3 .5 图问题中的蛮力法 3 .5 .1 哈密顿回路问题 3 .5 .2 TSP 问题 3 .6 几何问题中的蛮力法 3 .6 .1 最近对问题 3 .6 .2 凸包问题 3 .7 实验项目— — —串匹配问题 阅读材料— — —蚁群算法 习题 3 第 4 章 分治法 4 .1 概述 4 .1 .1 分治法的设计思想 4 .1 .2 分治法的求解过程 4 .2 递归 4 .2 .1 递归的定义 4 .2 .2 递归函数的运行轨迹 4 .2 .3 递归函数的内部执行过程 4 .3 排序问题中的分治法 4 .3 .1 归并排序 4 .3 .2 快速排序 4 .4 组合问题中的分治法 4 .4 .1 最大子段和问题 4 .4 .2 棋盘覆盖问题 4 .4 .3 循环赛日程安排问题 4 .5 几何问题中的分治法 4 .5 .1 最近对问题 4 .5 .2 凸包问题 4 .6 实验项目— — —最近对问题 阅读材料— — —鱼群算法 习题 4 第 5 章 减治法 5 .1 减治法的设计思想 5 .2 查找问题中的减治法 5 .3 排序问题中的减治法 5 .4 组合问题中的减治法 5 .5 实验项目— — —8 枚硬币问题 阅读材料— — —粒子群算法 习题 5 第 6 章 动态规划法 6 .1 概述 6 .2 图问题中的动态规划法 6 .3 组合问题中的动态规划法 6 .4 查找问题中的动态规划法 6 .5 实验项目— — —最大子段和问题 阅读材料— — —文化算法 习题 6 第 7 章 贪心法 7 .1 概述 7 .2 图问题中的贪心法 7 .3 组合问题中的贪心法 7 .4 实验项目— — —霍夫曼编码 阅读材料— — —模拟退火算法 习题 7 第 8 章 回溯法 8 .1 概述 8 .2 图问题中的回溯法 8 .3 组合问题中的回溯法 8 .4 实验项目— — —0/ 1 背包问题 阅读材料— — —禁忌搜索算法 习题 8 第 9 章 分支限界法 9 .1 概述 9 .2 图问题中的分支限界法 9 .3 组合问题中的分支限界法 9 .4 实验项目— — —电路布线问题 阅读材料— — —免疫算法 习题 9 第 10 章 概率算法 10 .1 概述 10 .1 .1 概率算法的设计思想 10 .1 .2 随机数发生器 10 .2 舍伍德(Sherwood)型概率算法 10 .2 .1 快速排序 10 .2 .2 选择问题 10 .3 拉斯维加斯( Las Vegas)型概率算法 10 .3 .1 八皇后问题 10 .3 .2 整数因子分解问题 10 .4 蒙特卡罗(Monte Ca rlo)型概率算法 10 .4 .1 主元素问题 10 .4 .2 素数测试问题 10 .5 实验项目— — —随机数发生器 阅读材料— — —DNA 计算与 DNA 计算机 习题 10 第 11 章 近似算法 11 .1 概述 11 .1 .1 近似算法的设计思想 11 .1 .2 近似算法的性能 11 .2 图问题中的近似算法 11 .2 .1 顶点覆盖问题 11 .2 .2 TSP 问题 11 .3 组合问题中的近似算法 11 .3 .1 装箱问题 11 .3 .2 子集和问题 11 .4 实验项目— — —TSP 问题的近似算法 阅读材料— — —量子密码技术 习题 11 第 12 章 计算复杂性理论 12 .1 计算模型 12 .1 .1 图灵机的基本模型 12 .1 .2 k 带图灵机和时间复杂性 12 .1 .3 离线图灵机和空间复杂性 12 .2 P 类问题和 NP 类问题 12 .2 .1 非确定性图灵机 12 .2 .2 P 类语言和 NP 类语言 12 .3 NP 完全问题 12 .3 .1 多项式时间变换 12 .3 .2 Cook 定理 12 .4 实验项目— — —NP 完全问题树 阅读材料— — —算法优化策略

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值