数据结构与算法 | 递归和分治思想

                                                       递归

递归实际上是一种效率较为低下的算法。

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

斐波那契数列用数学函数来定义:

             0,当n=0

F(n) =   1,当n=1

             F(n-1)+F(n-2),当n>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;
}

递归

#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;
	for( i=0; i < 40; i++ )
	{
		printf("%d ", Fib(i));
	}
	
	return 0;
}

两种实现斐波那契的代码,迭代和递归的区别是:迭代使用的是循环结构,递归使用的是选择结构。

•递归函数分为调用和回退阶段,递归的回退顺序是它调用顺序的逆序。

举例,计算阶乘:计算n的阶乘n!

             1             n = 0

n! =

             n*(n-1)   n > 0

很容易设计得到:

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

实例分析:编写一个递归函数,实现将输入的任意长度的字符串反向输出的功能。例如输入字符串FishC,则输出字符串ChsiF。

思考:要将一个字符串反向地输出,首先想到的就是将字符串存放到数组再将数组元素反向输出。

但是题目要求任意长度,不用递归就会比较麻烦(或者可以用动态申请内存)。

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

                                                       分治

实例:折半查找算法的递归实现

如果从文件中读取的数据记录的关键字是有序排列的,则可以用一种效率比较高的查找方法来查找文件的记录,这就是折半查找法,又称为二分法搜索

折半查找的基本思想是:减小查找序列的长度,分而治之地进行关键字的查找。

折半查找的实现过程是:先确定待查找记录的所在范围,然后逐渐缩小这个范围,直到找到该记录或查找失败(查无该记录)为止。

用迭代方法实现如下:

#include<stdio.h>

int bin_search(int num[],int n, int key)
{
    int low,high,mid;

    low = 0;
    high = n-1;


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

}
int main()
{
    int num[10] = {1, 2, 5, 6, 8, 10, 25, 27, 30, 33};
    int result,key;
    printf("请输入待查找的关键字:");
    scanf("%d",&key);
    result = bin_search(num , 10, key);
    if(result !=-1)
    {
        printf("查找成功!关键字%d所在位置是%d",key , result);
    }
    else
    {
        printf("查找失败!");
    }
    return 0;
}

 转化为用递归的方法就很方便:

#include<stdio.h>
int bin_search(int *num,int n, int key)
{
    int result, low = 0,high = n-1;
    int mid = (low+high)/2;

    if(num[mid] == key)
    {
        return mid;
    }
    if(num[low] >= num[high])
    {
        return -1;
    }
    if(num[mid] > key)
    {
        high = mid -1;
        result = bin_search(&num[low],high-low+1, key);
    }
    if(num[mid] < key)
    {
        low = mid+1;
        result = bin_search(&num[low],high-low+1, key);
    }

    if(result != -1)
    {
        result += low;
    }
    return result;
}

int main()
{
    int num[10] = {1, 2, 5, 6, 8, 10, 25, 27, 30, 33};
    int addr,key;
    printf("请输入待查找的关键字:");
    scanf("%d",&key);

    int n = 10;

    addr = bin_search(num,n,key);
    if(addr !=-1)
    {
        printf("查找成功!关键字%d所在位置是%d",key , addr);
    }
    else
    {
        printf("查找失败!");
    }
    return 0;
}

一个经典问题——汉诺塔

一位法国数学家曾编写过一个印度的古老传说:
 

在世界中心贝拿勒斯的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。

不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。

僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。
#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);
        printf("%c-->%c\n", x, z);
        move(n-1, y, x, z);
    }
}

int main()
{
    int n;
    printf("请输入汉诺塔的层数:");
    scanf("%d",&n);
    printf("移动的步骤如下:\n");
    move(n,'X','Y','Z');
    return 0;
}

 一个经典问题——八皇后

•该问题是十九世纪著名的数学家高斯1850年提出:

–在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

用递归方法思考

#include<stdio.h>
//行、列、指针
//其中指针表示指向棋盘的每一行指针
//行为指针列是数组
//参数raw表示起始行
//参数m表示列数
int count = 0;
int isDanger(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 1;
    }
    else
    {
        return 0;
    }

}
void EightQueen(int row,int n,int (*chess)[8])
{
    //临时的一个棋盘打印当前的情况
    int chess2[8][8];
    int i,j;
    for(i=0;i<8;i++)
    {
        for(j=0;j<8;j++)
        {
            chess2[i][j] = chess[i][j];
        }
    }

    if(row == 8)
    {
        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\n");
        count++;
    }
    else
    {
        //判断位置
        for(j = 0;j<n ;j++)
        {
            if(!isDanger(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];
    int i,j;
    for(i=0;i<8;i++)
    {
        for(j=0;j<8;j++)
        {
            chess[i][j] = 0;
        }
    }
    EightQueen( 0, 8, chess);
    printf("一共有%d种方法:",count);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35924276/article/details/79349873
今日推荐