栈是一种操作受限的线性表,只允许在一端插入和删除数据。

 

栈的实现:

栈,即可以用数组来实现,也可以用链表来实现,使用数组来实现的栈叫顺序栈,使用链表来实现的栈叫链式栈。

 

这是基于java语言来分别实现的顺序栈和链式栈.

 

顺序栈

 

/**

 * 用数组实现一个顺序栈

 *

 * <p>栈,先进后出,后进的先出

 *

 * @author liujun

 * @version 0.0.1

 * @date 2019/08/07

 */

  public class MyArrayStack {

  

  private final int[] stackArray;

  

  /** 限制栈的大小 */

  private final int maxStackSize;

  

  /** 当前栈的大小 */

  private int currSize = -1;

  /**

   * 进行栈的初始化操作

   *

   * @param stackSize

   */

  public MyArrayStack(int stackSize) {

    this.maxStackSize = stackSize;

    this.stackArray = new int[stackSize];

  }

  

  /**

   * 栈中放入一个数

   *

   * @param value

   */

  public void push(int value) {

    if (currSize + 1 > maxStackSize) {

      throw new IndexOutOfBoundsException("index out of bounds ");

    }

  

    stackArray[++currSize] = value;

  }

  

  /**

   * 获取当前栈的大小

   *

   * @return

   */

  public int size() {

    return currSize + 1;

  }

  

  /**

   * 从栈中取出一个元素

   *

   * @return 取出的栈顶的元素

   */

  public int pop() {

  

    if (currSize < 0) {

      throw new NegativeArraySizeException("index is less than 0 ");

    }

  

    int getValue = stackArray[currSize];

    stackArray[currSize] = -1;

    currSize--;

  

    return getValue;

  }

}

 

链式栈

/**

 * 使用链表实现链式栈

 *

 * @author liujun

 * @version 0.0.1

 * @date 2019/08/14

 */

  public class MyLinkedStack {

  

  class Node {

  

    private int val;

  

    private Node next;

  

    public Node(int val) {

      this.val = val;

    }

  }

  

  private Node root = new Node(-1);

  

  /** 最大的链式栈大小 */

  private final int maxSize;

  

  /** 当前大小 */

  private int size;

  

  public MyLinkedStack(int maxSize) {

    this.maxSize = maxSize;

    this.size = 0;

  }

  

  /**

   * 插入数据到栈顶

   *

   * @param value

   */

  public void push(int value) {

    if (size > maxSize) {

      throw new IndexOutOfBoundsException("exceed the limit");

    }

    // 1,获得原来链表的下级节点

    Node nextTmp = root.next;

  

    Node currTmpNode = new Node(value);

    root.next = currTmpNode;

    currTmpNode.next = nextTmp;

  

    size++;

  }

  

  public boolean full() {

    return size == maxSize;

  }

  

  // 获取当前链式栈的大小

  public int size() {

    return size;

  }

  

  /**

   * 实现弹出栈顶操作

   *

   * @return

   */

  public int pop() {

    if (size < 1) {

      throw new NullPointerException("stack is null");

    }

  

    // 1,找到栈顶元素

    Node tmpNode = root.next;

  

    if (tmpNode == null) {

      return -1;

    }

  

    // 找到下级元素

    Node nextValue = null;

    nextValue = tmpNode.next;

  

    int value = tmpNode.val;

    root.next = nextValue;

  

    size--;

  

    return value;

  }

}

 

 

 

栈的应用:

括号的有效性判断:

 

leetcode题目:

Given a string containing just the characters '(', ')', '{', '}', '[' and ']',

 determine if the input string is valid.



An input string is valid if:



Open brackets must be closed by the same type of brackets.

Open brackets must be closed in the correct order.

Note that an empty string is also considered valid.



Example 1:



Input: "()"

Output: true

Example 2:



Input: "()[]{}"

Output: true

Example 3:



Input: "(]"

Output: false

Example 4:



Input: "([)]"

Output: false

Example 5:



Input: "{[]}"

Output: true

 

 

检查括号输入括号是否有效,包括”{”, ”}” , ”[” , ”]” , ”(” , ”)”,检查需要支持括号嵌套。

 

我的代码实现

  /**

 * 解决方案

 *

 * <p>通过定义枚举进行括号的配对操作

 *

 * @author liujun

 * @version 0.0.1

 * @date 2018/11/17

 */

  public class Solution {

  

  public enum BracketEnum {

    /** 小括号 */

    PARENTHESES('(', ')'),

    /** 中括号 */

    BRACKETS('[', ']'),

    /** 大括号 */

    CURLYBRACES('{', '}');

  

    private char left;

    private char right;

  

    BracketEnum(char left, char right) {

      this.left = left;

      this.right = right;

    }

  

    public static boolean left(char val) {

      for (BracketEnum item : values()) {

        if (item.left == val) {

          return true;

        }

      }

      return false;

    }

  

    public static BracketEnum right(char val) {

      for (BracketEnum item : values()) {

        if (item.right == val) {

          return item;

        }

      }

      return null;

    }

  }

  

  public boolean isValid(String s) {

    if (null == s) {

      return false;

    }

    if (s.length() == 0) {

      return true;

    }

  

    char[] braket = s.toCharArray();

    Stack<Character> leftStack = new Stack();

    BracketEnum bracket = null;

  

    for (int i = 0; i < braket.length; i++) {

      // 如果为左括号压入左括号左括号栈中

      if (BracketEnum.left(braket[i])) {

        leftStack.push(braket[i]);

      } else {

        // 如果为右括号,则从左侧栈中弹出一个做比较,一至则不放入

        if ((bracket = BracketEnum.right(braket[i])) != null) {

          if (!leftStack.isEmpty()) {

            char left = leftStack.pop();

            if (left != bracket.left) {

              return false;

            }

          } else {

            return false;

          }

        }

      }

    }

  

    return leftStack.isEmpty();

  }

}

 

 

 

进行分解成示意图就是图中这个操作流程.

 

 

这样看起来非常的简单,在遇到左括号时,进行压栈操作,在遇到右括号时进行出栈操作。

当发生一个括号不能匹配时,则可以直接返回校验失败,当所有的括号都遍历完成后,如果栈为空,说明匹配成功,否则匹配失败。接下来详细说明下整个过程以”{[()]}”为例

  1. 在遇到”{”括号了,进行压栈操作。就像1中所示.
  2. 接下来是”[”括号,也是左括号,进行压栈操作。如2所示
  3. 在接下来遍历的是”(”,同样是左括号,也同样进行压栈操作。如3
  4. 接下来遇到的是”)”,此时是右括号,将从栈顶元素同当前括号进行配对,“()”正好配对成功,将”(”出栈。如4
  5. 再接下来是”]”,此时也是右括号,将同栈顶元素进行配对操作。“[]”也可以匹配成功。将”[”出栈。如5
  6. 再接下来就是最后一个字符”}”,与栈顶元素”{”,可以配对成功,将”{”出栈。如6所示。

当遍历完成后,栈已经为空,说明配对成功。

 

 

针对这个来说,还有更优的解法。此参考了leetcode上其他人的实现。

/**

 * 解决办法

 *

 * <p>使用数组来实现栈的功能

 *

 * @author liujun

 * @version 0.0.1

 * @date 2018/11/17

 */

  public class SolutionArrayStackOther {

  

  public boolean isValid(String s) {

    if (null == s) {

      return false;

    }

  

    if (s.length() == 0) {

      return true;

    }

  

    char[] stack = new char[s.length()];

    int stackIndex = -1;

  

    char data;

    int readIndex = 0;

  

    while (readIndex < s.length()) {

      data = s.charAt(readIndex);

      

      if (data == ' ') {

        readIndex++;

        continue;

      }

  

      if (data == '(' || data == '{' || data == '[') {

        stack[++stackIndex] = data;

      } else if (data == ')' || data == '}' || data == ']') {

        if (stackIndex >= 0

            && ((stack[stackIndex] == '(' && data == ')')

                || (stack[stackIndex] == '{' && data == '}')

                || (stack[stackIndex] == '[' && data == ']'))) {

          stackIndex--;

        } else {

          return false;

        }

      }

  

      readIndex++;

    }

  

    return stackIndex == -1;

  }

}

 

 

 

表达式的求值:

Leetcode227:

227 basic calculator 2



Implement a basic calculator to evaluate a simple expression string.



The expression string contains only non-negative integers, +, -, *, / operators and empty spaces .

 The integer division should truncate toward zero.



Example 1:



Input: "3+2*2"

Output: 7

Example 2:



Input: " 3/2 "

Output: 1

Example 3:



Input: " 3+5 / 2 "

Output: 5

Note:



You may assume that the given expression is always valid.

Do not use the eval built-in library function.

 

注:需要注意的是在乘除法的优先级比加减法要高,需要进行优先计算。

 

可以通过两个栈来实现一个保存数值的栈,一个保存操作数的栈。当遇到操作数时就进行压栈操作,

当遇到操作符时,就与栈顶元素作比较,如果比栈顶元素的优先级高。就将当前操作符压栈操作;如果比栈顶元素的优先级低,则弹出两个操作数与一个操作符进行运算。然后继续比较。

 

我的实现:

  /**

 * 一个简单的非负数的计算器的实现

 *

 * @author liujun

 * @version 0.0.1

 * @date 2019/08/16

 */

  public class SolutionCalculator {

  

  enum OperEnum {

    PLUS('+', 1),

    MINU('-', 1),

    MULT('*', 2),

    DIVI('/', 2),

    ;

    private char oper;

    private int order;

  

    public static OperEnum getOper(char item) {

      OperEnum oper = null;

      for (OperEnum operitem : values()) {

        if (operitem.oper == item) {

          oper = operitem;

        }

      }

  

      return oper;

    }

  

    OperEnum(char oper, int order) {

      this.oper = oper;

      this.order = order;

    }

  }

  

  public int calculate(String s) {

  

    if (s == null || s.isEmpty()) {

      return 0;

    }

  

    // 数值栈

    Stack<Integer> valueStack = new Stack<>();

    // 操作数栈

    Stack<Character> operStack = new Stack<>();

  

    int curIndex = 0;

    char charItem;

    while (curIndex < s.length()) {

      charItem = s.charAt(curIndex);

  

      if (charItem >= '0' && charItem <= '9') {

        int currValue = 0;

  

        //进行数字的提取,比较巧妙。利用的是位之前是10的倍数的关系,然后再加当前数。

        while (curIndex < s.length() && Character.isDigit(s.charAt(curIndex))) {

          currValue = currValue * 10 + (s.charAt(curIndex) - '0');

          curIndex++;

        }

        // 将操作数压栈操作

        valueStack.push(currValue);

  

        continue;

      } else if (charItem == OperEnum.PLUS.oper

          || charItem == OperEnum.MINU.oper

          || charItem == OperEnum.MULT.oper

          || charItem == OperEnum.DIVI.oper) {

  

        if (operStack.isEmpty()) {

          operStack.push(charItem);

        } else {

          OperEnum operLast = OperEnum.getOper(operStack.peek());

          OperEnum operCurr = OperEnum.getOper(charItem);

          //算法符的优先级比较

          if (operCurr.order > operLast.order) {

            operStack.push(charItem);

          } else {

            // 执行运算

            runCount(valueStack, operStack, operCurr);

            operStack.push(charItem);

          }

        }

      }

  

      curIndex++;

    }

  

    if (!operStack.isEmpty()) {

      runCount(valueStack, operStack, null);

    }

  

    return valueStack.pop();

  }

  

  /**

   * 进行栈中数据的计算操作

   * @param valueStack 数据栈

   * @param operStack 操作符栈

   * @param operCurr 当前操作符

   */

  private void runCount(Stack<Integer> valueStack, Stack<Character> operStack, OperEnum operCurr) {

    while (!operStack.isEmpty()) {

  

      //栈中的操作符优先级低于当前操作符优先级,则不运算。

      OperEnum lastStack = OperEnum.getOper(operStack.peek());

      if (null != operCurr && lastStack.order < operCurr.order) {

        break;

      }

  

      char currValue = operStack.pop();

      int value2 = valueStack.pop();

      int value1 = valueStack.pop();

  

      if (currValue == OperEnum.PLUS.oper) {

        valueStack.push(value1 + value2);

      } else if (currValue == OperEnum.MINU.oper) {

        valueStack.push(value1 - value2);

      } else if (currValue == OperEnum.MULT.oper) {

        valueStack.push(value1 * value2);

      } else if (currValue == OperEnum.DIVI.oper) {

        valueStack.push(value1 / value2);

      }

    }

  }

}

 

 

详细的运算流程:

以3+2*2/2+6为例

 

 

  1. 解析到数字将数字3,压入到数值栈中。
  2. 解析到操作符+,此时操作符栈为空,将当前操作符+直接压入到操作符栈中。
  3. 解析到数字2,将2压入到数值栈中。
  4. 解析到操作符*号,由于乘法的优先级高于加法,因此,将操作符*压入栈中。
  5. 解析到数字2,将2压入到数值栈中
  6. 解析到数字/号,由于除号的优先级与乘法相同,因此将弹出两个操作数“2”,“2”及一个操作符*号进行运算得到4,将结果4压入到数值栈中,再将/号压入到操作符栈中。
  7. 解析到2,将2压入到数值的栈中
  8. 解析到+号,由于+号的优先级低于除法的优先级,因此将弹出两个操作数“4”,“2”及操作符/号,进行运算得到2,将结果2,压入到数值栈中,当前操作符+号与操作符栈中的+号优先级相同,所以会再次弹出两个操作数“3”,“2”和一个操作符“+”进行运算得到5,将5压入到数值栈中。再将当前+号压入到操作数栈中。
  9. 解析到6,将6压入到数值栈中。当前遍历结束。
  10. 当前操作符栈中还存在操作符,将弹出两个数值“5”,“6”和一个操作符+,进行运算处到11

 

这就是一个数值表达式计算的完整的过程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值