数据结构与算法之路-4(栈和队列)

一、什么是栈
1.后进者先出,先进者后出,这就是典型的“栈”结构。
2.从栈的操作特性来看,是一种“操作受限”的线性表,只允许在端插入和删除数据,其操作特性用数组和链表均可实现。
3.任何数据结构都是对特定应用场景的抽象,数组和链表虽然使用起来更加灵活,但却暴露了几乎所有的操作,难免会引发错误操作的风险。
4.当某个数据集合只涉及在某端插入和删除数据,且满足后进者先出,先进者后出的操作特性时,我们应该首选栈这种数据结构。

二、栈的应用
1.栈在函数调用中的应用
操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构,用来存储函数调用时的临时变量。每进入一个函数,就会将其中的临时变量作为栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。
2.栈在表达式求值中的应用(比如:34+13*9+44-12/3)
利用两个栈,其中一个用来保存操作数,另一个用来保存运算符。我们从左向右遍历表达式,当遇到数字,我们就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较,若比运算符栈顶元素优先级高,就将当前运算符压入栈,若比运算符栈顶元素的优先级低或者相同,从运算符栈中取出栈顶运算符,从操作数栈顶取出2个操作数,然后进行计算,把计算完的结果压入操作数栈,继续比较。
3.栈在括号匹配中的应用(比如:{}{()})
用栈保存为匹配的左括号,从左到右一次扫描字符串,当扫描到左括号时,则将其压入栈中;当扫描到右括号时,从栈顶取出一个左括号,如果能匹配上,则继续扫描剩下的字符串。如果扫描过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。
当所有的括号都扫描完成之后,如果栈为空,则说明字符串为合法格式;否则,说明未匹配的左括号为非法格式。
4.如何实现浏览器的前进后退功能?
我们使用两个栈X和Y,我们把首次浏览的页面依次压如栈X,当点击后退按钮时,再依次从栈X中出栈,并将出栈的数据一次放入Y栈。当点击前进按钮时,我们依次从栈Y中取出数据,放入栈X中。当栈X中没有数据时,说明没有页面可以继续后退浏览了。当Y栈没有数据,那就说明没有页面可以点击前进浏览了。

	内存中的堆栈和数据结构堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构。
	内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区。
	代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。
	静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收。
	栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。
	堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。

队列

一、什么是队列?
1.先进者先出,这就是典型的“队列”结构。
2.支持两个操作:入队,放一个数据到队尾;出队,从队头取一个元素。
3.和栈一样,队列也是一种操作受限的线性表。

二、队列有哪些常见的应用?
1.阻塞队列
1)在队列的基础上增加阻塞操作,就成了阻塞队列。
2)阻塞队列就是在队列为空的时候,从队头取数据会被阻塞,因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后在返回。
3)从上面的定义可以看出这就是一个“生产者-消费者模型”。这种基于阻塞队列实现的“生产者-消费者模型”可以有效地协调生产和消费的速度。当“生产者”生产数据的速度过快,“消费者”来不及消费时,存储数据的队列很快就会满了,这时生产者就阻塞等待,直到“消费者”消费了数据,“生产者”才会被唤醒继续生产。不仅如此,基于阻塞队列,我们还可以通过协调“生产者”和“消费者”的个数,来提高数据处理效率,比如配置几个消费者,来应对一个生产者。
2.并发队列
1)在多线程的情况下,会有多个线程同时操作队列,这时就会存在线程安全问题。能够有效解决线程安全问题的队列就称为并发队列。
2)并发队列简单的实现就是在enqueue()、dequeue()方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或取操作。
3)实际上,基于数组的循环队列利用CAS原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。
3.线程池资源枯竭是的处理
在资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。
队列实现

public class ArrayQueue {
        //存储数据的数组
        private String[] items;
        //记录数组容量
        private int n;
        private int size;
        //head记录队头索引,tail记录队尾索引
        private int head = 0;
        private int tail = 0;

        //申请一个指定容量的队列
        public ArrayQueue(int capacity) {
            items = new String[capacity];
            n = capacity;
        }

        /*
         * 入队:
         * 1.堆满的时,入队失败
         * 1.1频繁出入队,造成数组使用不连续
         * 1.2在入队的时候,集中触发进行数据搬移
         * 2.在末尾插入数据,注意tail指向队尾元素的索引+1
         */
        public boolean enqueue(String item) {
//表示队满
            if (head == 0 && tail == n)
                return false;
//表示需要数据搬移
            else if (head != 0 && tail == n) {
                for (int i = head; i < tail; i++) {
                    items[i - head] = items[i];
                }
                head = 0;
                tail = tail - head;
            }
//将数据加入队列
            items[tail++] = item;
            size++;
            return true;
        }

        //出队:1.队空时,出队失败;2.出队,head索引+1
        public String dequeue() {
            String res = null;
            if (head == tail) return res;
            res = items[head++];
            size--;
            return res;
        }
    }

  //  二、循环队列

    public class LoopArrayQueue {
        //存储数据的数组
        private String[] items;
        //记录数组容量
        private int n;
        private int size = 0;
        //head记录队头索引,tail记录队尾索引
        private int head = 0;
        private int tail = 0;

        //申请一个指定容量的队列
        public LoopArrayQueue(int capacity) {
            items = new String[capacity];
            n = capacity;
        }

        //入队:关键在于队满的条件
        public boolean enqueue(String item) {
            if ((tail + 1) % n == head) return false;
            items[tail] = item;
            tail = (tail + 1) % n;
            size++;
            return true;
        }

        //出队:关键在于队空的条件
        public String dequeue() {
            String res = null;
            if (head == tail) return res;
            res = items[head];
            head = (head + 1) % n;
            size--;
            return res;
        }
    }

  //  三、链表实现

    public class LinkedQueue {
        //定义一个节点类
        private class Node {
            String value;
            Node next;
        }

        //记录队列元素个数
        private int size = 0;
        //head指向队头结点,tail指向队尾节点
        private Node head;
        private Node tail;

        //申请一个队列
        public LinkedQueue() {
        }

        //入队
        public boolean enqueue(String item) {
            Node newNode = new Node();
            newNode.value = item;
            if (size == 0) head = newNode;
            else tail.next = newNode;
            tail = newNode;
            size++;
            return true;
        }

        //出队
        public String dequeue() {
            String res = null;
            if (size == 0) return res;
            if (size == 1) tail = null;
            res = head.value;
            head = head.next;
            size--;
            return res;
        }
    }

你可能感兴趣的:(数据结构与算法,数据结构,算法,栈,队列)