【学习记录】关于栈的实现、栈的几个小练习

栈的简单介绍

线性表

  1. 定义

线性表是具有相同特性数据元素的一个有限序列,
该序列中所含元素的个数叫做线性表的长度。

  1. 逻辑特性
    只有一个表头元素、没有前驱,只有一个表尾元素,没有后继。除表头和表尾外,其他元素只有一个直接前驱,也只有一个直接后继。
  2. 存储结构
    可以是顺序存储结构 也可以是链式存储结构

  1. 栈的定义

栈是一种只能在一端进行插入或删除的 操作受限的 线性表

  1. 栈的特点
    FILO
  2. 栈的数学性质
    当n个元素以某种顺序进栈,并且可以在任意时刻出栈时,所获得的元素排列的数目N恰好满足函数Catalan()的计算即 图来自这里->贴个链接
    【学习记录】关于栈的实现、栈的几个小练习_第1张图片

栈的实现

自己实现

顺序映像

import java.util.EmptyStackException;

public class MyStack<E> {
    private static final int DEFAULT_CAPACITY = 10;
    private static final int MAX_CAPACITY = Integer.MAX_VALUE-8;

    //属性
    private E[] list;
    private int top = -1;

    //构造方法
    public MyStack(){
        list = (E[])new Object[DEFAULT_CAPACITY];
    }

    public MyStack(int initialCapacity){
        checkCapacity(initialCapacity);
        list = (E[])new Object[initialCapacity];
    }
    private void checkCapacity(int initialCapacity) {
        if(initialCapacity <0 || initialCapacity> MAX_CAPACITY){
            throw new IllegalArgumentException("initialCapacity = "+ initialCapacity );
        }
    }

    //方法
    public void push(E e){
        if(top==list.length-1){
            //如果需要扩容
            int newLength = calculateCapacity();
            grow(newLength);
        }
        list[++top] =e;
    }
    //计算容量
    private int calculateCapacity(){
        if(list.length ==MAX_CAPACITY) throw new RuntimeException("too long");
        int newLength = list.length+(list.length>>1);
        if(newLength<0 ||newLength>MAX_CAPACITY){
            newLength = MAX_CAPACITY;
        }
        return newLength;
    }
    private void grow(int newLength){
        E[] tempList = (E[])new Object[newLength];
        for (int i = 0; i <top ; i++) {
            tempList[i] = list[i];
        }
        list = tempList;
    }


    public E pop(){
        if(isEmpty()) throw new EmptyStackException();
        E e = list[top];
        list[top--] =null;
        return e;
    }

    public boolean isEmpty(){
        return top == -1;
    }


    public E peek(){
        if(isEmpty()) throw new EmptyStackException();
        return (E)list[top];
    }
}

非顺序映像

public class MyStack<E> {

    //属性
    private Node top;
    //Node
    private  static class Node{
        Object data;
        Node next;
        public Node(Object data){
            this.data = data;
        }
        public Node(Object data, Node next){
            this.data =data;
            this.next = next;
        }

    }
    public void push(E e){
        top = new Node(e, top);
    }

    public E pop(){
        if(isEmpty()) {
            throw new EmptyStackException();
        }
        E topData = (E)top.data;
        top = top.next;
        return topData;
    }

    public E peek(){
        if(isEmpty()){
            throw new EmptyStackException();
        }

        return (E)top.data;
    }

    public boolean isEmpty(){
        return top ==null;
    }

}

练习

表达式逆序

思路:遍历字符串,将一个个字符压入栈中
再一个个出栈,并且拼接成字符串

public class MyStackDemo1 {
    public static void main(String[] args) {
        String s = "ABC";
        s = reverse(s);
        System.out.println(s);
    }

    public static String reverse(String original){
        MyStack<Character> stack = new MyStack();
        for (int i = 0; i <original.length() ; i++) {
            stack.push(original.charAt(i));
        }
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()){
            sb.append(stack.pop());
        }

        return sb.toString();
    }
}

括号匹配

/*
 括号匹配问题
思路: 遍历字符串
 遇到左括号,直接将对应的右括号入栈
 遇到右括号,判断此时栈是否为空,
        是: 返回false
        否:出栈一个元素与右括号进行比较
                相同:继续遍历
                不相同 返回false

 遍历完字符串,判断栈是否为空
    是:true
    否:false
 */

public class MyStackDemo2 {
    public static void main(String[] args) {
        String s = "public static void main(String[] args)";  // 待检查字符串
        System.out.println(judge(s));

    }

    public static boolean judge (String s ){
        MyStack stack = new MyStack();
        for (int i = 0; i< s.length(); i++){
            //如果是左括号,入栈
            char c = s.charAt(i);
            if(c == '(') stack.push(')');
            if(c == '[') stack.push(']');
            if(c == '{') stack.push('}');

            if(c==')' || c ==']' || c =='}' ){
                if(stack.isEmpty()) {
                    return false;
                }
                if((char)stack.pop() != c) return false;
            }
        }
        return stack.isEmpty();
    }
}

表达式求值

中缀表达式求值
可以先转成后缀表达式,再对后缀表达式求值;也可以同时进行

public class MyStackDemo3 {
    public static void main(String[] args) {
        String exp = "12+4-3*4+5-23+1";
        System.out.println(calculate(exp));

    }

    public static int calculate(String exp){
        // 利用两个栈 分别存储操作数 和 操作符
        MyStack<Integer> stackNum = new MyStack();
        MyStack<Character> stackOp = new MyStack();
        int  result;  //num1、num2存储操作数,result存储计算结果
        int n =0;  //保存每个数字
        for (int i = 0; i < exp.length() ; i++) {
            char c = exp.charAt(i);
            //如果c是操作数
            if(Character.isDigit(c)) {  // Character.isDigit(chr ch) 判断是否是数字
                n = n * 10 + Integer.parseInt(String.valueOf(c)); //将 单个数字字符保存
            } else {
                if (n != 0) {
                    stackNum.push(n);
                    n = 0;
                }
                //如果遇到操作符 当操作符栈非空时,
                while (!stackOp.isEmpty()) {
                    //如果非空,与操作符栈顶元素进行比较
                    if (getPriority(c)>getPriority(stackOp.peek())) { //如果优先级大于栈顶元素,则将其入操作符栈
                        stackOp.push(c);
                        break;  //退出循环
                    } else {
                        //如果优先级小于或等于栈顶元素,则出栈,进行计算
                        result = com(stackNum.pop(), stackOp.pop(), stackNum.pop());
                        //将计算结果压入操作数栈中
                        stackNum.push(result);
                    }
                }
                //如果操作符栈为空,直接入栈
                if (stackOp.isEmpty()) stackOp.push(c);

            }
        }
        //如果最后一个字符是数字,未入栈
        if(n!=0) stackNum.push(n);
        //for循环结束,说明表达式遍历完毕,
        // 但是此时StackNUm和StackOp中可能还有剩余元素,需要继续计算完
        return remainderCom(stackNum,stackOp);
    }
    //计算栈中剩余的操作数
    private static int remainderCom(MyStack<Integer> stackNum, MyStack<Character> stackOp) {
        while(!stackOp.isEmpty()){
            stackNum.push(com(stackNum.pop(),stackOp.pop(),stackNum.pop()));
        }
        //将栈顶元素返回 ,为最终结果
        return stackNum.pop();
    }

    //根据优先级判断能否出栈计算
    private static int getPriority(char c) {
        if(c=='+' || c=='-')
            return 0;
        else
            return 1;
    }

    //注意运算次序,num2先出栈,num1后出栈
    private static int com(int num2, char op, int num1) {
        System.out.println(num1+" " + op +" "+ num2);
        if(op=='+') return num1+num2;
        if(op=='-') return num1-num2;
        if(op=='*') return num1*num2;
        if(op=='/') {
            if (num2==0){
                throw new ArithmeticException();
            }else {
                return num1/num2;
            }
        }
        return Integer.MIN_VALUE;
    }


}

用栈实现队列数据结构

leet-code 用栈实现队列

/*
思路:
	push: 添加元素都是在s1中添加,并且用front标识队头元素。
	pop: 出队列是从s2中出队列,如果s2是空的,就将s1中的元素倒入s2中。
	peek: 如果s2是空的,就返回s1中标识的front, 否则返回s2的栈顶元素
	empty: 两个栈都为空, 队列才为空
*/
public class MyQueue<E> {
    Deque<E> s1 = new LinkedList<>();
    Deque<E> s2 = new LinkedList<>();
    E front;

    public void push(E e) {
        if(s1.isEmpty()) {
            front = e;
        }
        s1.push(e);
    }
 public E pop() {
        if(s2.isEmpty()) {
            while(!s1.isEmpty()) {
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    public E peek() {
        if(s2.isEmpty()) {
            return front;
        }
        return s2.peek();
    }
    
    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }
}

你可能感兴趣的:(【学习记录】关于栈的实现、栈的几个小练习)