数据结构与算法-4基础数据结构-栈

基础数据结构-栈

面试经典

  1. 如何设计一个括号匹配的功能?比如给你一串括号让你判断是否符合我们的括号原则,如下所示:[(){()}{}]符合 {}[]{[][[][][]{{}[}}{}}]]] 不符合栈
  2. 如何设计一个浏览器的前进和后退功能?两个栈

栈的定义和特点

  • 栈是一种特殊的线性表,只能在表尾进行插入和删除操作,具有后进先出(LIFO)的特点。

  • 栈的应用场景很多,如括号匹配、浏览器的前进和后退功能等。

  • 栈可以用数组或链表实现,数组实现的栈更常用,因为它在内存访问上更快,且扩容相对简单。

    • 基于数组的栈——以数组为底层数据结构时,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向

      在这里插入图片描述

    • 基于单链表的栈——以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部

      在这里插入图片描述

    • 最大的区别就是扩容,链表天然支持动态扩容。栈溢出。

栈的操作和实现

  • 栈的基本操作包括入栈(push)、出栈(pop)、获取栈顶元素(peek)、判断栈是否为空(isEmpty)等。
  • 用数组实现栈时,需要注意扩容和缩容的问题,以避免空间浪费。
  • 代码实现了栈的基本操作,并通过括号匹配的例子展示了栈的应用。
数组实现栈
/**
 * 使用数组实现栈
 * 1.数组
 * 2.头指针:对应栈顶
 */
public class MyArrayStack<T> {
    private static final int DEFAULT_SIZE = 10;
    private T[] stack;
    private int top;

    public MyArrayStack() {
        stack = (T[]) new Object[DEFAULT_SIZE];
        top = -1;
    }

    public void push(T data){
        if (top == stack.length - 1){
            resize();
        }
        stack[++top] = data;
    }

    public T pop(){
        if (top == -1){
            return null;
        }
        T t = stack[top];
        stack[top] = null;
        top--;
        return t;
    }

    public void resize() {
        T[] newStack = (T[]) new Object[stack.length * 2];
        for (int i = 0; i < stack.length; i++) {
            newStack[i] = stack[i];
        }
    }

    public void print(){
        if (top == -1) {
            System.out.println("stack is empty");
            return;
        }
        for (int i = top; i >= 0; i--) {
            System.out.print(stack[i] + " ");
        }
        System.out.println();
    }

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

    public static void main(String[] args) {
        MyArrayStack<Integer> stack = new MyArrayStack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.print();
        stack.pop();
        stack.print();
        stack.pop();
        stack.print();
    }
}
单链表实现栈
/**
 * 使用单链表实现栈
 * 1.链表节点类
 * 2.栈类
 */
public class MyLinkStack {
    MyStackNode head;
    public MyLinkStack()
    {
        head = null;
    }
    public void push(int data)
    {
        MyStackNode node = new MyStackNode(data);
        node.next = head;
        head = node;
    }

    public MyStackNode pop(){
        MyStackNode node = head;
        if (node != null){
            head = node.next;
            node.next = null;
        }
        return node;
    }

    public void print(){
        MyStackNode node = head;
        while (node != null){
            System.out.print(node.data + " ");
            node = node.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        MyLinkStack stack = new MyLinkStack();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.print("初始栈:");
        stack.print();
        stack.pop();
        System.out.print("出栈:");
        stack.print();
        stack.pop();
        System.out.print("出栈:");
        stack.print();
    }
}
class MyStackNode{
    int data;
    MyStackNode next;

    public MyStackNode(int data) {
        this.data = data;
    }
}

栈的时间复杂度

  • 栈的入栈和出栈操作的时间复杂度都是O(1),非常高效。
  • 括号匹配算法的时间复杂度也是O(n),其中n是括号的数量。

栈的应用

  • 函数调用中使用栈来保存函数的调用信息,实现后进先出的调用顺序。
  • 用栈实现简单的计算器功能,通过两个栈分别存储数字和符号,按照运算规则进行计算。
  • 用栈实现浏览器的前进和后退功能,通过两个栈分别存储访问的页面信息,实现页面的切换。
括号匹配
/**
 * 括号匹配问题
 * 思路:1.使用栈,遍历字符串,遇到左括号入栈,遇到右括号出栈,如果栈为空,则说明匹配,否则不匹配
 */
public class BracketsProblem {
    public static void main(String[] args) {
        String str = "()()()()())";
        System.out.println(isMatch(str));
    }

    private static boolean isMatch(String str) {
        MyArrayStack<String> stack = new MyArrayStack<String>();
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == '(') {
                stack.push(str.charAt(i) + "");
            } else if (str.charAt(i) == ')') {
                String pop = stack.pop();
                if (pop == null) {
                    return false;
                }
            }
        }
        return stack.isEmpty();
    }
}

数学表达式求值

比如用栈实现一个简单的四则运算:3+11*2+8-15/5,用栈来实现这个算术表达式

两个栈来实现:一个放数字 一个放符号。

我们从头开始遍历这个算术表达式:

1.遇到是数字 我们就直接入栈到数字栈里面去。

2.遇到是符合 就把符号栈的栈顶拿出来做比较。如果说他比栈顶符号的优先级高就直接入栈,如果比符号栈顶的优先级低或者相同,就从符号栈里面取栈顶进行计算(从数字栈中取栈顶的2个数),计算完的结果还要再放入到数字栈中。

/**
 * 表达式问题:比如用栈实现一个简单的四则运算:3+11*2+8-15/5
 * 注意:需要考虑 * 和 / 的优先级问题
 */
public class ExpressionProblem {
    public static void main(String[] args)
    {
        String expression = "3 + 11 * 2 + 8 - 15 / 5";
        int result = calculator(expression);
        System.out.println(result);
    }

    private static int calculator(String expression) {
        MyArrayStack<String> numStack = new MyArrayStack<>();
        MyArrayStack<String> operatorStack = new MyArrayStack<>();
        String[] split = expression.split(" ");
        for (int i = 0; i < split.length; i++) {
            String c = split[i];
            if (isOperator(c)) {
                if (isPriority(c)){ // 遇到 * 和 / ,先弹出数据进行处理,将处理后的结果压入栈中
                    String num1 = numStack.pop();
                    String num2 = split[++i];
                    numStack.push(String.valueOf(calculate(num1, num2, c)));
                }else{
                    operatorStack.push(c);
                }
            }else{
                numStack.push(c);
            }
        }
        while (!operatorStack.isEmpty()){
            String num1 = numStack.pop();
            String num2 = numStack.pop();
            String operator = operatorStack.pop();
            numStack.push(String.valueOf(calculate(num2, num1, operator)));
        }
        return Integer.parseInt(numStack.pop());
    }

    private static boolean isOperator(String c) {
        return "+".equals(c) || "-".equals(c) || "*".equals(c) || "/".equals(c);
    }
    private static boolean isPriority(String c) {
        return  "*".equals(c) || "/".equals(c);
    }

    private static int calculate(String num1, String num2, String operator) {
        if ("+".equals(operator)){
            return Integer.parseInt(num1) + Integer.parseInt(num2);
        } else if ("-".equals(operator)) {
            return Integer.parseInt(num1) - Integer.parseInt(num2);
        } else if ("*".equals(operator)) {
            return Integer.parseInt(num1) * Integer.parseInt(num2);
        } else if ("/".equals(operator)) {
            return Integer.parseInt(num1) / Integer.parseInt(num2);
        }
        throw new RuntimeException("运算符错误");
    }
}

课程总结

  • 栈是一种非常重要的数据结构,具有后进先出的特点,在很多场景中都有应用。
  • 用数组实现栈时,需要注意扩容和缩容的问题,以提高空间利用率。
  • 栈的时间复杂度很低,非常高效,可以用于解决很多实际问题。
  • 下节课将讲队列,它是另一种重要的数据结构,与栈的特点和应用场景不同。
  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值