Data Structures (三) - 栈Stack实现

简介: Data Structures (三) - 栈Stack实现

一、栈的基本概念

栈也是线性表的一种,但是与其他线性表不同的是,栈分为栈顶和栈底且只允许从栈顶进行操作,即入栈(push)或者出栈(pop)操作,所以栈的操作遵循后进先出的原则(Last In First Out)

1e68e4d8d3aa423fbe71aa50c1c670b4_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

由于栈的特殊性,它的方法也就与其他线性表相对来说会少一些,但是栈的本质也是数组,栈中的数据也是连续的,拥有size属性,只不过只有一端可以操作,主要包含下面这些方法

int size(); //获取栈中元素的数量
boolean isEmpty(); // 判断栈是否为空
void push(T element); // 入栈操作
T pop(); // 出栈操作
T top(); //获取栈顶的元素
void clear(); // 清空栈中的元素
复制代码

二、栈的实现

栈的内部可以使用动态数组实现,即将动态数组作为栈的私有属性,如果继承动态数组的话,就不符合只能从栈顶操作栈的元素特性了。 在data-structures项目中新增一个Module 04-栈,新增package com.citi.stack,新增栈实体类Stack,并且将之前实现过数据结构中的List接口、AbstractList抽象类、ArrayList动态数组拷贝到citi包下面的list包中,将utils包拷贝到citi包下。

public class Stack<T> {
    // 私有属性ArrayList
    private List<T> list = new ArrayList<T>();
    public int size(){
        return null;
    }
    public boolean isEmpty(){
        return false;
    }
    public void push(T element){
    }
    public T pop(){
        return null
    }
    public T top(){
        return null;
    }
}
复制代码

size(),栈是基于动态数组ArrayList的,栈的size就是动态数组的size

public int size(){
    return list.size();
}
复制代码

isEmpty(),同样如果ArrayList为空,那么栈也为空,栈的数据存储在私有属性ArrayList中

public boolean isEmpty(){
    return list.isEmpty();
}
复制代码

push(),栈的栈顶相当于动态数组的尾部,从栈顶加入元素即动态数组中从数组尾部添加元素,直接调用动态数组的add方法

public void push(T element){
    list.add(element);
}
复制代码

pop(),从栈顶删除元素即动态数组删除末尾元素,调用remove方法

public T pop(){
    return list.remove(list.size() - 1);
}
复制代码

top(),获取栈顶元素即获取动态数组的尾部的元素,调用get方法,索引为size-1

public T top(){
    return list.get(list.size() - 1);
}
复制代码

clear(),调用动态数组中的clear方法既可以清空栈中的元素

public void clear(){
    list.clear();
}
复制代码

增加栈的测试类StackTest

public class StackTest {
    Stack stack = null;
    @Before
    public void init(){
        stack = new Stack<>();
        stack.push(11);
        stack.push(10);
        stack.push(20);
        System.out.println("Init Stack " + stack);
    }
    @Test
    public void push() {
        stack.push(30);
        System.out.println("After Push " + stack);
    }
    @Test
    public void pop() {
        stack.pop();
        System.out.println("After Pop " + stack);
    }
    @Test
    public void top() {
        System.out.println("Get Top " + stack.top());
    }
}
复制代码

在Stack中增加toString()方法,也是调用动态数组的toString()方法

@Override
public String toString() {
    return list.toString();
}
复制代码

执行所有的测试方法,即可验证Stack

三、栈的应用

由于栈只能从栈顶操作元素的特性,所有凡是包含了前进后退、恢复撤销等功能的实现都是利用了栈的数据特性,例如浏览器的前进后退功能就可以概括为是由两个栈数据结构实现的。

首先在浏览器中依次访问page1、page2、page3,就相当于依次将page1、page2、page3三个页面push到第一个栈中,第一个栈的栈顶元素就是浏览器当前显示的页面,浏览器最后访问的page3,浏览器当前显示的页面也是page3。

ab25606e3855436f8bc0a1fbb57295ec_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

此时点击后退按钮,相当于把第一个栈中栈顶元素pop弹出并push到第二个栈中,而第一个栈的栈顶元素就变成了page2,所有浏览器显示的页面为page2;再点击一次后退按钮,又把第一个栈的栈顶元素弹出并push进第二个栈,这是第一个栈的栈顶元素为page1,此时浏览器显示的页面为page1;若点击前进按钮,相当于将第二个栈的栈顶元素page2弹出并push进第一个栈作为栈顶元素,此时浏览器的现实的页面为page2

05453d6eb6ce4b8dacb02f859314220e_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

四、有效的括号

LeetCode第20题:有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s,判断字符串是否有效

class Solution {
public boolean isValid(String s) {
    }
}
复制代码

利用Stack的数据特性解决: 首先遍历字符串,如果遇到左字符(括号的左边部分)就push到栈中,如果遇到右字符就将由字符与栈顶元素比较,如果一致就弹出该元素,直到最后如果栈为空,说明是有效的括号,如果栈不为空,说明不是有效的括号,如果遇到右字符时栈为空,那么也是无效的括号。

首先定义一个栈,用来保存左字符

// 定义一个栈
Stack<Character> stack = new Stack<>();
复制代码

接着遍历字符串,将遇到的左字符放入栈中,如果遇到右字符串,首先判断栈是否为空,栈为空返回false,接着取出栈顶元素,判断栈顶元素与遍历到的右字符是否为一对,否则返回false

for (int i = 0; i < len; i++) {
    char c = s.charAt(i);
    if (c == '(' || c == '[' || c == '{'){
        // 入栈
        stack.push(c);
    } else { // 有括号
        // 判断栈是否为空
        if (stack.isEmpty()) return false;
        // 弹出栈顶元素
        char left = stack.pop();
        // 判断栈顶元素与遍历到的右字符是否是一对
        if (left == '(' && c != ')') return false;
        if (left == '[' && c != ']') return false;
        if (left == '{' && c != '}') return false;
    }
}
复制代码

最后判断栈是否为空

return stack.isEmpty();
复制代码

完整代码

public boolean isValid(String s) {
    // 定义一个栈
    Stack<Character> stack = new Stack<>();
    int len = s.length();
    for (int i = 0; i < len; i++) {
        char c = s.charAt(i);
        if (c == '(' || c == '[' || c == '{'){
            // 入栈
            stack.push(c);
        } else { // 有括号
            // 判断栈是否为空
            if (stack.isEmpty()) return false;
            // 弹出栈顶元素
            char left = stack.pop();
            // 判断栈顶元素与遍历到的右字符是否是一对
            if (left == '(' && c != ')') return false;
            if (left == '[' && c != ']') return false;
            if (left == '{' && c != '}') return false;
        }
    }
    return stack.isEmpty();
}


相关文章
|
4天前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
1月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
142 77
|
1月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
38 7
|
1月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
44 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
1月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
46 9
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
99 5
|
3月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
72 0
|
3月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
54 1
|
3月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
116 21
|
3月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。

热门文章

最新文章