【Java数据结构】线性表之栈和队列

ops/2024/10/20 6:06:23/

栈(Stack)

简单描述

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

 使用栈的一些常见的方法

压栈、出栈、获取栈顶元素、获取栈中元素个数、检查栈是否为空

java">public class Main {public static void main(String[] args) {Stack<Integer> stack = new Stack<>();stack.push(1);//压栈stack.push(2);stack.push(3);stack.peek();//获取栈顶元素stack.pop();//删除栈顶元素stack.size();//获取栈中元素的个数stack.empty();//判断栈是否为空}
}

使用数组模拟实现栈

java">public class MyStack {private int[] elem;private int usedSize;private static final int DEFAULT_CAPACITY = 10;public MyStack() {this.elem = new int[DEFAULT_CAPACITY];}public MyStack(int capacity){this.elem = new int[capacity];}private boolean checkCapacity(){if(this.usedSize == elem.length){return true;}return false;}public void push(int val){if(this.checkCapacity()){this.elem = Arrays.copyOf(this.elem,2*this.elem.length);}this.elem[this.usedSize] = val;this.usedSize++;}public int pop() throws EmptyException {if(this.usedSize == 0){//throw new EmptyException("栈为空!");return -1;}int tmp = this.elem[this.usedSize-1];this.usedSize--;return tmp;}public int peek() throws EmptyException {if(this.usedSize == 0){//throw new EmptyException("栈为空!");return -1;}int tmp = this.elem[this.usedSize-1];return tmp;}public int size(){return this.usedSize;}public boolean empty(){return this.usedSize == 0;}
}

我们使用 usedSize 来记录栈中元素的个数,压栈的时候就在 elem[usedSize] 输入元素,出栈的时候 usedSize 减一就可以了。

为什么用数组来实现栈? 因为用数组来实现栈的时间复杂度为 O(1) 比较高效。

从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的 。

逆序输出链表

我们知道栈的一个特点是先进后出,那么先放进去的元素最后出栈。因此我们就可以达到一个目的:逆序

我们知道一个逆序的方法:递归

void printList(Node head){
   if(null != head){
       printList(head.next);
       System.out.print(head.val + " ");
  }
}

接下来我们利用栈来实现逆序:

void printList(Node head){
   if(null == head){
       return;
  }
   Stack<Node> s = new Stack<>();
   // 将链表中的结点保存在栈中
   Node cur = head;
   while(null != cur){
       s.push(cur);
       cur = cur.next;
  }

   // 将栈中的元素出栈
   while(!s.empty()){
       System.out.print(s.pop().val + " ");
  }

队列(Queue)

简单描述

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front) 。

在Java中,Queue是个接口,底层是通过链表实现的。

队列(Queue)的一些常见方法

入队、出队、获取队头元素、获取队中元素个数、判断队列是否为空

java">public class Main {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();queue.offer(1);//入队queue.offer(2);queue.offer(3);queue.poll();//删除队头元素queue.peek();//获取队头元素queue.size();//获取队中元素个数queue.isEmpty();//判断队列是否为空}
}

使用链式结构来模拟实现队列

我们模拟实现队列既可以用线性结构也可以用链式结构,接下来我们使用链式结构来模拟实现一个简单队列。

java">public class MyQueue {private int usedsize;static class ListNode{private int val;private ListNode prev;private ListNode next;public ListNode(int val){this.val = val;}}private ListNode front;//队头private ListNode rear;//队尾public void offer(int data){ListNode node = new ListNode(data);if(front == null){front = rear = node;}else{node.next = front;front.prev = node;front = node;this.usedsize++;}}public int poll(){if(front == null){System.out.println("队列没有元素!");return -1;}if(front == rear){int b = this.rear.val;front = null;rear = null;this.usedsize--;return b;}int a = this.rear.val;this.rear.prev.next = null;this.usedsize--;return a;}public int peek(){if(front == null){System.out.println("队列没有元素!");return -1;}if(front == rear){int b = this.rear.val;return b;}int a = this.rear.val;return a;}public int size(){return this.usedsize;}public boolean isEmpty(){return this.usedsize == 0;}
}

这里我们 offer 入队操作采用的是链表中的头插法,我们使用双向链表的操作使得我们可以保存尾节点的位置,利于我们可以直接进行删除操作。

循环队列

上面我们提到还可以使用线性结构来实现队列。但是这时就有一个问题了,我们使用数组来达到队列的效果,在进行删除操作的时候,尾节点慢慢向前,会造成假溢出的现象。这时我们就要引入循环队列的概念。

此时我们在进行入队和出队操作的时候就不能单纯的使用 front++ 和 rear++ 不然我们达不到循环的效果。我们应该使用 (front+1)%elem.length 和 (rear+1)%elem.length 来控制头节点和尾节点的移动。

判断队空和队满

三种方法:

  • 1. 通过添加 size 属性记录
  • 2. 保留一个位置
  • 3. 使用标记

1.我们从一开始就定义一个 size 来记录队列中元素的个数,当 size == 0 的时候队空 ,当 size == elem.length 的时候队满。

2.我们保留一个位置不放元素,当 front == rear 的时候队空,当 ((rear+1)%elem.length+1)elem.length == front 的时候队满。

3.我们使用一个 flag 来充当标记,开始的时候 front == rear 这时 flag = 0 队为空,当再次遇到 front == rear 的时候将 flag = 1 这时队满。

双端队列

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

Deque是一个接口,使用时必须创建LinkedList的对象。

 由于该队列的特点:元素可以从队头出队和入队,也可以从队尾出队和入队。因此双端队列既可以充当栈来使用也可以充当普通队列来使用。

 在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。


http://www.ppmy.cn/ops/87610.html

相关文章

【C++】关联容器探秘:Map与Multimap详解

目录 1.映射类 map 0. 引入 pair&#xff1a; 1.定义 2.插入 3. 遍历 4.❗operator[]的实现 5. 插入 运用 2.Multimap 类 0. 引入&#xff1a;不去重的 Multi 1. Multimap 不支持 Operator[] 2. Multimap 的删除 1.映射类 map 0. 引入 pair&#xff1a; 在C中&…

启动pip或ipython提示Fatal error in launcher: Unable to create process的解决方法

错误以及原因分析 有时&#xff0c;运行python的pip或ipython组件会报错&#xff1a; Fatal error in launcher: Unable to create process using "C:\third_party\Python\3.9\win64-msvc-14.2\python.exe" 错误信息最后这一串路径在我们的电脑上很可能并不存在&…

Python从0到100(四十):Web开发简介-从前端到后端(文末免费送书)

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

python-爬虫实例(5):将进酒,杯莫停!

目录 前言 将进酒&#xff0c;杯莫停&#xff01; 一、浇给 二、前摇 1.导入selenium库 2.下载浏览器驱动 三、爬虫四步走 1.UA伪装 2.获取url 3.发送请求 4.获取响应数据进行解析并保存 总结 前言 博主身为一个农批&#xff0c;当然要尝试爬取王者荣耀的东西啦。 将进…

C++ :内联函数inline|nullptr

欢迎来到HarperLee的学习笔记&#xff01; 博主主页传送门&#xff1a;HarperLee博客主页&#xff01; 欢迎交流学习&#xff01; 一、inline关键字 1.1 什么是内联函数&#xff1f; 内联函数&#xff1a;用** inline 修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用…

【Python核心数据结构探秘】:元组与字典的完美协奏曲

文章目录 &#x1f680;一、元组⭐1. 元组查询的相关方法❤️2. 坑点&#x1f3ac;3. 修改元组 &#x1f308;二、集合⭐1. 集合踩坑❤️2. 集合特点&#x1f4a5;无序性&#x1f4a5;唯一性 ☔3. 集合&#xff08;交&#xff0c;并&#xff0c;补&#xff09;&#x1f3ac;4. …

【python】最新版抖音js逆向拿到数据,非常详细教程(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

【前端新手小白】学习Javascript的【开源好项目】推荐

目录 前言 1 项目介绍 1.1 时间日期类 1.2 网页store类 1.3 事件类 1.4 Number类 1.5 String类 1.6 正则验证类 1.7 ajax类 1.8 data数据类 1.9 browser浏览器类 2 学习js-tool-big-box开源项目时有哪些收获 2.1 你可以这样做 2.2 如果你需要使用本项目 2.3 你…