第三章 递归

今天看了算法图解第三章递归 写一下收货 省着以后忘

递归算法

递归就是不断的调用自己

递归和循环的关系

如果使用循环,程序效率会更高,如果使用递归,程序可能更容易被理解

递归的基线条件和递归条件
1.递归条件:自己调用自己
2.基线条件:如何让自己停下来

push(Eitem):元素入栈

// push方法 元素入栈
public E push(E item) 
{
    addElement(item);
    return item;
}

public synchronized void addElement(E obj) 
{
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
}

pop():取出栈顶元素,并且将其从栈中移除

public synchronized E pop() 
{
    E    obj;
    int  len = size();
    obj = peek();
    removeElementAt(len - 1);
    return obj;
}

peek():取出栈顶元素,但是不从栈中移除元素

public synchronized E peek() 
{
    // 数组长度
    int len = size();
    if (len == 0)
        throw new EmptyStackException();
    // 取最后一个元素
    return elementAt(len - 1);
}

empty():判断栈是否为空,为空返回true,否则返回false

public boolean empty() 
{
    // 写的挺美不是
    return size() == 0;
}

search(Objecto):在栈中查找元素位置,位置从栈顶开始往下算,栈顶为1,

public synchronized int search(Object o) 
{
    int i = lastIndexOf(o);
    if (i >= 0) 
    {
        return size() - i;
    }
    return -1;
}
// 底层用的循环 写的真美
public synchronized int lastIndexOf(Object o, int index) 
{
    if (index >= elementCount)
        throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
    if (o == null) 
    {
        for (int i = index; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } 
    else 
    {
        for (int i = index; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

栈:先进后出 ,计算机内部是如何使用调用栈的

public static void main(String[] args)
{
    int i = 1;
    i = i++;
    int j = i++;
    int k = i + ++i * i++;
}

1、int i=1;分两步:第一步,操作数栈中放1,第二步赋值操作,把操作数栈中的1赋值给局部变量表中的位置1的变量i,同时操作数栈中的1消除
2、i++;也分两步,第一步,先把局部变量表中i的值取出放入操作数栈中的栈顶,第二步,把局部变量表中的i为1的值自增1,变成2,这需要记住。也就是自增、自减操作都是直接修改变量的值,不经过操作数栈。
所以 i = i++ 后 i的值还是1 同理 j 也是 1
3、这里我简化了 因为j=i++的时候 局部变量表里的情况为 j = 1; i = 2
4、K的计算 i = 2 ,2压栈 ,++i ,局部变量自增后压栈 局部变量表 i = 3,栈为 2,3 , 在计算 i++,局部变量表自增,3 压入栈,局部变量里现在为 j = 1,i = 4 , 栈中 2,3,3现在计算K = 2 + 3*3 = 11

思考的问题:
自己手动实现一个栈1
自己手动实现一个栈2
栈数据结构能做什么

递归的问题

1.递归写错了会造成栈内存溢出
2.如果效率低 考虑尾部递归
正常的递归

public static int recursive(int n)
{
    return (n == 1) ? 1 : n * recursive (n - 1);
}

recursive(5)
{5 * recursive(4)}
{5 * {4 * recursive(3)}}
{5 * {4 * {3 * recursive(2)}}}
{5 * {4 * {3 * {2 * recursive(1)}}}}
{5 * {4 * {3 * {2 * 1}}}}
{5 * {4 * {3 * 2}}}
{5 * {4 * 6}}
{5 * 24}
120
尾部递归

public static int tailRescuvie(int n, int a)
{
    return (n == 1) ? a : tailRescuvie(n - 1, a * n);
}

public static int tailRescuvie(int n) 
{
    return (n == 0) ? 1 : tailRescuvie(n, 1);
}

tailRescuvie(5)
tailRescuvie(5, 1)
tailRescuvie(4, 5)
tailRescuvie(3, 20)
tailRescuvie(2, 60)
tailRescuvie(1, 120)

很容易看出, 普通的线性递归比尾递归更加消耗资源, 在实现上说, 每次重复的过程
调用都使得调用链条不断加长. 系统不得不使用栈进行数据保存和恢复.而尾递归就
不存在这样的问题, 因为他的状态完全由n和a保存

原理:
当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值