数据结构与算法之三(栈和队列的java实现)
2017-10-18 16:26
579 查看
栈
栈在java中的实现方式是:public class Stack<E> extends Vector<E>
Vector向量可以理解为高级的数组,因为它里面通过一个Object[]数组来维护所有元素,并且实现了该数组的管理以及动态增长。所以Stack本质上就是一个数组,通过继承Vector,同时提供Stack特性的几个方法push,pop,peek等,原理比较简单。
队列
》一般队列队列的特点是先进先出(FIFO),java中已经封装好Queue类。
队列的效率:添加和移除数据项时间为O(1)。注意这里的添加是往队尾添加数据项,移除的是队头的数据项,不是同一个。
队列在java中的实现方式是:
public interface Queue<E> extends Collection<E> public interface Collection<E> extends Iterable<E>
注意这里是一个接口,也就是说可以通过实现该接口得到自己的队列。Collection接口中提供了诸如add,remove,isEmpty等方法,同时继承了Iterable可以获取迭代器。Queue中则新增了一些针对队列的一些方法。
》双端队列
双端队列顾名思义,就是可以在两端同时进行添加和移除操作的队列。在java中的实现如下
public interface Deque<E> extends Queue<E>
也是一个接口,通过继承Queue,并提供对双端的额外操作,addFirst,addLast,removeFirst,removeLast等等。这是一个综合体,因为可以操作一端,你也可以当成栈来使用,同时还是个队列。
》优先级队列
优先级队列中的元素按照关键字的值有序。数据项在插入的时候会插入到合适的位置维持队列的顺序。在java中的实现如下:
//PriorityQueue继承了AbstractQueue class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable //抽象类AbstractQueue 继承自AbstractCollection并实现Queue接口 public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E> //抽象类AbstractCollection实现了Collection接口 public abstract class AbstractCollection<E> implements Collection<E>
看了源码你会发现PriorityQueue是一个具体的类,之前的Queue等接口我们是看不到队列里的元素是什么的。只有实现接口的子类中才有。比如这里PriorityQueue中也使用Object[]数组来保存队列元素。
同时,抽象类AbstractQueue中还是只有add, remove等操作。跟优先级相关的内容肯定是在PriorityQueue中的,它是如何实现的呢?我们知道优先级队列在添加元素的时候维持顺序,所以直接看add方法。源码如下:
public boolean add(E e) { return offer(e); } //offer源码如下,我们具体看一下 public boolean offer(E e) { //排除空指针 if (e == null) throw new NullPointerException(); /**modCount的解释是The number of times this priority queue has been structurally modified,不用管*/ modCount++; //当数量超出数组大小时,通过grow动态新增数组空间 int i = size; if (i >= queue.length) grow(i + 1); //size++ size = i + 1; //如果是第一次插入数据则直接放入 if (i == 0) queue[0] = e; else //如果已经存在数据则调用siftup,这里面是某种排序 siftUp(i, e); return true; } //siftUp源码如下: private void siftUp(int k, E x) { //comparator不会陌生,如果构造器里提供了比较器就用比较器,没有就默认处理。siftUpUsingComparator与siftUpComparable算法一样就是比较器不一样 if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } //就看siftUpComparable。x是要插入的元素,k是插入前队列的长度 private void siftUpComparable(int k, E x) { //获取默认比较器,这里注意一定是可排序的元素才能使用优先级队列 Comparable<? super E> key = (Comparable<? super E>) x; //下面是一段神秘代码,但是我们知道它一定实现了某种排序 while (k > 0) { //其实就是(k-1)/2 int parent = (k - 1) >>> 1; Object e = queue[parent]; if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; }
下面我们分析一下这段神秘代码,对于我们这些算法战五渣只知道插入排序什么的,这里明显不是啊。如果我们自己按照它的程序模拟插入会发现一个奇怪的结果,有点不对劲啊。我写了个简单的例子,记录了它的储存过程。
PriorityQueue<Integer> queue = new PriorityQueue<>(); queue.add(1); System.out.println(queue.toString()); queue.add(5); System.out.println(queue.toString()); queue.add(2); System.out.println(queue.toString()); queue.add(3); System.out.println(queue.toString());
然后运行,结果如下:
[1] [1, 5] [1, 5, 2] [1, 3, 2, 5]
wtf,这排了序?兄台莫慌,会有这样的疑问很正常,因为我们的惯性思维就是排好序的就是1,2,3,4。。。然而,这里的数据结构是一个队列,不是数组,应该以队列的方式验证它的排序,这叫在其位谋其政。队列的方式就是进去的时候无序,出来的时候是有序的。怎么出来?
while(!queue.isEmpty()){ System.out.print(queue.poll()+" "); }
结果是:1 2 3 5 。完全正确
看来这段神秘代码确实有效,只不过排序的过程比较特别,刚才我们解锁的姿势不对。那么这段神秘代码是什么?其实从parent,siftUp这些字眼能看出来这是一个堆排序,建立在二叉树的基础上。
相关知识我们后面了解,今天的话题是栈和队列在java中是如何实现的。我们可以看到存储可以用数组,在存储的基础上实现各中对应操作。优先级队列底层用了堆排等等。
上一节:数据结构与算法之二(栈常见案例)
下一节:数据结构与算法之四(链表)
相关文章推荐
- 算法与数据结构-栈与队列 讲解与java代码实现
- 【Java数据结构学习笔记之三】Java数据结构与算法之队列(Queue)实现
- 【数据结构和算法】java 队列实现
- java数据结构之LinkedQueue(用链表实现的双端单向队列)
- java实现 数据结构:链表、 栈、 队列、优先级队列、哈希表
- 14-数据结构_队列-算法实现
- 数据结构C语言实现之链式队列的6种算法代码
- 严蔚敏《数据结构》中迷宫算法java实现
- java与数据结构(8)---java实现链队列
- java 实现数据结构之队列
- java实现数据结构——栈Stack与队列Queue
- 数据结构:循环队列--Java实现
- java数据结构,实现栈/队列
- java 实现数据结构之队列
- 用Python实现的数据结构与算法:队列
- C++类模板 实现循环队列的顺序存储结构算法 《数据结构》(北京科海) 部分摘抄 自己编写实现
- 数据结构的Java实现——栈和队列
- 【数据结构】之队列的java实现(一)
- 数据结构和算法设计(迷宫求解问题的栈和队列的实现)
- 【数据结构】之队列的java实现(二)