php做网站基本流程,网站总类,网站开发进度设计,网站分类目录栈
一、什么是栈#xff1f;
1.后进者先出#xff0c;先进者后出#xff0c;这就是典型的“栈”结构。 2.从栈的操作特性来看#xff0c;是一种“操作受限”的线性表#xff0c;只允许在端插入和删除数据。
二、为什么需要栈#xff1f;
1.栈是一种操作受限的数据结构…栈
一、什么是栈
1.后进者先出先进者后出这就是典型的“栈”结构。 2.从栈的操作特性来看是一种“操作受限”的线性表只允许在端插入和删除数据。
二、为什么需要栈
1.栈是一种操作受限的数据结构其操作特性用数组和链表均可实现。 2.但任何数据结构都是对特定应用场景的抽象数组和链表虽然使用起来更加灵活但却暴露了几乎所有的操作难免会引发错误操作的风险。 3.所以当某个数据集合只涉及在某端插入和删除数据且满足后进者先出先进者后出的操作特性时我们应该首选栈这种数据结构。
三、如何实现栈
1.栈的API public class Stack { //压栈 public void push(Item item){} //弹栈 public Item pop(){} //是否为空 public boolean isEmpty(){} //栈中数据的数量 public int size(){} //返回栈中最近添加的元素而不删除它 public Item peek(){} }
实现
2.1 数组实现自动扩容
时间复杂度分析根据均摊复杂度的定义可以得数组实现自动扩容符合大多数情况是O(1)级别复杂度个别情况是O(n)级别复杂度比如自动扩容时会进行完整数据的拷贝。 空间复杂度分析在入栈和出栈的过程中只需要一两个临时变量存储空间所以O(1)级别。我们说空间复杂度的时候是指除了原本的数据存储空间外算法运行还需要额外的存储空间。 // 基于数组实现的顺序栈
public class ArrayStack {private String[] items; // 数组private int count; // 栈中元素个数private int n; //栈的大小// 初始化数组申请一个大小为n的数组空间public ArrayStack(int n) {this.items new String[n];this.n n;this.count 0;}// 入栈操作public boolean push(String item) {// 数组空间不够了直接返回false入栈失败。if (count n) return false;// 将item放到下标为count的位置并且count加一items[count] item;count;return true;}// 出栈操作public String pop() {// 栈为空则直接返回nullif (count 0) return null;// 返回下标为count-1的数组元素并且栈中元素个数count减一String tmp items[count-1];--count;return tmp;}
}2.2 链表实现
时间复杂度分析压栈和弹栈的时间复杂度均为O(1)级别因为只需更改单个节点的索引即可。 空间复杂度分析在入栈和出栈的过程中只需要一两个临时变量存储空间所以O(1)级别。我们说空间复杂度的时候是指除了原本的数据存储空间外算法运行还需要额外的存储空间。 实现代码见另一条留言
public class StackOfLinkedItem implements IterableItem {
//定义一个内部类就可以直接使用类型参数
private class Node{
Item item;
Node next;
}
private Node first;
private int N;
//构造器
public StackOfLinked(){}
//添加
public void push(Item item){
Node oldfirst first;
first new Node();
first.item item;
first.next oldfirst;
N;
}
//删除
public Item pop(){
Item item first.item;
first first.next;
N--;
return item;
}
//是否为空
public boolean isEmpty(){
return N 0;
}
//元素数量
public int size(){
return N;
}
//返回栈中最近添加的元素而不删除它
public Item peek(){
return first.item;
}
Override
public IteratorItem iterator() {
return new LinkedIterator();
}
//内部类迭代器
class LinkedIterator implements Iterator{
int i N;
Node t first;
Override
public boolean hasNext() {
return i 0;
}
Override
public Item next() {
Item item (Item) t.item;
t t.next;
i--;
return item;
}
}
}三、栈的应用
1.栈在函数调用中的应用
操作系统给每个线程分配了一块独立的内存空间这块内存被组织成“栈”这种结构用来存储函数调用时的临时变量。每进入一个函数就会将其中的临时变量作为栈帧入栈当被调用函数执行完成返回之后将这个函数对应的栈帧出栈。
2.栈在表达式求值中的应用比如3413*944-12/3
利用两个栈其中一个用来保存操作数另一个用来保存运算符。我们从左向右遍历表达式当遇到数字我们就直接压入操作数栈当遇到运算符就与运算符栈的栈顶元素进行比较若比运算符栈顶元素优先级高就将当前运算符压入栈若比运算符栈顶元素的优先级低或者相同从运算符栈中取出栈顶运算符从操作数栈顶取出2个操作数然后进行计算把计算完的结果压入操作数栈继续比较。
3.栈在括号匹配中的应用比如{}{()}
用栈保存为匹配的左括号从左到右一次扫描字符串当扫描到左括号时则将其压入栈中当扫描到右括号时从栈顶取出一个左括号如果能匹配上则继续扫描剩下的字符串。如果扫描过程中遇到不能配对的右括号或者栈中没有数据则说明为非法格式。 当所有的括号都扫描完成之后如果栈为空则说明字符串为合法格式否则说明未匹配的左括号为非法格式。
4.如何实现浏览器的前进后退功能
我们使用两个栈X和Y我们把首次浏览的页面依次压如栈X当点击后退按钮时再依次从栈X中出栈并将出栈的数据一次放入Y栈。当点击前进按钮时我们依次从栈Y中取出数据放入栈X中。当栈X中没有数据时说明没有页面可以继续后退浏览了。当Y栈没有数据那就说明没有页面可以点击前进浏览了。
JVM 内存管理中有个“堆栈”的概念。栈内存用来存储局部变量和方法调用堆内存用来存储 Java 中的对象。那 JVM 里面的“栈”跟数据结构中的“栈”是不是一回事呢
内存中的堆栈和数据结构堆栈不是一个概念可以说内存中的堆栈是真实存在的物理区数据结构中的堆栈是抽象的数据存储结构。 内存空间在逻辑上分为三部分代码区、静态数据区和动态数据区动态数据区又分为栈区和堆区。
代码区存储方法体的二进制代码。高级调度作业调度、中级调度内存调度、低级调度进程调度控制代码区执行代码的切换。静态数据区存储全局变量、静态变量、常量常量包括final修饰的常量和String常量。系统自动分配和回收。栈区存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。堆区new一个对象的引用或地址存储在栈区指向该对象存储在堆区中的真实数据。
leetcode上关于栈的题目20,155,232,844,224,682,49
队列
一、什么是队列
1.先进者先出这就是典型的“队列”结构。 2.支持两个操作入队enqueue()放一个数据到队尾出队dequeue()从队头取一个元素。 3.所以和栈一样队列也是一种操作受限的线性表。
二、如何实现队列
1.队列API
public interface Queue { public void enqueue(T item); //入队 public T dequeue(); //出队 public int size(); //统计元素数量 public boolean isNull(); //是否为空 }
2.实现
** 2.1 数组** // 用数组实现的队列
public class ArrayQueue {// 数组items数组大小nprivate String[] items;private int n 0;// head表示队头下标tail表示队尾下标private int head 0;private int tail 0;// 申请一个大小为capacity的数组public ArrayQueue(int capacity) {items new String[capacity];n capacity;}// 入队public boolean enqueue(String item) {// 如果tail n 表示队列已经满了if (tail n) return false;items[tail] item;tail;return true;}// 出队public String dequeue() {// 如果head tail 表示队列为空if (head tail) return null;// 为了让其他语言的同学看的更加明确把--操作放到单独一行来写了String ret items[head];head;return ret;}// 入队操作将item放入队尾,如图public boolean enqueue(String item) {// tail n表示队列末尾没有空间了if (tail n) {// tail n head0表示整个队列都占满了if (head 0) return false;// 数据搬移for (int i head; i tail; i) {items[i-head] items[i];}// 搬移完之后重新更新head和tailtail - head;head 0;}items[tail] item;tail;return true;}
}2.2 循环链表思想 关键队空条件 head tail 队满判断条件 (tail1)%nhead public class CircularQueue {// 数组items数组大小nprivate String[] items;private int n 0;// head表示队头下标tail表示队尾下标private int head 0;private int tail 0;// 申请一个大小为capacity的数组public CircularQueue(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;return true;}// 出队public String dequeue() {// 如果head tail 表示队列为空if (head tail) return null;String ret items[head];head (head 1) % n;return ret;}
}2.3 链表实现
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;
}三、队列有哪些常见的应用
1.阻塞队列
1在队列的基础上增加阻塞操作就成了阻塞队列。 2阻塞队列就是在队列为空的时候从队头取数据会被阻塞因为此时还没有数据可取直到队列中有了数据才能返回如果队列已经满了那么插入数据的操作就会被阻塞直到队列中有空闲位置后再插入数据然后在返回。 3从上面的定义可以看出这就是一个“生产者-消费者模型”。这种基于阻塞队列实现的“生产者-消费者模型”可以有效地协调生产和消费的速度。当“生产者”生产数据的速度过快“消费者”来不及消费时存储数据的队列很快就会满了这时生产者就阻塞等待直到“消费者”消费了数据“生产者”才会被唤醒继续生产。不仅如此基于阻塞队列我们还可以通过协调“生产者”和“消费者”的个数来提高数据处理效率比如配置几个消费者来应对一个生产者。
2.并发队列
1在多线程的情况下会有多个线程同时操作队列这时就会存在线程安全问题。能够有效解决线程安全问题的队列就称为并发队列。 2并发队列简单的实现就是在enqueue()、dequeue()方法上加锁但是锁粒度大并发度会比较低同一时刻仅允许一个存或取操作。 3实际上基于数组的循环队列利用CAS原子操作可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。 3.线程池资源枯竭是的处理 在资源有限的场景当没有空闲资源时基本上都可以通过“队列”这种数据结构来实现请求排队。
[剑指offer][JAVA]面试题第[09]题[用两个栈实现队列][LinkedList]
主要整理参考作者姜威 笔记整理来源 王争 数据结构与算法之美