栈的定义

栈可以定义为只允许在表的末端进行插入和删除的线性表。允许插入的一端称为栈顶,而不允许插入和删除的一端叫做栈底。栈有两种典型的存储表示,基于数组的存储表示和基于链表的存储表示。基于数组的存储表示实现的栈称为顺序栈,基于链表的存储表示实现的栈称为链式栈。

栈的应用

  1. 括号匹配
public void printMatchPairs(String expression) {
        Stack<Integer> stack = new Stack<>();
        int length = expression.length();
        int j;
        for (int i = 0; i < length; i++) {
            // 左括号位置进栈
            if (expression.charAt(i) == '(') {
                stack.push(i);
            }
            if (expression.charAt(i) == ')') {
                if (!stack.isEmpty()) {
                    // 退栈成功,匹配成功
                    j = stack.pop();
                    System.out.println(i + "与" + j + "匹配");
                } else {
                    System.out.println("没有与" + i + "匹配的左括号");
                }
            }
        }
        // 栈中还有左括号
        while (!stack.isEmpty()) {
            j = stack.pop();
            System.out.println("没有与" + j + "匹配的右括号");
        }
    }
  1. 表达式的计算
/**
     * 计算后缀表达式的值
     *
     * @param expression 后缀表达式
     * @return 计算结果
     */
    public int calculate(String expression) {
        int length = expression.length();
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < length; i++) {
            char character = expression.charAt(i);
            // 如果式操作数则入栈
            if (Character.isDigit(character)) {
                stack.push(Character.getNumericValue(character));
            } else {
                // 如果是操作符,则退出两个操作数
                // 计算结果后再入栈
                if (stack.size() >= 2) {
                    int rightOperator = stack.pop();
                    int leftOperator = stack.pop();
                    switch (character) {
                        case '+':
                            stack.push(leftOperator + rightOperator);
                            break;
                        case '-':
                            stack.push(leftOperator - rightOperator);
                            break;
                        case '*':
                            stack.push(leftOperator * rightOperator);
                            break;
                        case '/':
                            if (rightOperator == 0) {
                                throw new IllegalArgumentException("divided 0");
                            }
                            stack.push(leftOperator / rightOperator);
                            break;
                    }
                }
            }
        }
        if (!stack.isEmpty()) {
            return stack.pop();
        }
        return 0;
    }
  1. 中缀表达式转后缀表达式
public String postFix(String expression) {
        // 中缀表达式转后缀表达式,需要考虑操作符的
        // 栈内和栈外的优先级
        Map<Character, Integer> ispMap = new HashMap<>();
        ispMap.put('#', 0);
        ispMap.put('(', 1);
        ispMap.put('*', 5);
        ispMap.put('/', 5);
        ispMap.put('%', 5);
        ispMap.put('+', 3);
        ispMap.put('-', 3);
        ispMap.put(')', 6);

        Map<Character, Integer> icpMap = new HashMap<>();
        icpMap.put('#', 0);
        icpMap.put('(', 6);
        icpMap.put('*', 4);
        icpMap.put('/', 4);
        icpMap.put('%', 4);
        icpMap.put('+', 2);
        icpMap.put('-', 2);
        icpMap.put(')', 1);

        Stack<Character> stack = new Stack<>();
        StringBuilder stringBuilder = new StringBuilder();
        stack.push('#');
        int i = 0;
        while (!stack.isEmpty()) {
            char character = expression.charAt(i);
            // 如果是操作数,直接输出
            if (Character.isAlphabetic(character) && Character.isDigit(character)) {
                stringBuilder.append(character);
                i++;
            } else {
                char top = stack.peek();
                // 新输入的操作符优先级高,则入栈
                if (ispMap.get(top) < icpMap.get(character)) {
                    stack.push(character);
                    i++;
                } else if (ispMap.get(top) > icpMap.get(character)) {
                    // 新输入操作符优先级低,则出栈
                    stringBuilder.append(stack.pop());
                } else {
                    if (stack.pop() == '(') {
                        i++;
                    }
                }
            }
        }
        return stringBuilder.toString();
    }

栈与递归

若一个对象部分的包含它自己,或用它自己给自己定义,则称这个对象是递归的;而且如果一个过程直接或者间接地调用自己,则称这个过程是递归的。
对于一个比较复杂的问题,如果能够分解成几个相对简单的且解法相同或类似的子问题时,只要解决了这些子问题,那么原问题就迎刃而解了。当分解后的子问题可以直接解决时,就停止分解。递归定义的函数可以用递归过程来编程求解。

  1. 数据结构是递归的。
    比方说链表这中数据结构的定义就是递归的。
  2. 问题解法是递归的。
    用递归来求解汉诺塔问题。
public void hanoi(int n, String A, String B, String C) {
        // 如果只有一个盘子
        // 直接从A柱移动到C柱
        if (n == 1) {
            System.out.println("Move top disk from " + A + " to " + C);
        } else {
            // 借助C柱,将A柱上的上面
            // n-1个盘子移动到B柱
            hanoi(n - 1, A, C, B);
            System.out.println("Move top disk from " + A + " to " + C);
            // 借助A柱,将B柱上的
            // n-1个盘子移动到C柱
            hanoi(n - 1, B, A, C);
        }
    }
  1. 递归与递归工作栈。
    外部调用结束后,将返回调用递归过程的主函数。内部调用结束后,将返回到递归过程本地调用语句的后继语句处。在每进入一层递归时,系统就要建立一个新的工作记录,包括返回地址、实参的副本、本层的局部变量,并加入到递归工作栈的栈顶。每退出一层递归,就从递归工作栈中退出一个工作记录。
  2. 用栈实现递归过程的非递归算法。
class Node {
        public long n;
        public int tag;
        public Node(long n, int tag) {
            this.n = n;
            this.tag = tag;
        }
}
public long fibnacci(long n) {
        Stack<Node> stack = new Stack<>();
        long sum = 0;
        do {
            while (n > 1) {
                Node node = new Node(n, 1);
                stack.push(node);
                n--;
            }
            sum = n + sum;
            while (!stack.isEmpty()) {
                Node top = stack.pop();
                if (top.tag == 1) {
                    // 向右递归
                    top.tag = 2;
                    stack.push(top);
                    n = top.n - 2;
                    break;
                }
            }
        } while (!stack.isEmpty());
        return sum;
    }
  1. 用迭代实现递归过程。
public long fibIter(long n) {
        if (n <= 1) {
            return n;
        }
        long twoBack = 0;
        long oneBack = 1;
        long current = 0;
        for (int i = 2; i <= n; i++) {
            current = oneBack + twoBack;
            twoBack = oneBack;
            oneBack = current;
        }
        return current;
    }

一般对于尾递归或单向递归的情形,都可以利用迭代的方法,将递归过程改为非递归过程。

用回溯法求解迷宫问题

回溯法也称试探法。用回溯法求解问题时常常使用递归方法进行试探,或使用栈帮助向前试探和回溯。

public class Maze {
    class Move {
        public int xOffset;
        public int yOffset;
        public String dir;
        public int dirNum;
        
        public Move(int xOffset, int yOffset, String dir, int dirNum) {
            this.xOffset = xOffset;
            this.yOffset = yOffset;
            this.dir = dir;
            this.dirNum = dirNum;
        }
    }
    
    class Item {
        public int x;
        public int y;
        public int dir;
    }
    
    private int[][] maze;
    private int[][] mark;
    private int m;
    private int p;
    private Move[] moves = new Move[] {new Move(-1, 0, "N", 0),
    new Move(-1, 1, "NE", 1), new Move(0, 1, "E", 2),
    new Move(1, 1, "SE", 3), new Move(1, 0, "S", 4),
    new Move(1, -1, "SW", 5), new Move(0, -1, "W", 6),
    new Move(-1, -1, "NW", 7)};
    
    public Maze(int[][] maze, int[][] mark, int m, int p) {
        this.maze = maze;
        this.mark = mark;
        this.m = m;
        this.p = p;
    }
    
    public int seek(int x, int y) {
        int g, h;
        String dir;
        // 到达出口
        if (x == m && y == p) {
            return 1;
        }
        
        for (int i = 0; i < moves.length; i++) {
            g = x + moves[i].xOffset;
            h = y + moves[i].yOffset;
            dir = moves[i].dir;
            // 找下一个位置和方向
            if (maze[g][h] == 0 && mark[g][h] == 0) {
                mark[g][h] = 1;
                if (seek(g, h) == 1) {
                    System.out.println(g + h + dir);
                }
                return 1;
            }
            // 回溯,换一个方向再试探
        }
        if (x == 1 && y == 1) {
            System.out.println("not path in maze");
        }
        return 0;
    }
    
    public void path(int m, int p) {
        int i, j, d, g, h;
        mark[1][1] = 1;
        Stack<Item> stack = new Stack<>();
        Item temp = new Item();
        temp.x = 1;
        temp.y = 0;
        temp.dir = 2;
        stack.push(temp);
        while (!stack.isEmpty()) {
            temp = stack.pop();
            i = temp.x;
            j = temp.y;
            d = temp.dir;
            while (d < 8) {
                // 找下一个位置
                g = i + moves[d].xOffset;
                h = j + moves[d].yOffset;
                // 到达出口
                if (g == m && h == p)  {
                    System.out.println(i + j + d);
                    System.out.println(m + p);
                    return;
                }
                // 新的位置可通
                if (maze[g][h] == 0 && mark[g][h] == 0) {
                    mark[g][h] = 1;
                    // 记忆已经通过的位置和前进方向
                    temp.x = i;
                    temp.y = j;
                    temp.dir = d;
                    stack.push(temp);
                    // 移动到(g,h),再各个方向试探
                    i = g;
                    j = h;
                    d = 0;
                } else {
                    d++;
                }
            }
        }
        System.out.println("no path to maze");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值