数据结构之栈

什么是栈?

如何理解栈

生活中的例子比喻:比如我们在放盘子的时候都是从下往上一个个放,拿的时候是从上往下一个个的那,不能从中间抽,这种其实就是一个典型的栈型数据结构。
特性:后进先出即Last In First Out (LIFO)

栈如何实现

  1. 其实它是一个限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。
  2. 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素
  3. 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈的分类

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

image.png

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

image.png


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

栈的基本操作

假定以数组来实现

  1. 入栈
  2. 出栈
  3. 时间复杂度分析

栈的实际应用

  1. 括号匹配
  2. 函数嵌套调用
void main{ add(); }
void add(){ sub(); }
// 是不是这个sub最先完成。sub又是最后才进来的吧 是不是就是后进先出,函数调用就是用的栈来实现的
  1. 数学表达式求值

编程题

通过数组实现一个栈

package stack;

import java.util.Arrays;

/**
 * 数组实现栈:操作核心在栈顶
 * 头为栈底
 * 尾为栈头
 *
 * @author zw
 * @create 2023-03-25 2:45
 */
public class ArrayStack<Item> implements MyStack<Item> {

    //最好就是开始的时候就设置大小
    private Item[] elementData = (Item[]) new Object[1];
    //大小 初始的元素个数
    private int size = 0;
    // 扩容因子
    private final double DILATANCY_FACTOR = 0.75d;

    /**
     * 入栈 O(1)
     * @param item
     */
    @Override
    public void push(Item item) {
        elementData[size++] = item;
        resize();
    }

    /**
     * 出栈 O(1)
     * @return
     */
    @Override
    public Item pop() {
        if (isEmpty()) {
            return null;
        }
        // 删除数组一个个元素,后边元素前移
        Item item = elementData[--size];
        // 回收删除的数据
        elementData[size] = null;
        resize();
        return item;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }


    /**
     * 扩容O(n)
     */
    private void resize() {
        // 扩容
        if (size >= elementData.length * DILATANCY_FACTOR) {
            elementData = Arrays.copyOf(elementData, size << 1);
        }
        //缩容
        if (size < elementData.length * (1 - DILATANCY_FACTOR)) {
            elementData = Arrays.copyOf(elementData, size+1);
        }
    }

    private void print() {
        StringBuffer sb = new StringBuffer();
        if (size == 0) {
            sb.append("[]");
        } else {
            sb.append("size=").append(size).append(" [");
            for (int i = 0; i < size; i++) {
                sb.append(elementData[i]).append(",");
            }
            sb.append("]");
        }
        System.out.print(sb.toString().replaceAll(",]", "]"));
    }


}

测试用例

    public static void main(String[] args) {
        ArrayStack<Integer> stack = new ArrayStack<>();
        for (int i = 0; i < 5; i++) {
            stack.push(i);
            System.out.print("入栈:"+i+"  ");
            stack.print();
            System.out.println();
        }
        for (int i = 0; i < 5; i++) {
            Integer pop = stack.pop();
            System.out.print("出栈: "+ pop +" ");
            stack.print();
            System.out.println();
        }

    }

运行结果

入栈:0  size=1 [0]
入栈:1  size=2 [0,1]
入栈:2  size=3 [0,1,2]
入栈:3  size=4 [0,1,2,3]
入栈:4  size=5 [0,1,2,3,4]
出栈: 4 size=4 [0,1,2,3]
出栈: 3 size=3 [0,1,2]
出栈: 2 size=2 [0,1]
出栈: 1 size=1 [0]
出栈: 0 []

通过链表实现一个栈



/**
 * 单向链表实现栈:操作核心在栈顶
 * 链表头节点为栈顶
 *
 * @author zw
 * @create 2023-03-25 2:45
 */
public class LinkedListStack<Item> implements MyStack<Item> {

    MyLinkedList<Item> linkedList = new MyLinkedList<>();

    /**
     * 入栈 O(1)
     * @param item
     */
    @Override
    public void push(Item item) {
        linkedList.addHead(item);
    }

    /**
     * 出栈 O(1)
     * @return
     */
    @Override
    public Item pop() {
        Item first = linkedList.getFirst();
        linkedList.removeHead();
        return first;
    }

    @Override
    public int size() {
        return linkedList.size;
    }

    @Override
    public boolean isEmpty() {
        return linkedList.size == 0;
    }



}

测试用例


    public static void main(String[] args) {
        LinkedListStack<Integer> stack = new LinkedListStack<>();
        for (int i = 0; i < 5; i++) {
            stack.push(i);
            System.out.print("入栈:"+i+"  ");
            stack.linkedList.print();
        }
        for (int i = 0; i < 5; i++) {
            Integer pop = stack.pop();
            System.out.print("出栈: "+ pop +" ");
            stack.linkedList.print();
        }

    }

运行结果

入栈:0  size=1  [0]
入栈:1  size=2  [1,0]
入栈:2  size=3  [2,1,0]
入栈:3  size=4  [3,2,1,0]
入栈:4  size=5  [4,3,2,1,0]
出栈: 4 size=4  [3,2,1,0]
出栈: 3 size=3  [2,1,0]
出栈: 2 size=2  [1,0]
出栈: 1 size=1  [0]
出栈: 0 []

括号匹配

如何设计一个括号匹配的功能?比如给你一串括号让你判断是否符合我们的括号原则,如下所示:
该算法的应用,如判断占位符表达式是否正确 #{age}
[(){()}{}]符合
{}[}}{}}]]] 不符合

思路:左右括号建立匹配关系。遍历字符串字符,遇到左括号压栈,遇到右括号弹栈,遍历完后栈中没数据则匹配成功

package stack;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * 括号匹配:
 *
 * @author zw
 * @create 2023-03-25 4:13
 */
public class ParenthesisMatch {

    //定义左右括号的对应关系
    static Map<Character, Character> bracket = new HashMap<>();

    static {
        bracket.put('(', ')');
        bracket.put('[', ']');
        bracket.put('{', '}');
    }


    public static boolean isOk(String s) {        //s表示的就是待匹配的括号串 [}使用字符来表示 时间复杂度 O(n)

        MyStack<Character> stack = new ArrayStack<Character>();
        for (char c : s.toCharArray()) {
            Character character = bracket.get(c);
            // 匹配上则为左括号
            if (character != null) {
                stack.push(c);
            }
                // 匹配右括号
            else {
                stack.pop();
            }
        }
        return stack.isEmpty();
    }


}

测试用例

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String s = scanner.next();
            System.out.println("s的匹配结果:" + isOk(s));
        }
    }

运行结果

[(){()}()]
s的匹配结果:true
[(){()}{}]
s的匹配结果:true
{}[}}{}}]]]
s的匹配结果:false
(***)-[{-------}]
s的匹配结果:true

如何设计一个浏览器的前进和后退功能?

两个栈

数学表达式求值

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

我们从头开始遍历这个算术表达式:
1.遇到是数字 我们就直接入栈到数字栈里面去。
2.遇到是符合 就把符号栈的栈顶拿出来做比较。如果说他比栈顶符号的优先级高就直接入栈,如果比符号栈顶的优先级低或者相同,就从符号栈里面取栈顶进行计算(从数字栈中取栈顶的2个数),计算完的结果还要再放入到数字栈中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值