从数据结构栈到递归算法详解

 

在计算机科学中,数据结构是一种组织和存储数据的方式,它为算法提供了基础。在众多经典的数据结构中,栈(Stack)以其独特的特性和广泛的应用而备受关注。本文将深入探讨栈的概念、特性以及在实际问题中的应用。

栈的概念

栈是一种线性数据结构,它具有后进先出(Last In, First Out,LIFO)的特性。简而言之,最后进栈的元素将首先出栈,而最早进栈的元素将最后出栈。这种特性使得栈在很多场景中都具有独特的用途。栈可以比喻成一个堆叠的盘子,你只能从最上面取走或添加盘子。

栈的操作

栈支持两种基本操作:压入(Push)和弹出(Pop)。

  • Push: 将一个元素添加到栈的顶部,栈顶指针上移。
  • Pop: 从栈顶移除一个元素,栈顶指针下移。

此外,栈还有一个很重要的操作:查看栈顶元素(Peek)。Peek 操作允许你查看栈顶元素,但不会移除它。

栈的实现

栈可以使用多种方式实现,其中两种常见的方式是使用数组(Array)和链表(Linked List)。

  • 数组实现: 利用数组的索引来表示栈顶指针,元素压入时栈顶指针加一,元素弹出时栈顶指针减一。这种方式效率高,但需要提前确定栈的最大容量。

class ArrayStack{
    private int max;
    private int[] stack;
    private int top = -1;
    public ArrayStack(int max){
        this.max = max;
        stack = new int[max];
    }
    public boolean isFull(){
        return top == max-1;
    }
    public boolean isEmpty(){
        return top==-1;
    }
    public void push(int value){
        if(isFull()){
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top] = value;
    }
    public int pop(){
        if(isEmpty()){
            throw new RuntimeException("栈空");
        }
        return stack[top--];
    }
    public void list(){
        if(isEmpty()){
            System.out.println("为空不能遍历");
            return;
        }
        for (int i = top; i >= 0 ; i--) {
            System.out.println(stack[i]);
        }
    }
}
  • 链表实现: 使用链表的头节点表示栈顶,每次元素压入或弹出时修改链表头指针。链表实现不需要事先确定容量,但可能会稍微降低一些性能。

栈的应用

栈在编程中有广泛的应用,以下是一些常见的应用场景:

  1. 函数调用和递归: 编程语言使用栈来跟踪函数的调用和返回。每次调用函数时,当前函数的状态(例如局部变量、返回地址等)都被压入栈中,函数返回时再从栈中弹出。

  2. 表达式求值: 栈可以用于计算表达式的值,特别是中缀表达式转换为后缀表达式后的求值。

  3. 括号匹配: 栈可以检查代码中的括号是否匹配,例如圆括号、方括号和花括号。

  4. 浏览器的前进后退功能: 浏览器使用栈来管理浏览历史记录,用户点击后退按钮时,前一个页面的状态从栈中弹出。

  5. 迷宫求解: 在图论中,栈可以用于求解迷宫等问题,通过回溯法在搜索中保存状态。

迷宫求解问题

     通过二维数组定义围墙,定义起始位置定义终止位置,计算出路径

这里以[1][1]为起点,以[7][7]为终点。在开始之前我们要先确认一个整体的策略,让程序跟着策略走,不同的策略会产生不同的路线,每一步就是上下左右这四个选择,所以一共4*3*2*1=24种策略。这里的策略是向下-->-向右->向上-->向左(策略体现在path方法中)

  public static void main(String[] args) {
        int[][] map = new int[8][8];
        map[2][1] = 1;
        map[2][2] = 1;
        for (int i = 0; i < 8; i++) {
            map[0][i] = 1;
            map[i][0] = 1;
            map[i][7] = 1;
            map[7][i] = 1;
        }
        path(map,1,1);
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                System.out.print(map[i][j]+" ");
            }
            System.out.println();
        }
    }

首先定义地图,为8*8方阵,四周是围墙,第三行有多余的障碍物,用0表示为走过的方格,用1表示围墙,用2表示走过行的通的路,用3表示行不通的方格

定义一个path方法用来判断当前方格是否能走:

 public static boolean path(int[][] map, int i, int j){
        if(map[6][6] == 2){
            return true;
        }else {
            if(map[i][j] == 0){
                map[i][j] = 2;
                if(path(map,i+1,j)){
                    return true;
                } else if (path(map,i,j+1)){
                    return true;
                } else if (path(map,i-1,j)) {
                    return true;
                } else if (path(map,i,j-1)) {
                    return true;
                }else {
                    map[i][j] = 3;
                    return false;
                }
            }else {
                return false;
            }

        }
    }

这里运用了递归回溯递归时当第一次调用path方法时该方法会被放在jvm虚拟机中的栈空间中,包括变量都会放在这个空间,对于每个空间中的n变量名一样却毫无关系,但对于map这种数组引用类型来说指向的都是用一个数组地址,等于共同操做一个数组,每一次递归调用path方法,都会生成新的path方法入栈,直至执行return语句,从栈顶一直return到栈底。回溯主要体现在if-else if - else if这里,当不符合条件便会return会上一步,在接着判断在接着return一致重复上述过程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值