数据结构和算法:递归

06_递归

标签(空格分隔): 数据结构和算法


6.1 斐波那契数列的迭代实现

#include <stdio.h>

int main()
{
	int i;
	int a[40];

	a[0] = 0;
	a[1] = 1;
	printf("%d %d ", a[0], a[1] );

	for( i=2; i<40; i++ )
	{
		a[i] = a[i-1] + a[i-2];
		printf("%d ", a[i]);
	}

	return 0;
}

6.2 斐波那契数列的递归实现

#include <stdio.h>

int Fib(int i)
{
	if( i < 2 )
		return i == 0 ? 0 : 1;
	return Fib(i-1) + Fib(i-2);
}

int main()
{
	int i = 0;
	printf("请输入需要打印的斐波那契列数: "); 
	scanf("%d", &i);
	printf("%d ", Fib(i)); 
	
	return 0;
} 

6.3 递归定义

  • 在高级语言中,函数调用自己和调用其他函数并没有本质的不同。我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称作递归函数。
  • 每个递归定义必须至少有一个条件,当满足这个条件时递归不再进行,即函数调用不再自身而是返回。
  • 对比两种实现斐波那契数列的代码,迭代和递归的区别是:迭代使用的是循环结构,递归使用的是选择结构。
  • 大量的递归调用会建立函数的副本,会消耗大量的时间和内存,而迭代则不需要此种付出。
  • 递归函数分为调用和回退阶段,递归的回退顺序是它调用顺序的逆序。

计算 n 的阶乘 n!

#include <stdio.h>

int factorial(n)
{
	if( 0 == n )
		return 1;
	else
		return n*factorial(n-1);
}

int main()
{
	int n = 0;
	printf("请输入需要计算阶乘的数字 n : ");
	scanf("%d", &n);
	printf("%d\n", factorial(n));
	
	return 0; 
}

6.4 实例分析

编写一个递归函数,实现将输入的任意长度的字符串反向输出的功能。

#include <stdio.h>

void print()
{
	char a;
	scanf("%c", &a);
	if( a != '#' )
		print();
	if( a != '#' )
		printf("%c", a);
}

int main()
{
	printf("请输入字符串,并以 # 作为结束标志!\n"); 	
	print();
	
	return 0;
}

6.5 分治思想

  • 当一个问题规模较大且不易求解的时候,就可以考虑将问题分成几个小的模块,逐一解决。

折半查找法的迭代实现

//折半查找法的迭代实现 1
#include <stdio.h>
#include <stdlib.h>

int bin_serach(int str[], int n, int key)
{
    int low, mid, high;

    low = 0;
    high = n - 1;

    while(low <= high)
    {
        mid = (low + high)/2;
        if (str[mid] == key)
        {
            return mid;
        }
        else if (str[mid] < key)
        {
            low = mid + 1;
        }
        else {
            high = mid - 1;
        }
    }
    return 0;
}

int main()
{
	const int SIZE = 10;
    int c;
    int i = 0;
    int str[SIZE];
    
	printf("请输入 %d 个数字作为初始数组:\n", SIZE);
	 
	while( i < SIZE )
	{
		scanf("%d", &c);
		str[i] = c;
		i++;
	}

    int n, addr;

    printf("\n请输入要查询的数字:");
    scanf("%d", &n);

    addr = bin_serach(str, SIZE, n);

    if (-1 != addr)
    {
        printf("查找成功,查询数字 %d 所在的位置是: %d\n", n, addr+1);
    }
    else
    {
        printf("查找失败!");
    }

    return 0;
}
//折半查找法的迭代实现 2
#include <stdio.h>
#include <stdlib.h>

int getAddr(int str[], int key, int low, int high)
{
    if (low > high)
        return -1;

    int mid = (low + high) / 2;

    if (key == str[mid])
        return mid;

    if (str[mid] < key)
    {
        low = mid + 1;
    }
    else if (str[mid] > key)
    {
        high = mid - 1;
    }

    return getAddr(str, key, low, high);
}

int main()
{
	const int SIZE = 10;
    int c;
    int i = 0;
    int str[SIZE];
    
	printf("请输入 %d 个数字作为初始数组:\n", SIZE);
	 
	while( i < SIZE )
	{
		scanf("%d", &c);
		str[i] = c;
		i++;
	}

    int n, addr;

    printf("\n请输入要查询的数字:");
    scanf("%d", &n);
    
    addr = getAddr(str, n, 0, SIZE);

    if (-1 != addr)
    {
        printf("查找成功,查询数字 %d 所在的位置是: %d\n", n, addr+1);
    }
    else
    {
        printf("查找失败!");
    }

    return 0;
}

折半查找法的递归实现

//折半查找法的递归实现

#include <stdio.h>

typedef int ElemType;

int half_search(ElemType *str, int low, int high, ElemType n)
{
	int mid = (low + high)/2;

	if(low > high)
		return -1;

	if( str[mid] == n )
	{
		return mid;
	}
	else if( str[mid] < n )
	{
		return half_search(str, mid+1, high, n);
	}
	else
	{
		return half_search(str, low, mid-1, n);
	}
}

int main()
{
	const int SIZE = 10;
	ElemType str[SIZE];
	int c;
	int i = 0;

	printf("请输入 %d 个数字作为初始数组:\n", SIZE);
	 
	while( i < SIZE )
	{
		scanf("%d", &c);
		str[i] = c;
		i++;
	}

	printf("\n"); 
    for( i=0; i<SIZE; i++ )
    {
        printf("%d ", str[i]);        
    }

    int n;
	printf("\n请输入要查询的数字:");
	scanf("%d", &n);

    int addr = half_search(str, 0, SIZE, n);

    if (-1 != addr)
    {
        printf("查找成功,查询数字 %d 所在的位置是: %d\n", n, addr+1);
    }
    else
    {
        printf("查找失败!");
    }

	return 0;
}

6.6 汉诺塔

#include <stdio.h>

//将 n 个盘子从 x 借助 y 移动到 z 上
void move(int n, char x, char y, char z)
{
	if( 1==n )
	{
		printf("%c-->%c\n", x, z);
	}
	else
	{
		move(n-1, x, z, y); //将 n-1 个盘子从 x 借助 z 移动到 y 上  
		printf("%c-->%c\n", x, z); //将第 n 个盘子从 x 移动到 z 上
		move(n-1, y, x, z); //将 n-1 个盘子从 y 借助 x 移动到 z 上
	}
}

int main()
{
	int n;

	printf("请输入汉诺塔的层数: ");
	scanf("%d", &n);
	printf("移动的步骤如下:\n");
	move(n, 'x', 'y', 'z');
	
	return 0;
}

6.7 八皇后问题

#include <stdio.h>

int count = 0;

int notDanger( int row, int j, int (*chess)[8] )
{
	int i, k, flag1=0, flag2=0, flag3=0, flag4=0, flag5=0;

	//判断列方向
	for( i=0; i<8; i++ )
	{
		if( *(*(chess+i)+j) != 0 )
		{
			flag1 = 1;
			break;
		}
	}

	//判断左上方
	for( i=row, k=j; i>=0 && k>=0; i--, k-- )
	{
		if( *(*(chess+i)+k) != 0 )
		{
			flag2 = 1;
			break;
		}
	}

	//判断右下方
	for( i=row, k=j; i<8 && k<8; i++, k++ )
	{
		if( *(*(chess+i)+k) != 0 )
		{
			flag3 = 1;
			break;
		}
	}

	//判断右上方
	for( i=row, k=j; i>=0 && k<8; i--, k++ )
	{
		if( *(*(chess+i)+k) != 0 )
		{
			flag4 = 1;
			break;
		}
	}

	//判断左下方
	for( i=row, k=j; i<8 && k>=0; i++, k-- )
	{
		if( *(*(chess+i)+k) != 0 )
		{
			flag5 = 1;
			break;
		}
	}

	if( flag1 || flag2 || flag3 || flag4 ||flag5 )
	{
		return 0;
	}
	else
	{
		return 1;
	}
}



//参数row:表示起始行
//参数n:表示列数
//参数(*chess)[8]:表示指向棋盘每一行的指针
void EightQueen( int row, int n, int (*chess)[8] )
{
	int chess2[8][8], i, j;

	for( i=0; i<8; i++ )
	{
		for( j=0; j<8; j++ )
		{
			chess2[i][j] = chess[i][j];
		}
	}

	if( 8 == row ) 
	{
		printf("第 %d 种\n", count+1);
		for( i=0; i<8; i++ )
		{
			for( j=0; j<8; j++ )
			{
				printf("%d ", *(*(chess2+i)+j));
			}
			printf("\n");
		}
		printf("\n");
		count++;
	}
	else
	{
		for( j=0; j<n; j++ )
		{
			if( notDanger(row, j, chess) ) //判断是否危险
			{
				for( i=0; i<8; i++ )
				{
					*(*(chess2+row)+i) = 0;
				}
				*(*(chess2+row)+j) = 1;

				EightQueen( row+1, n, chess2 );
			}
		}
	}
}


int main()
{
	int chess[8][8], i, j;

	for( i=0; i<8; i++ )
	{
		for( j=0; j<8; j++ )
		{
			chess[i][j] = 0;
		}
	}

	EightQueen( 0, 8, chess );
	printf("总共有 %d 种解决方法\n", count);

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/** * * @author SunnyMoon */ ////////////////////////////////////////////////////////////////////////////// /** * 概念介绍: * * 消除归: * 一个算法作为一个归的方法通常从概念上很容易理解,但实际使用中归的效率不高,在这种 * 情况下,把算法转换成非归的算法是非常有用的,这种转换经常用到栈。 * * 归和栈: * 归和栈之间有着紧密的联系,大部分的编译器使用栈实现归的。 * * 调用方法的时候发生什么: * 1. 编译器会把这个方法所有当前参数及返回地址压入栈中; * 2. 将控制权交给这个方法,方法通过获得栈顶元素值访问参数; * 3. 方法运行结束的时候,值退栈,参数消失且控制权重新回到返回地址; * * 模拟归方法: * 可以将任意一个归方法转换为非归的基于栈的方法。在一些简单的情况可以完全消除栈,只 * 使用一个简单的循环,但是在很复杂的情况,算法中必须须要保留栈。本例子是简单的情况,可 * 以进一步完全消除栈。 */ ///////////////////////////////////////////////////////////////////////////// /** * 计算三角数字的问题: * 算法描述如下 * int triiangle(int n){ * if(n==1) * return 1; * else * return (n+triangle(n-1)); * } */ import java.io.*; /** * 模拟一个归方法,通用的方式消除归 */ class Params {//封装了方法的返回地址和方法的参数 public int number; public int returnAddress; public Params(int num, int returnAdd) { number = num; returnAddress = returnAdd; } } class StackX {//模拟归时使用的栈 private int maxSize; private Params[] stackArray; private int top; public StackX(int s) { maxSize = s; stackArray = new Params[maxSize]; top = -1; } public void push(Params p) { stackArray[++top] = p; } public Params pop() { return stackArray[top--]; } public Params peek() { return stackArray[top]; } } class StackTriangleApp { static int theNumber; static int theAnswer; static StackX theStack; static int logicAddress; static Params theseParams; public static void main(String[] args) throws IOException{//主方法 System.out.print("Number = "); theNumber = getInt(); stackTriangle(); System.out.println(""); System.out.println("Trriangle = " + theAnswer); } @SuppressWarnings("empty-statement") public static void stackTriangle() {//计算三角数字的方法,模拟归方法 theStack = new StackX(100); logicAddress = 1;//设置一个逻辑地址为入口地址 while (step() == false); } public static boolean step() { switch (logicAddress) { case 1: theseParams = new Params(theNumber, 6);//设定循环返回的地址 theStack.push(theseParams); logicAddress = 2; break; case 2: theseParams = theStack.peek(); if (theseParams.number == 1) { theAnswer = 1; logicAddress = 5; } else { logicAddress = 3; } break; case 3: Params newParams = new Params(theseParams.number - 1, 4); theStack.push(newParams); logicAddress = 2; break; case 4: theseParams = theStack.peek(); theAnswer = theAnswer + theseParams.number; logicAddress = 5; break; case 5: theseParams = theStack.peek(); logicAddress = theseParams.returnAddress; theStack.pop(); break; case 6: return true; } return false; } public static String getString() throws IOException{ InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = br.readLine(); return s; } public static int getInt() throws IOException{ String s=getString(); return Integer.parseInt(s); } } /** * 总结: * 当要求效率的时候可以把弟归转化为基于栈的非归,进而可以把基于栈的转化为仅有循环的 * 非归,这种情况下效率是最高的。 * 但是一些复杂的情况可以转化为基于栈的非归,但是无法消除栈的。 * 一些归的算法是非常优秀的,比如分治算法。 */

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值