摘要:一前言上一篇已经讲过了链表实现单向链表了,它跟数组都是线性结构的基础,本文主要讲解线性结构的应用栈和队列如果写错的地方希望大家能够多多体谅并指正哦,如果有更好的理解的方式也希望能够在评论下留言,让大家学习学习二数据结构栈就是这么简单数据结构
一、前言
上一篇已经讲过了链表【Java实现单向链表】了,它跟数组都是线性结构的基础,本文主要讲解线性结构的应用:栈和队列
如果写错的地方希望大家能够多多体谅并指正哦,如果有更好的理解的方式也希望能够在评论下留言,让大家学习学习~
二、数据结构【栈】就是这么简单 2.1数据结构【栈】介绍数据结构的栈长的是这个样子:
其实非常好理解,我们将栈可以看成一个箱子
往箱子里面放东西叫做入栈
往箱子里面取东西叫做出栈
箱子的底部叫做栈底
箱子的顶部叫做栈顶
说到栈的特性,肯定会有一句经典的言语来概括:先进后出(LIFO, Last In First Out)
往箱子里边放苹果,箱子底部的苹果想要拿出来,得先把箱子顶部的苹果取走才行
2.2数据结构【栈】 代码实现栈的分类有两种:
静态栈(数组实现)
动态栈(链表实现)
从上一篇写链表我就认知到我的算法是有多渣了,普通的单链表操作也能把我绕得晕晕的。
由于我的链表还不是很熟,栈又不是很难,那么我就用链表来创建动态栈了!
既然是用链表,我们还是把上一篇节点的代码拿过来吧:
public class Node { //数据域 public int data; //指针域,指向下一个节点 public Node next; public Node() { } public Node(int data) { this.data = data; } public Node(int data, Node next) { this.data = data; this.next = next; } }
要链表用来表示栈,这次会有两个指针:
栈顶
栈底
public class Stack { public Node stackTop; public Node stackBottom; public Stack(Node stackTop, Node stackBottom) { this.stackTop = stackTop; this.stackBottom = stackBottom; } public Stack() { } }2.2.1进栈
将原本栈顶指向的节点交由新节点来指向,栈顶指向新加入的节点
/** * 进栈 * * @param stack 栈 * @param value 要进栈的元素 */ public static void pushStack(Stack stack, int value) { // 封装数据成节点 Node newNode = new Node(value); // 栈顶本来指向的节点交由新节点来指向 newNode.next = stack.stackTop; // 栈顶指针指向新节点 stack.stackTop = newNode; }2.2.2遍历栈
只要栈顶元素的指针不指向栈底,那么就一直输出遍历结果:
/** * 遍历栈(只要栈顶指针不指向栈底指针,就一直输出) * * @param stack */ public static void traverse(Stack stack) { Node stackTop = stack.stackTop; while (stackTop != stack.stackBottom) { System.out.println("关注公众号:Java3y:" + stackTop.data); stackTop = stackTop.next; } }
测试:
public static void main(String[] args) { //初始化栈(无元素) Stack stack = new Stack(new Node(), new Node()); //栈顶和栈尾是同一指向 stack.stackBottom = stack.stackTop; //指向null stack.stackTop.next = null; //进栈 pushStack(stack, 3); pushStack(stack, 4); pushStack(stack, 5); traverse(stack); }
结果:
这就符合了先进后出的特性了~
2.2.3判断该栈是否为空很简单,只要栈顶和栈底是同一指向,那么该栈就为空
/** * 判断该栈是否为空 * * @param stack */ public static void isEmpty(Stack stack) { if (stack.stackTop == stack.stackBottom) { System.out.println("关注公众号:Java3y---->该栈为空"); } else { System.out.println("关注公众号:Java3y---->该栈不为空"); } }2.2.4出栈
在出栈之前看看该栈是否为空,不为空才出栈...
将栈顶的元素的指针(指向下一个节点)赋值给栈顶指针(完成出栈)
/** * 出栈(将栈顶的指针指向下一个节点) * @param stack */ public static void popStack(Stack stack) { // 栈不为空才能出栈 if (!isEmpty(stack)) { //栈顶元素 Node top = stack.stackTop; // 栈顶指针指向下一个节点 stack.stackTop = top.next; System.out.println("关注公众号:Java3y---->出栈的元素是:" + top.data); } }
测试出栈:
多次出栈:
2.2.5清空栈当时学C的时候需要释放内存资源,可是Java不用呀,所以栈顶指向栈底,就清空栈了
/** * 清空栈 * @param stack */ public static void clearStack(Stack stack) { stack.stackTop = null; stack.stackBottom = stack.stackTop; }三、数据结构【队列】就是这么简单
数据结构的队列长的是这个样子:
其实队列非常好理解,我们将队列可以看成小朋友排队
队尾的小朋友到指定的地点了-->出队
有新的小朋友加入了-->入队
相对于栈而言,队列的特性是:先进先出
先排队的小朋友肯定能先打到饭!
队列也分成两种:
静态队列(数组实现)
动态队列(链表实现)
这次我就使用数组来实现静态队列了。值得注意的是:往往实现静态队列,我们都是做成循环队列
做成循环队列的好处是不浪费内存资源!
3.1数据结构【队列】 代码实现这次我们使用的是数组来实现静态队列,因此我们可以这样设计:
public class Queue { //数组 public int [] arrays; //指向第一个有效的元素 public int front; //指向有效数据的下一个元素(即指向无效的数据) public int rear; }
从上面的设计我们可以发现:rear并不指向最后一个有效的元素,在循环队列中这样设计是非常方便的!因为这样设计可以让我们分得清队头和队尾(不然循环队列不断入队或出队,位置是变化很快的)
由于我们是循环队列,所以front和rear值会经常变动,我们得把front和rear的值限定在一个范围内,不然会超出队列的长度的。
有这么一个算法:rear=(rear+1)%数组长度
比如rear的下标是2,数组的长度是6,往后面移一位是3,那么rear = (rear+1) % 6,结果还是3
3.1.2初始化队列此时队列为空,分配了6个长度给数组(只能装5个实际的数字,rear指向的是无效的位置的)
public static void main(String[] args) { //初始化队列 Queue queue = new Queue(); queue.front = 0; queue.rear = 0; queue.arrays = new int[6]; }3.1.3判断队列是否满了
如果rear指针和front指针紧挨着,那么说明队列就满了
/** * 判断队列是否满了,front和rear指针紧挨着,就是满了 * @param queue * @return */ public static boolean isFull(Queue queue) { if ((queue.rear + 1) % queue.arrays.length == queue.front) { System.out.println("关注公众号:Java3y--->此时队列满了!"); return true; } else { System.out.println("关注公众号:Java3y--->此时队列没满了!"); return false; } }3.1.4入队
判断该队列是否满了
入队的值插入到队尾中(具体的位置就是rear指针的位置【再次声明:rear指向的是无效元素的位置】
rear指针移动(再次指向无效的元素位置)
/** * 入队 * * @param queue */ public static void enQueue(Queue queue,int value) { // 不是满的队列才能入队 if (!isFull(queue)) { // 将新的元素插入到队尾中 queue.arrays[queue.rear] = value; // rear节点移动到新的无效元素位置上 queue.rear = (queue.rear + 1) % queue.arrays.length; } }3.1.5遍历
只要front节点不指向rear节点,那么就可以一直输出
/** * 遍历队列 * @param queue * */ public static void traverseQueue(Queue queue) { // front的位置 int i = queue.front; while (i != queue.rear) { System.out.println("关注公众号:Java3y--->" + queue.arrays[i]); //移动front i = (i + 1) % queue.arrays.length; } }
队列没满时:
队列已满了就插入不了了(验证上面的方法是否正确):
3.1.6判断该队列是否为空只要rear和front指针指向同一个位置,那该队列就是空的了
/** * 判断队列是否空,front和rear指针相等,就是空了 * @param queue * @return */ public static boolean isEmpty(Queue queue) { if (queue.rear == queue.front) { System.out.println("关注公众号:Java3y--->此时队列空的!"); return true; } else { System.out.println("关注公众号:Java3y--->此时队列非空!"); return false; } }3.1.7出队
出队的逻辑也非常简单:
判断该队列是否为null
如果不为null,则出队,只要front指针往后面移就是出队了!
/** * 出队 * * @param queue */ public static void outQueue(Queue queue) { //判断该队列是否为null if (!isEmpty(queue)) { //不为空才出队 int value = queue.arrays[queue.front]; System.out.println("关注公众号:Java3y--->出队的元素是:" + value); // front指针往后面移 queue.front = (queue.front + 1) % queue.arrays.length; } }
结果:
四、总结数据结构的栈和队列的应用非常非常的多,这里也只是最简单的入门,理解起来也不困难。
栈:先进后出
队列:先进先出
关于数据结构这方面我就到暂时到这里为止了,都简单的入个门,以后遇到更加复杂的再继续开新的文章来写~毕竟现在水平不够,也无法理解更深层次的东西~数据结构这东西是必备的,等到研究集合的时候还会来回顾它,或者遇到新的、复杂的也会继续学习....
想要更加深入数据结构的同学就得去翻阅相关的书籍咯~这仅仅是冰山一角
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71049.html
摘要:在改进前使用数组的一个缺点是必须声明数组的大小,所以栈有确定的容量。待解决的问题建立一个能够增长或者缩短到任意大小的栈。下边的图是观察时间开销的另一种方式,表示了入栈操作需要访问数组的次数。 前言 上一篇:算法分析下一篇:基本排序 本篇内容主要是栈,队列 (和包)的基本数据类型和数据结构文章里头所有的对数函数都是以 2 为底关于性能分析,可能还是需要一些数学知识,有时间可以回一下在很多...
摘要:的前部分内容讲的是栈和队列的实现。学习环境在学习这门课之前,先引入的概念,即抽象数据类型。链表实现学习,链表实现简单的数组实现链表实现简单的数组实现解决使用栈或者队列时,的数据类型指定问题。 Week2 的前部分内容讲的是栈和队列的Java实现。学习环境:mac, inteliJ, java version 1.8.0_77 在学习这门课之前,先引入Abstract Data Type...
摘要:在这种情况下,是以其为根的树的最后一个结点。来源二总结底层是红黑树,能够实现该集合有序如果在构造方法中传递了对象,那么就会以对象的方法进行比较。 前言 声明,本文用得是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码剖析】 LinkedHashMap就这么简单【源码剖析】 本...
摘要:栈队列双端队列都是非常经典的数据结构。结合了栈和队列的特点。因此,在中,有栈的使用需求时,使用代替。迭代器之前源码源码之与字段中分析过,容器的实现中,所有修改过容器结构的操作都需要修改字段。 栈、队列、双端队列都是非常经典的数据结构。和链表、数组不同,这三种数据结构的抽象层次更高。它只描述了数据结构有哪些行为,而并不关心数据结构内部用何种思路、方式去组织。本篇博文重点关注这三种数据结构...
摘要:下面总结一下集合常用的三个子类吧无序,允许为,底层是散列表红黑树,非线程同步有序,不允许为,底层是红黑树非线程同步迭代有序,允许为,底层是双向链表,非线程同步从结论而言我们就可以根据自己的实际情况来使用了。 前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单【源码剖析】 Map集合、散列表、红黑树介绍 HashMap就是这么简单【源码...
阅读 2232·2021-11-22 09:34
阅读 1984·2021-09-22 15:22
阅读 1963·2019-08-29 15:05
阅读 2080·2019-08-26 10:43
阅读 3388·2019-08-26 10:26
阅读 836·2019-08-23 18:29
阅读 3498·2019-08-23 16:42
阅读 1976·2019-08-23 14:46