《Java小子怒闯数据结构九重天》第三重天——栈

简介: 自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰,现在讲的是第三重。

前言


自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰。


但是这谈何容易,因为每一重天都有神兽把守,想要冲破每一重天都必须收服守护的神兽才行。


守护九重天的神兽分别是:数组、字符串、栈、队列、链表、树、散列表、堆、图。可见他们的战斗力也是逐层增强的。想只凭靠自身的能力拿下他们谈何容易。


不过大家不必惊慌,我这里有一本上古秘籍《Java小子怒闯数据结构九重天》,里面有每一重天神兽的攻略。只要修炼者仔细钻研里面的每一篇,对九重天了如指掌之后,冲破这九重天也是易如反掌的。


今天为大家带来第三重天的攻略!

image.png


1.🌀栈的基础知识

栈(Stack):只允许在一端进行插入或删除的线性表

栈有两个概念是:栈底和栈顶。一般栈只允许在栈顶进行操作,这样就会让栈有一个特性——后进先出(LIFO:Last in, First out结构)。


栈的重要知识:


(1)栈是函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配;

(2)栈内存特点,数据一执行完毕,变量会立即释放,节约内存空间;

(3)栈内存中的数据,没有默认初始化值,需要手动设置;


1.1 入栈


入栈push:入栈也称压栈,指的是栈的插入操作,在栈顶位置插入新的数据元素。


下图从左到右依次是入栈操作演示:


image.png

1.2 出栈


出栈pop:出栈也称弹栈,指的是栈的删除操作,删除栈顶位置的数据元素。


下图从左到右依次是出栈操作演示:

image.png

综合两幅图,我们就可以来看出来栈后进先出的操作。


2.🌀Java中实例化栈

在Java中为栈特地实现了一个类为Stack,它实现了一个标准的后进先出的栈,也定义了自己的一些方法。


初始化一个栈的写法:E表示数据类型,stack是你给栈起的名字。


Stack  stack = new Stack();


我们在声明Stack类对象之后就可以使用,类的实例化stack来调用Stack自带的一些方法了。这些方法我们后面会仔细讲到了。


但是我们在做题中需要用到栈结构时一般不使用Stack来实现栈结构。这是为什么呢?这又是谁说的呢?


其实这并不是某个Java大佬或者大家总结出来的习惯,而是Java的官方文档中自己提出来的。


我们可以在Java官网中查一下Stack得到以下结果:

image.png

我们对上面一大段英文进行翻译:


Stack类表示后进先出(LIFO)对象堆栈。它通过五个操作扩展了类向量,允许将向量作为堆栈处理。提供了常见的推送和弹出操作,以及一种查看堆栈顶部项目的方法、一种测试堆栈是否为空的方法,以及一种搜索堆栈中某个项目并发现其距离顶部有多远的方法。首次创建堆栈时,它不包含任何项。


Deque接口及其实现提供了一组更完整、更一致的后进先出堆栈操作,应该优先于此类使用。例如:


Deque stack = new ArrayDeque();


所以我们一般在做题或者使用到栈结构的时候一般都是使用下面形式来实例化栈的。


Deque stack = new ArrayDeque ();


那么具体为什么不使用Stack呢?


是因为Stack类继承于Vector类,而Vector是线程安全的,每个可能出现线程安全的方法上加了synchronized关键字,所以效率低。所以最后导致Stack的效率也比较低!

3.🌀Java中栈常用API

3.1 使用Stack类实现的栈


3.1.1 入栈


stack.push(value);


3.1.2 查看此堆栈顶部的对象,而不从堆栈中删除它


stack.peek();


3.1.3 删除此堆栈顶部的对象,并将该对象作为此函数的值返回


stack.pop();


3.1.4 判断栈是否为空,返回一个boolean类型的值


stack.empty();


3.2 使用Deque类实现的栈


官方推荐的是使用Deque的实现子类ArrayDeque来实现栈,不过平时我们使用到LinkedList这个实现子类也比较多。


Deque stack = new ArrayDeque();

Deque stack = new LinkedList();


ArrayDeque与LinkedList都实现了Deque接口,区别:


(1)它们的底层数据结构不同,ArrayDeque是基于数组,LinkedList是基于链表,ArrayDeque默认长度为16,当容量不够需要扩容(两倍),其扩容很费时间(需要复制原数组到新数组)。所以未知数据量时,尽量使用LinkedList

(2)ArrayDeque适合频繁访问,LinkedList适合频繁删除


ArrayDeque与LinkedList对应实现栈的方法:


ArrayDeque与LinkedList本身也有push(e),pop(),peek() 方法,功能与Stack中的一致。


在ArrayDeque与LinkedList中,isEmpty()与Stack中的empty()方法对应。


4.🌀Java实现栈

栈底层有两种结构来实现:数组与链表。那么我们自己能否使用数组与链表来实现一个栈呢?


这当然是可以的,接下来我们就分别使用数组与链表来实现栈结构。


4.1 用数组实现栈

public class MyStack {
    //定义栈的大小
    private int maxStack;
    //底层使用数组来实现数组
    private int[] stack;
    //表示栈顶所在的位置,默认情况下如果没有数据时,使用-1
    private int top = -1;
  //构造函数
    public MyStack(int maxStack) {
        this.maxStack = maxStack;
        stack=new int[maxStack];
    }
    //判断是否已经满栈
    public boolean isFull(){
        return this.top == this.maxStack-1;
    }
    //判断栈是否是空栈
    public boolean isEmpty(){
        return  this.top == -1;
    }
  //入栈
    public void push(int val){
        //在压栈之前要先判断是否栈满
        if (isFull()){
            throw  new RuntimeException("栈已满!");
        }
        top++;//指针到下一个位置
        stack[top]=val;//添加元素
    }
    //出栈
    public int pop(){
        //在弹栈之前要先判断是否为空栈
        if (isEmpty()){
            throw new RuntimeException("栈为空");
        }
        int value= stack[top];
        top--;
        return value;
    }
    // 查看栈顶元素,不删除
    public int peek(){
        if (isEmpty()){
            throw new RuntimeException("栈为空");
        }
        int value= stack[top];
        return value;
  }
    //查看栈中所有元素
    public void list(){
        System.out.println(Arrays.toString(stack));
    }
}
4.2 用链表实现栈
//定义链表节点,即每一个栈元素
class Node{
    public int val;
    public Node next;
    public Node(int val){
        this.val=val;
    }
}
public class MyStack{
  //底层使用链表实现栈
    Node head=null;
    //默认构造方法
    public MyStack(){
    }
    // 入栈
    public int push(int val){
        Node node=new Node(val);
        node.next = head;
        head = node;
        return head.val;
    }
    // 出栈
    public int pop(){
        if(empty()){
            throw new RuntimeException("栈为空");
        }
        int val=head.val;
        head=head.next;
        return val;
    }
    // 查看栈顶元素,不删除
    public int peek(){
        if(empty()){
            throw new RuntimeException("栈为空");
        }
        return head.val;
    }
    // 判断栈是否为空
    public boolean empty(){
        return head==null;
    }
}



5.🌀栈进阶练习

Leetcode 20. 有效的括号

微信图片_20220523225223.png

题解:


栈先入后出特点恰好与本题括号排序特点一致,即若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空则返回true。

class Solution {
    public boolean isValid(String s) {
        Stack stack= new Stack();
        for(char c : s.toCharArray()) {
            if(c == '(' || c == '{' || c == '[') {
                stack.push(c);
            }
            else if(c == ')' && !stack.empty()) {
                if(stack.pop() != '(')
                return false;
            }else if(c == '}' && !stack.empty()) {
                if(stack.pop() != '{')
                return false;
            }else if(c == ']' && !stack.empty()) {
                if(stack.pop() != '[')
                return false;
            }else return false;
        }
        if(stack.empty()) return true;
        else return false;
    }
}

输出:

image.png


结语

恭喜你修炼到这里,你已经基本有了收服神兽栈的能力。栈也是是我们到进攻算法界最重要的能力之一。大家要多加思考它的结构原理才能更好的运用。


相关文章
|
3月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
307 9
|
3月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
48 1
|
23天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
134 77
|
4月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
59 1
|
23天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
40 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
23天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
41 9
|
23天前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
34 7
|
2月前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
51 5
|
2月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
83 5
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
92 5

热门文章

最新文章