算法设计与分析(期末复习版2)

一、什么是递归

1.递归的定义

递归是指在函数的定义中又调用函数自身的方法。
递归包括两种,直接递归和间接递归。
若p函数中调用p函数,则称为直接递归;若p函数中定义q函数,q函数中又调用p函数,则称之为间接递归。
例1.1,设计求n!(n为正整数)

int fun(int n)//求n!(n为正整数) 
{
	if(n==1)
	  return(1) ;
	else
	  return(fun(n-1)*n);

}

2.何时使用递归

(1.定义是递归的。 比如一些数学公式、数列和概念的定义是递归的,例如n!和斐波那契数列等
(2.数据结构是递归的。 算法是用于数据处理的 ,有些存储数据的数据结构是递归的,例如单链表,如下例1.2
用于求一个不带头结点的单链表L的所有data域

 typedef struct Node
{
	ElemType data;
	struct Node * next;
 } LinkNode;
 int sum(LinkNode *L)
  {
  	if(L==NULL)
  	  return 0;
  	else
  	  return(L->data+sum(L->next));
  }

例1.3
分析二叉树的二叉链存储结构的递归性,设计求非空二叉链bt中所有结点值之和的递归算法,假设二叉链的data域为int型

typedef struct BNode
 {
 	int data;
 	struct BNode *lchild,*rchild;
 }BNode;
 int sum(BNode *bt)
   {
   	if(bt->lchild==NULL &&bt->rchild==NULL)
   	  return bt->data;
   	else
   	  return bt->data+sum(bt->lchild)+sum(bt->rchild);
   }

(3.问题求解方式是递归的
有些问题的解法是递归的,典型的有梵塔问题求解
设Hanoi(n,x,y,z)表示n个盘片从x通过y移动到z上,递归分解过程如图所示,其中move(n,x,z)是可以直接操作的

原创

3.递归模型

递归模型是递归算法的抽象,反应一个递归问题的递归结构,如下模型为例1.1的递归模型

f(n)=1 当n=1时 //递归出口
f(n)=nf(n-1) 当n>1时 //递归体

4.递归算法执行过程

递归算法执行过程中调用自身函数表面看是调用自身代码,可以看作是调用一个函数复制体,但实际上在调用自身函数时系统会生成一个系统栈,为每一次调用提供返回地址以及被中断的数值
(可以理解为一个栈帧,即一个栈元素),然后将栈帧入栈,继续调用知道递归出口,将数值代入计算之后,对应栈帧执行出栈过程,返回计算函数值,控制转到相应的返回地址继续执行

二、递归算法设计

1.递归与数学归纳法

2.设计步骤

递归算法的关键在于提取求解问题的递归模型,并用编程语言表示出来,每次递归都要求我呢提处理规模缩小
例1.4用递归法求一个整数数组a中的最大元素

//该代码只是主要算法,主函数部分需自写 
  int fmax(int a[],int i)
 {
 	if(i==1)
 	   return a[0];
 	else 
 	   return MAX(fmax(a,i-1),a[i-1]);
 }
 int MAX(int a,int b)
    {
    	if(a>b)
    	 return a;
    	else
    	 return b;
	}

3.递归数据结构和递归算法设计

单链表递归算法设计
例1.5有一个不带头结点的单链表L,设计一个算法删除其中所有结点值为x的结点

void Delallx(LinkNode * &L,ElemType x)
     {
     	LinkNode *p;
	 if(L->next==NULL) 
	  return;
	 if(L->next==x)
	 {
	 	p=L;
	 	L=L->next;
	 	free(p);
	 	Delallx(L,x);
	 }
	 else Delallx(L->next,x);
	 
	 }

二叉树递归算法设计
例1.6假设二叉树采用二叉链存储结构,设计一个递归算法释放二叉树bt中所有结点

 void DestroyBTree(BTNode * &bt) 
  {
  	if(bt!=NULL)
  	{
  	DestroyBTree(bt->lchild);
  	DestroyBTree(bt->rchild);
  	free(bt);
    }
  }

例1.7假设二叉树采用二叉链存储结构,设计一个递归算法由二叉树bt复制产生另一棵二叉树bt1

void CopyBTree(BNode *bt,BNode *bt1) 
   {
   	if(bt==NULL)
   	   bt1=NULL;
   	else 
	  {
		 bt1=(BTNode *)malloc(sizeof(BTNode));
		 bt1->data=bt->data;
		 CopyBTree(bt->lchild,bt1->lchild);
		 CopyBTree(bt->rchild,bt1->rchild);
	  }
   }

4.基于归纳思想的递归算法设计

三、递归算法设计实例

1.简单选择排序和冒泡排序

简单选择排序采用简单比较方式在无序区中选择最小元素并放在开头
源代码如下:

#include <stdio.h>

// 使用指针参数以交换两个整数的值
void swap(int *x, int *y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

// 输出数组中的所有元素
void disp(int a[], int n) {
    int i;
    for(i = 0; i < n; i++)
        printf("%d ", a[i]);
}

// 选择排序算法的实现
void SelectSort(int a[], int n, int i) {
    int j, k;
    if (i == n - 1) return;
    else {
        k = i;
        for (j = i + 1; j < n; j++)
            if (a[j] < a[k])
                k = j;
        // 确保k!=i以避免不必要的交换
        if (k != i)
            swap(&a[i], &a[k]);
        // 使用k作为下一轮排序的起始索引
        SelectSort(a, n, i + 1);
    }
}

int main() {
    int n = 10;
    int a[] = {2, 5, 1, 7, 10, 6, 9, 4, 3, 8};
    printf("排序前:");
    disp(a, n);
    SelectSort(a, n, 0);
    printf("排序后:");
    disp(a, n);
    return 0;
}

冒泡排序采用交换方式将无序区中的最小元素放在开头处
源代码如下

#include <stdio.h>
#include <stdbool.h>  // 包含布尔类型的定义

// 交换两个整数的值
void swap(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

// 输出数组中的所有元素
void disp(int a[], int n) {
    int i;
    for(i = 0; i < n; i++)
        printf("%d ", a[i]);
}

// 递归冒泡排序函数
void BubbleSort(int a[], int n, int i, bool exchange) {
    if (i >= n - 1) return;  // 基本情况:已经检查到最后一个元素
    for (int j = n - 1; j > i; j--) {
        if (a[j] < a[j - 1]) {
            swap(&a[j], &a[j - 1]);  // 交换元素
            exchange = true;  // 设置交换标志为 true
        }
    }
    if (exchange) {  // 如果发生了交换,则继续递归排序
        BubbleSort(a, n, i + 1, exchange);
    }
}

// 主函数
int main() {
    int n = 10;
    int a[] = {2, 5, 1, 7, 10, 6, 9, 4, 3, 8};
    printf("排序前:");
    disp(a, n);
    BubbleSort(a, n, 0, false);  // 从第一个元素开始排序
    printf("排序后:");
    disp(a, n);
    return 0;
}

2.求解n皇后问题

在n*n的棋盘上放置n个皇后,要求每个皇后不同行不同列,不同对角线
源代码如下

#include <stdio.h>
#include <stdlib.h>
#define N 20//最多皇后个数 
int q[N];
int count=0;//累计解的个数 
void dispasolution(int n)
{
	printf("   第%d个解:",++count);
	for(int i=1;i<=n;i++)
	printf("(%d,%d)",i,q[i]);
	printf("\n");
}

bool place(int i, int j)
//测试(1.J)位置能否摆放皇后//第二个皇后总是可以放置
{
if(i==1)return true;
int k=1;
while(k<i)//k-1~i-1是已放置了皇后的行
  {
  if((q[k]==j)||(abs(q[k]-j)==abs(i-k)))
  return false ;
  k++;
  }
  return true;
}
void queen(int i, int n)
{
  if(i>n)
     dispasolution(n);
  else
   {
   	for(int j=1;j<=n;j++)
	   if(place(i,j))
	   {
	   q[i]=j;
	   queen(i+1,n);
	   }
    }
}
int main(){
	int n;
	printf("皇后问题(n<20)n=");
	scanf("%d",&n);
	if(n>20)
	   
	   printf("n值太大,不能求解\n"); 
	else
	  {
	    printf("%d皇后问题求解如下:\n",n);
	   queen(1,n);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值