数据结构与算法之递归算法

1 递归介绍

1.1 原理

递归算法是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。

一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).

1.2 特点

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

1.3 递归算法所体现的“重复”一般有三个要求

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

2 算法分类解析

2.1 二分法查找算法

2.1.1 思想

(1)二分查找又称折半查找,当数据量很大适宜采用该方法。采用二分法查找时,数据必须采用顺序存储结构按关键字大小有序排列好的。优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。

(2)核心思想是:(设查找的数组区间为array[low, high]),确定该区间的中间位置K(2)将查找的值T与array[k]比较。若相等,查找成功返回此位置;否则确定新的查找区域,继续二分查找。

2.1.2 演示

(1)时间复杂度
1.最坏情况查找最后一个元素(或者第一个元素)Master定理T(n)=T(n/2)+O(1)所以T(n)=O(logn)
2.最好情况查找中间元素O(1)查找的元素即为中间元素(奇数长度数列的正中间,偶数长度数列的中间靠左的元素)
(2)空间复杂度:S(n)=n

2.1.3 源码

package com.dn.search;

import com.dn.sort.BasicSort;

/**
 * 二分法查找
 * @author chenliguan
 *
 */
public class BinarySearch {

    /**
     * 递归的方式
     * @param elem
     * @param array
     * @param low
     * @param high
     * @return
     */
    public int binarySearch(int elem,int [] array,int low,int high){
        if(low>high){
            return -1;
        }
        int middle = (low+high)/2;
        if(array[middle] == elem){
            System.out.println("找到对应元素值,下标为:"+middle);
            return middle;
        }
        if(array[middle]<elem){
            //找右边
            return binarySearch(elem, array, middle+1, high);
        }
        if(array[middle]>elem){
            //找左边
            return binarySearch(elem, array, low, middle-1);
        }
        return -1;
    }

    /**
     * 非递归
     * @param args
     */
    public int directBinarySearch(int[] array,int elem){
        int low = 0;
        int high = array.length-1;
        while(low<=high){
            int middle = (low+high)/2;
            if(elem>array[middle]){
                //右边找
                low = middle+1;
            }else  if(elem<array[middle]){
                high = middle - 1;
            }else{
                System.out.println("找到相应元素,下标为:"+middle);
                return middle;
            }
        }
        return -1;
    }


    public static void main(String[] args){
        BinarySearch binarySearch = new BinarySearch();
        int [] array = {10,23,4,3,2,5,1,2,623,92,23,23,234,2,34,234,234,2,10};
        BasicSort basicSort = new BasicSort();
        //通过基数排序
        basicSort.basicSort(array);
        for(int n:array){
            System.out.print(" "+n);
        }
        //递归的方式查找
//      binarySearch.binarySearch(5, array, 0, array.length-1);
        //非递归查找
        binarySearch.directBinarySearch(array, 2);
    }
}

2.2 汉诺塔算法

2.2.1 核心思想

从左到右 A B C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面.

2.2.2 演示图

(1)分析
这里写图片描述

(2)2个盘子的思路
这里写图片描述

(3)演变过程
这里写图片描述

2.2.3 源码

package com.dn.recursion;

/**
 * 汉诺塔
 * 
 * 从左到右 A  B  C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面.
 * @author chenliguan
 *
 */
public class HanNota {
    private int i = 1;

    public void hanNota(int n, char from, char dependOn, char to) {// A B C 
        if (n == 1) {
            move(1, from, to);
        } else {
            hanNota(n - 1, from, to, dependOn);// 第一步,先将n-1个盘子从A利用C挪到B
            move(n, from, to);// 将第n这个盘子(底盘)从A挪到C
            hanNota(n - 1, dependOn, from, to);// 将n-1个盘子从B利用A挪到C
        }
    }

    private void move(int n, char from, char to) {
        System.out.println("第" + i++ + "步从" + from + "------>" + to);//打印
    }

    public static void main(String[] args) {
        HanNota hanNota = new HanNota();
        hanNota.hanNota(3, 'A', 'B', 'C');
    }
}

2.3 欧几里得算法

2.3.1 核心思想

定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。最大公约数(greatest common divisor)缩写为gcd。

2.3.2 演示图

(1)证明
这里写图片描述

2.3.3 源码

package com.dn.recursion;

/**
 * 欧几里得算法
 * 
 * 定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。最大公约数(greatest common divisor)缩写为gcd。
 *
 * gcd(a,b) = gcd(b,a mod b) (不妨设a>b 且r=a mod b ,r不为0)
 *
 */
public class Gcd {
    // (m>n)m和n的最大公约数 = n 和m%n的最大公约数
    // 36 24 = 12   =    24和12 = 12和 0(24%12)

    public int gcd(int m, int n) {
        if (n == 0) {
            return m;
        } else {
            return gcd(n, m % n);
        }
    }

    public static void main(String[] args) {
        Gcd gcd = new Gcd();
        int x = gcd.gcd(10, 5);
        System.out.println("x:" + x);
    }
}

2.4 阶乘求解算法

2.4.1 源码

package com.dn.recursion;

/**
 * 阶乘
 */
public class CalNFact {
    public int f(int n) {
        if (n == 1) {
            return n;
        } else {
            return n * f(n - 1);
        }
    }

    public static void main(String[] args) {
        CalNFact calNFact = new CalNFact();
        int n = calNFact.f(15);
        System.out.println(15 + "的阶乘为:" + n);
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值