现在的位置: 首页 > 综合 > 正文

算法复习之递归算法_02

2013年01月24日 ⁄ 综合 ⁄ 共 4823字 ⁄ 字号 评论关闭

递归算法

  一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称为递归函数。递归算法是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。(说明:递归算法往往效率低下,所以能不使用时就尽量不要用,以下所论递归程序只是为了说明问题,以便于理解递归的原理。对于这些问题,其实可以使用更高效的方法来解决的)( 对于今天自己写的这篇文章,一点都不满意,因为递归算法效率往往是低下的,在花时间分析递归好像是在浪费时间。但是这么说也有点片面,因为毕竟在很多条件下,利用递归法会使用问题的分析变得简单得多。而且从递归程序到非递归程序的转换,已有成熟的形式化方法。这也许是能够安慰我的唯一理由吧 )

递归算法的特点
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以使用递归时要小心。

递归算法要求
要使用递归算法来解决的问题应该满足以下三个条件:
(1)每次调用在规模上都有所缩小(通常是减半);
(2)相邻两次重复调用之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
(3)在问题的规模很小时必须直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。

栈的作用:

  当一个运行中的函数调用另一个函数时,在运行被调函数前,系统要完成3件事:

(1)将所有的实在参数、返回地址等信息传递给被调用函数保存;

(2)为被调用函数的局部变量分配存储区;

(3)将控制转移到被调函数的入口。

  同样地从被调函数返回调用函数之前,系统也要先完成3件事:

(1)保存被调用函数的计算结果;

(2)释放被调函数的数据区;

(3)按照被调函数保存的返回地址将控制转移到调用函数。

  当有多个函数构成嵌套调用时,按照后调用先返回的原则,函数之间的信息传递和控制转移必须通过栈来实现。系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放它的存储区,当前正在运行的函数的数据区必须在栈顶。递归函数的运行过程类似于多个函数的嵌套调用,只是调用函数和被调用函数是同一个函数。

递归算法应用实例:
1. 十进制转换成二进制的递归实现

C语言实现:

#include<stdio.h>

void conversion(int num)//十进制转换成二进制的函数,递归函数
{
	if(num/2 > 0)
	{
		conversion(num/2);
		printf("%d", num%2);
	}
	else
	{
		printf("%d", num);
	}
}

void main()
{
	int num, temp;
	printf("请输入一个十进制数:");
	scanf("%d", &num);

	if(num < 0)
	{
		temp = -num;
		printf("\n%d 转换成二进制数后的值为: -", num);
	}
	else
	{
		temp = num;
		printf("\n%d 转换成二进制数后的值为: ", num);
	}
	
	conversion(temp);
	printf("\n\n");
}

下面对这个程序的功能进行简单的扩充,使它能够将十进制数转换成16进制以内的数:

#include<stdio.h>

void conversion(int num, int n)
{
	int pt;
	if(num/n > 0)
	{
		conversion(num/n, n);
		pt = num%n;
		switch(pt)  
        {  
            case 15:printf("F");break;  
            case 14:printf("E");break;  
            case 13:printf("D");break;  
            case 12:printf("C");break;  
            case 11:printf("B");break;  
            case 10:printf("A");break;  
            default:printf("%d", pt);  
        }
	}
	else
	{
		printf("%d", num);
	}
}
void main()
{
	int num, n, temp;
	printf("请输入一个十进制数:");
	scanf("%d", &num);
	printf("\n您想把它转换成几进制数(最高为16进制),输入:");
	scanf("%d", &n);
	if(num < 0)
	{
		temp = -num;
		printf("\n%d 转换成 %d 进制数后的值为: -", num, n);
	}
	else
	{
		temp = num;
		printf("\n%d 转换成 %d 进制数后的值为: ", num, n);
	}
	
	conversion(temp, n);
	printf("\n\n");
}

十进制转换成二进制的递归实现(Java版)

import java.util.Scanner;

public class Conversion {
	public static void main(String[] args){
		int temp;
		Scanner input = new Scanner(System.in);
		System.out.print("请输入一个十进制数:");
		int num = input.nextInt();
		System.out.print("\n您想把它转换成几进制数(最高为16进制),输入:");
		int n = input.nextInt();
		if(num < 0){
			temp = -num;
			System.out.print("\n" + num + "转换成 " + n + "进制数后的值为: -");
		}else{
			temp = num;
			System.out.print("\n" + num + "转换成 " + n + "进制数后的值为: ");
		}
		conversion(temp, n);
		System.out.println("\n");
	}
	static void conversion(int num, int n){
		if(num/n > 0){
			conversion(num/n, n);
			int pt = num % n;
			switch(pt){
            case 15:System.out.print("F");break;  
            case 14:System.out.print("E");break;  
            case 13:System.out.print("D");break;  
            case 12:System.out.print("C");break;  
            case 11:System.out.print("B");break;  
            case 10:System.out.print("A");break;  
            default:System.out.print(pt);  
			}
		}else{
			System.out.print(num);
		}
	}
}

2. 最大公约数(Greatest common divisor)

(1)最大公约数的非递归实现(C语言版)

#include<stdio.h>
void main()//求整数m,n的最大公约数
{
	int m, n, temp;
	printf("请输入两个整数:");
	scanf("%d,%d", &m, &n);
	if(m < n)
	{
		temp = m;
		m = n;
		n = temp;
	}
	while((temp = m % n) != 0)//利用辗转相除法求m,n的最大公约数
	{
		m = n;
		n = temp;
	}
	printf("\n所求两数的最大公约数为: %d\n\n", n);
}

(2)下面让我们来利用递归的方法再来求整数m,n的最大公约数,体验一下递归算法在此的应用

C语言版

#include<stdio.h>

int gcd(int m, int n)//递归函数,用于求整数m,n的最大公约数
{
	int temp = m % n;
	if(temp != 0)
	{
		return gcd(n, temp);
	}
	else//当求到最大公约数,才返回n
	{
		return n;
	}
}

void main()
{
	int m, n, temp;
	printf("请输入两个整数:");
	scanf("%d,%d", &m, &n);
	if(m < n)
	{
		temp = m;
		m = n;
		n = temp;
	}
	temp = gcd(m, n);
	printf("\n所求两数的最大公约数为: %d\n\n", temp);
}

求最大公约数(Java版)

import java.util.Scanner;

public class Gcd{
	public static void main(String[] args){
		Scanner input = new Scanner(System.in);
		System.out.print("请输入第一个整数:");
		int m = input.nextInt();
		System.out.print("请输入第二个整数:");
		int n = input.nextInt();
		if(m < n){
			int temp = m;
			m = n;
			n = temp;
		}
		System.out.println("所求最大公约数为: " + gcd(m, n));
	}
	static int gcd(int m, int n){
		int temp = m % n;
		if(temp != 0){
			return gcd(n, temp);
		}
		else
		{
			return n;
		}
	}
}

3. 在n个元素中找出最大元素和最小元素

(1)采用直接比较法找出最大最小数(C语言版)

#include<stdio.h>

void maxminNum(int arr[], int n, int &max, int &min)//采用直接比较法找出最大最小数
{
	int i;
	max = min = arr[0];
	for(i = 2; i < n; i++)
	{
		if(arr[i] > max)
		{
			max = arr[i];
		}
		if(arr[i] < min)
		{
			min = arr[i];
		}
	}
}

void main()
{
	int max, min;
	int arr[12] = {10, 12, 32, 8, 45, 15, 36, 24, 42, 28, 81, 56};
	maxminNum(arr, 12, max, min);
	printf("最大数为: %d 最小数为: %d\n\n", max, min);
}

对于长度为 n 的序列, 此种算法的时间复杂度为O(n)

(2)采用分治的思想, 如果数组大小为 1 则可以直接给出结果,如果大小为 2则一次比较即可得出结果,于是得出到求解该问题的子问题即: 数组大小 <= 2。到此我们就可以进行分治运算了,只要求解的问题数组长度比 2 大就继续分治,否则求解子问题的解并更新全局解。(当然,对于此题用递归来实现,效率低下,但是本人只是为了说明如何使用递归的问题)

#include<stdio.h>

void maxminNum(int s, int e, int *pt, int &max, int &min)
{
	/*
	* s 当前递归(分治)段的开始下标,e 当前递归(分治)段的结束下标,pt 数组的地址,
	* max 存储当前搜索到的最大值,min 存储当前搜索到的最小值
	*/
	int i;
	if(e - s <= 1)//
	{
		if(pt[s] > pt[e])
		{
			if(pt[s] > max)max = pt[s];
			if(pt[e] < min)min = pt[e];
		}
		else
		{
			if(pt[e] > max)max = pt[e];
			if(pt[s] < min)min = pt[s];
		}
		return;
	}
	else/* 不是子问题继续分治 */
	{
		i = s + (e - s)/2;
		maxminNum(s, i, pt, max, min);
		maxminNum(i + 1, e, pt, max, min);
	}
}

void main()
{
	int max, min;
	int arr[12] = {88, 12, 32, 8, 45, 15, -21, 24, 42, 28, 81, -99};
	max = min = arr[0];
	maxminNum(0, 12, arr, max, min);/* 递归(分治)法获取最值 */
	printf("最大数为: %d 最小数为: %d\n\n", max, min);
}

抱歉!评论已关闭.