数据结构与算法(4)-栈,队列,优先级队列

时间:2022-12-28 16:11:37

1.栈

先进后出,头进头出.
一般基于数组实现.
出栈操作一般不删除数据,只是指针的移动.
入栈,入栈的时间复杂度都为O(1).

栈结构主要应用:
校验表达式语法是否正确,jvm中方法的执行调用等.

  • 代码:用数组模拟栈结构
public class StackDemo {
    private int maxSize;    //最大尺寸
    private long[] longArray;   //数组
    private int top;    //指针

    public StackDemo(int maxSize){
        this.maxSize = maxSize;
        this.longArray = new long[maxSize];
        this.top = -1;
    }

    //入栈操作
    public void push(long data){
        if(top > this.maxSize-1){   //栈已满
            System.out.println("栈已满,无法进行入栈操作!!!");
        }else{
            longArray[++top] = data;
            System.out.println(data+"被压入栈顶!!!");
        }
    }

    //出栈操作
    public long pop(){
        if(this.isEmpty()){
            try {
                throw new Exception();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //出栈只是移动指针的位置,并非删除
        return longArray[top--];
    }

    //读取栈顶
    public long peek(){
        return longArray[top];
    }

    //是否为空
    public boolean isEmpty(){
        return -1==top;
    }

    //是否满栈
    public boolean isFull(){
        return (maxSize-1)==top;
    }

    public static void main(String[] args) {
        StackDemo stack = new StackDemo(10);
        stack.push(10);
        stack.push(10);
        stack.push(20);
        stack.push(30);

        while(!stack.isEmpty()){
            System.out.println(stack.pop());
        }
    }
}

输出:

10被压入栈顶!!!
10被压入栈顶!!!
20被压入栈顶!!!
30被压入栈顶!!!
30
20
10
10
  • 代码:校验文本中的左右括号是否匹配
public class CheckCharStack {
    private int maxSize;
    private char[] charArray;
    private int top;

    public CheckCharStack(int maxSize) {
        this.maxSize = maxSize;
        this.charArray = new char[maxSize];
        this.top = -1;
    }

    // 判断满栈
    public boolean isFull() {
        return (maxSize - 1) == top;
    }

    // 判断空栈
    public boolean isEmpty() {
        return -1 == top;
    }

    // 入栈操作
    public void push(char c) {
        if (!this.isFull()) {
            charArray[++top] = c;
            System.out.println(c+" 入栈!!!");
        } else {
            System.out.println("栈已满!!!");
        }
    }

    // 出栈操作
    public char pop() throws Exception {
        if (this.isEmpty()) {
            throw new Exception();
        } else {
            System.out.println(charArray[top]+" 出栈!!!");
            return charArray[top--];
        }
    }

    // 获取但不出栈
    public char peek() throws Exception {
        if (this.isEmpty()) {
            throw new Exception();
        } else {
            return charArray[top];
        }
    }
}
public class CharChecker {
    private String input;

    public CharChecker(String input) {
        this.input = input;
    }

    // 字符串括号语法校验
    public void check() {
        int maxSize = this.input.length();
        CheckCharStack stack = new CheckCharStack(maxSize);

        for (int i = 0; i < maxSize; i++) {
            char c = input.charAt(i);
            switch (c) {
            // 如果为左括号,入栈
            case '(':
            case '[':
            case '{':
                stack.push(c);
                break;
            // 如果为右括号,出栈一个用来比较
            case ')':
            case ']':
            case '}':
                char ch = 0;
                System.out.println("当前右括号为 "+c);
                if (!stack.isEmpty()) {
                    try {
                        ch = stack.pop();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    // 如果取出的左括号与当前的右括号不对应,则报错
                    if (('(' == ch && ')' != c) || ('[' == ch && ']' != c) || ('{' == ch && '}' != c)) {
                        System.out.println("Error:" + c + " at " + i + "!!!");
                        return;
                    }
                } else {
                    // 如果栈已空,则报错
                    System.out.println("Error:" + c + " at " + i + "!!!");
                    return;
                }
                break;
            default:
                break;
            }
        }

    }

    //运行
    public static void main(String[] args) {
        new CharChecker("a{b[c])d}").check();
        new CharChecker("a{b[c]d}").check();
    }
}

输出:

{ 入栈!!!
[ 入栈!!!
当前右括号为 ]
[ 出栈!!!
当前右括号为 )
{ 出栈!!!
Error:) at 6!!!
{ 入栈!!!
[ 入栈!!!
当前右括号为 ]
[ 出栈!!!
当前右括号为 }
{ 出栈!!!

2.队列
后进后出.
数组和链表常用来实现队列.
插入和取出时间复杂度为O(1).

以数组模拟队列为例,插入时,先移动再赋值,尾部向右移动,当尾部位置为maxSize-1时,需要将尾部位置赋值-1,而下一次则在0的位置插入.
删除时,头部向右移动,先取值再移动,如果移动后的值为maxSize,需要将头部赋值为0,下一次取0的位置的值.
同时需要记录当前元素的数目.(其实队列可以理解为一个环形结构)

  • 代码:用数组模拟队列结构
public class QueueDemo {
    private int maxSize;    //队列长度
    private int curNum; //当前元素个数
    private int front;  //队列头部
    private int rear;   //队列尾部
    private long[] queue;

    public QueueDemo(int maxSize){
        this.maxSize = maxSize;
        this.curNum = 0;
        //取出时,头部右移,先取后移,头部初始置于0
        this.front = 0;
        //插入时,尾部右移,先移后插,尾部置于-1
        this.rear = -1;
        this.queue = new long[maxSize];
    }

    //插入
    public void insert(long data){
        if(this.isFull()){
            System.out.println("队列已满!!!");
            return;
        }
        //如果尾部已经在最后一个,将尾部置为-1
        if(this.rear == maxSize-1){
            this.rear = -1;
        }
        queue[++rear] = data;
        this.curNum++;
        System.out.println("数据"+data+"被插入队列,位置为"+this.rear);
    }

    //取出(删除)
    public long remove() throws Exception{
        //如果队列为空,抛出异常
        if(this.isEmpty()){
            throw new Exception("队列为空!!!");
        }
        long result = queue[front++];
        this.curNum--;
        System.out.println("数据"+result+"被取出,从位置"+(this.front-1));
        if(maxSize == front){
            front = 0;
        }
        return result;
    }

    //只获取不删除
    public long peek(){
        return queue[front];
    }

    //判断是否为空
    public boolean isEmpty(){
        return 0 == curNum;
    }

    //判断是否已满
    public boolean isFull(){
        return curNum == maxSize;
    }

    //获取当前元素个数
    public int size(){
        return curNum;
    }

    public static void main(String[] args) throws Exception {
        QueueDemo queue = new QueueDemo(5);
        queue.insert(10);
        queue.insert(20);
        queue.insert(30);
        queue.insert(40);

        queue.remove();
        queue.remove();
        queue.remove();

        queue.insert(10);
        queue.insert(20);
        queue.insert(30);
        queue.insert(40);
        queue.insert(50);

        queue.remove();
        queue.remove();
        queue.remove();
        queue.remove();
        queue.remove();
        queue.remove();
    }
}

输出:

数据10被插入队列,位置为0
数据20被插入队列,位置为1
数据30被插入队列,位置为2
数据40被插入队列,位置为3
数据10被取出,从位置0
数据20被取出,从位置1
数据30被取出,从位置2
数据10被插入队列,位置为4
数据20被插入队列,位置为0
数据30被插入队列,位置为1
数据40被插入队列,位置为2
队列已满!!!
数据40被取出,从位置3
数据10被取出,从位置4
数据20被取出,从位置0
数据30被取出,从位置1
数据40被取出,从位置2
Exception in thread "main" java.lang.Exception: 队列为空!!!
    at queue.QueueDemo.remove(QueueDemo.java:39)
    at queue.QueueDemo.main(QueueDemo.java:92)
  • 双端队列简介

    一种多用途的数据机构,左右两边都可以进行插入和取出操作,需要定义以下四种方法.
    insertLeft()
    insertRight()
    removeLeft()
    removeRight()

双端队列可以通过禁用部分功能,同时做栈和队列使用.
禁用insertLeft(),removeLeft()可以作栈使用.
禁用insertLeft(),removeRight()可以作一般队列使用.

3.优先级队列
插入队列的数据有优先级的高低,优先级高的将被先取出.通常使用堆来实现优先级队列,此处暂时用数组模拟.如果将数据的值作为优先级,值越大则越先被取出.
通常将值最小的固定数组角标的0的位置,后续插入的数将进行排序和移动,取出始终取值最大的.
优先级队列的时间复杂度:插入O(N),删除O(1).

  • 代码:数组模拟优先级队列
public class PriorityQueueDemo {
    private int curIndex;
    private long[] queueArray;
    private int maxSize;

    public PriorityQueueDemo(int maxSize) {
        this.maxSize = maxSize;
        this.queueArray = new long[maxSize];
        this.curIndex = -1;
    }

    // 插入,每次插入需要查找遍历应该插入的位置
    public void push(long data) {
        if (this.curIndex == this.maxSize - 1) {
            System.out.println("队列已满!!!");
            return;
        }
        System.out.println("元素" + data + "被插入!!!");
        if (this.curIndex == -1) {
            queueArray[++this.curIndex] = data;
        } else {
            // 从数组尾部开始比较,如果data<=queue[i],将queue[i]右移一位
            for (int i = this.curIndex; i >= 0; i--) {
                if (data <= queueArray[i]) {
                    queueArray[i + 1] = queueArray[i];
                    if(i == 0){
                        queueArray[0] = data;
                    }
                } else { // 如果data>queue[i],将data填入queue[i+1]
                    queueArray[i + 1] = data;
                    break;
                }
            }
            ++this.curIndex;
        }
    }

    // 取出元素
    public long pop() throws Exception {
        if (curIndex == -1) {
            throw new Exception("队列为空!!!");
        } else {
            System.out.println("元素" + this.queueArray[this.curIndex] + "被取出");
            return this.queueArray[this.curIndex--];
        }
    }

    // 获取元素值
    public long peek() throws Exception {
        if (curIndex == -1) {
            throw new Exception("队列为空");
        } else {
            return this.queueArray[this.curIndex];
        }
    }

    // 是否已满
    public boolean isFull() {
        return (maxSize - 1) == this.curIndex;
    }

    // 是否为空
    public boolean isEmpty() {
        return -1 == this.curIndex;
    }

    public static void main(String[] args) throws Exception {
        PriorityQueueDemo queue = new PriorityQueueDemo(5);
        queue.push(20);
        queue.push(30);
        queue.push(10);
        queue.push(35);
        queue.push(5);

        while (!queue.isEmpty()) {
            queue.pop();
        }
    }
}

输出:

元素20被插入!!!
元素30被插入!!!
元素10被插入!!!
元素35被插入!!!
元素5被插入!!!
元素35被取出
元素30被取出
元素20被取出
元素10被取出
元素5被取出