01玩转数据结构_03_栈和队列

栈Stack:

栈的概述:

虽然看起来它只是数组的子集,但是栈的这种结构对于计算机中组建逻辑有这非常非常大的作用。

入栈 出栈:

栈是一种先进后出(FILO First in Last Out )的结构。

栈的应用01:撤销操作:

栈的应用02:程序调用的系统栈:

了解它有助于我们更加理解递归!!! 

此时的系统栈中会记录下:

A2的意思是执行到A函数的第二行了。

同样系统栈中会继续压入!!!

当C 函数执行完毕之后,我们的计算机就不知道该执行谁了,

它此时会看系统栈中的内容,

发现是B2 ,所以开始从B2继续执行。(同时会弹出B2)

当B函数也执行完毕时候,计算机又会看下系统栈,发现是A2

所以,它就会从A2开始执行。(同时会弹出A2)

当A函数也执行完毕的时候,系统栈中为空。所以函数执行完毕!!!

这个过程对理解递归有很大的帮助,后面会有关于递归的内容。 

栈的实现:

栈基本的五种操作:

其中peek () 有的 语言中 喜欢叫它top()  !

这里说一种 栈的底层实现(另外一种后面会说):

后面的队列会说两种不同的底层实现。

代码设计方式:

先设计一个接口,然后用ArrayStack 去实现它。

具体代码如下:

从头写一个自己的栈出来(主要利用数组,将数组封装成一个栈。)!!!注:数组也是我们自定义的可以动态改变长度的数组!

 1 package cn.zcb.demo03;
 2 
 3 public interface MyInterface<T> {
 4     /*接口也是支持泛型的*/
 5     public abstract void push(T a);
 6     public abstract T pop();
 7     public abstract T peek();
 8     public abstract int getSize();
 9     public abstract boolean isEmpty();
10 }
MyInterface.java
 1 package cn.zcb.demo03;
 2 
 3 public class MyStack<T> implements MyInterface<T> {
 4     /*
 5     * 我们自己的栈  。
 6     * */
 7     //它的内部用 我们之前自己写的数组 来做 。
 8     private MyArray <T> array;  //MyArray 是我们自己写的数组类。
 9     //构造函数
10     public MyStack(int capacity){
11         array = new MyArray<>(capacity);
12     }
13     public MyStack(){
14         array = new MyArray<>();
15     }
16 
17     public void push(T a){
18         array.addLast(a);
19     }
20     public T pop(){
21         return array.removeLast();
22     }
23     public T peek(){
24         return array.getByIndex(array.getSize()-1); //栈顶元素
25     }
26     public  int getSize(){
27         return array.getSize();
28     }
29     public  boolean isEmpty(){
30         return array.isEmpty();
31     }
32     //下面的方法不是  接口中的方法 !!!
33     public int getCapacity(){
34         return array.getCapacity();
35     }
36 
37     //为了打印方便 ,这里重写Object 中的toString
38     @Override
39     public String toString(){
40         StringBuilder builder = new StringBuilder();
41         builder.append("Stack's data is below : 
");
42         builder.append("[");
43         for (int i=0;i< array.getSize();i++){
44             builder.append(array.getByIndex(i));
45             if(i != array.getSize()-1){
46                 builder.append(" ,");
47             }
48         }
49         builder.append("] top");
50         return builder.toString();
51     }
52 
53 
54 }
MyStack
  1 package cn.zcb.demo03;
  2 
  3 public class MyArray<T> {
  4     private T[] data; //数组
  5     private int size;  //数组实际大小,
  6     //容量不用写,可以由data.length()得知
  7 
  8     //满参构造函数
  9     public MyArray(int capacity){
 10 //        this.data  = new int[capacity];
 11 //        this.data  = new T[capacity];  //不支持这样
 12         this.data =  (T[]) new Object[capacity];  //先new 出个 Object[]  类的,然后转为 T[]
 13         this.size = 0; //开始时 实际大小size =0
 14     }
 15     //空参构造器
 16     public MyArray(){
 17         this(10);  //如果是空参的话,调用有参构造器。默认是10
 18     }
 19 
 20     //获取数组中的元素的个数
 21     public int getSize(){
 22         return this.size;
 23     }
 24 
 25     //获取数组的容量
 26     public int getCapacity(){
 27         return this.data.length;
 28     }
 29 
 30     //数组中是否为空
 31     public boolean isEmpty(){
 32         return this.size ==0;
 33     }
 34 /////////////////////增////////////////////////////
 35     //向 数组的末尾添加一个新元素
 36     public void addLast(T num){
 37         /*
 38         if(size == this.data.length){
 39             //此时开辟的空间已经满了
 40             //这里先抛出一个异常
 41             throw new IllegalArgumentException("addLast failed.数组已经满了");
 42         }
 43         this.data[size] = num;
 44         size ++;  //要维持数组  指针。 size 是当前数组中的个数。
 45         */
 46         //此时其实可以直接复用 insert ()函数
 47         insert(this.size,num);
 48     }
 49     // 向数组头添加元素 (直接复用  向指定位置添加元素的函数 )
 50     public void addFirst(T num){
 51         insert(0,num);
 52     }
 53 
 54     //向 指定数组位置添加一个新元素
 55     public void insert(int idx,T num){
 56         if(this.size == this.data.length){
 57 //            throw new IllegalArgumentException("数组已经放满了");
 58             /*此时对其扩容*/
 59             resize(2*this.data.length);
 60 
 61 
 62         }
 63         if(idx <0 || idx > this.size )
 64             throw new IllegalArgumentException("索引非法");
 65 
 66         for(int i=this.size-1;i >= idx;--i){
 67             this.data[i+1] = this.data[i];
 68         }
 69         this.data[idx] = num;
 70         //更新size
 71         this.size ++;
 72     }
 73 
 74 /////////////////////////////////////////////////////////////
 75     //删除第一个
 76     public T removeFirst(){
 77         return remove(0);  //删除并将之返回出去
 78     }
 79 
 80     //删除最后一个
 81     public T removeLast(){
 82         return remove(this.size-1);
 83     }
 84 
 85     //删除指定位置  的元素
 86     public T remove(int idx){  //返回 删除的元素
 87         if(idx < 0 || idx > this.size -1)
 88             throw new IllegalArgumentException("idx错误!");
 89         T temp = this.data[idx];
 90         for (int i =idx;i<this.size - 1;i++){
 91             this.data[i] = this.data[i+1];
 92         }
 93         this.size -- ;
 94 
 95         //动态减少   当数组中的元素少于空间的四分之一的时候,缩减空间为原来的一半(这样可以有效的防止 复杂度震荡问题)。
 96         if(this.size == this.data.length / 4 && this.data.length/2 != 0 ){  //第二个条件是防止 length =1 的时候
 97             resize(this.data.length/2);
 98         }
 99         return temp;
100     }
101 
102     //删除 某个元素 不根据其位置  //它之所以返回void 是因为调用着已经知道的element ,所以没必要再返回出来了。
103     public void removeElement(T element){
104         //首先要 寻找是否有 element
105         int idx = find(element);
106         if(idx == -1){
107             throw new IllegalArgumentException("没有该元素");
108         }else{
109             remove(idx);
110         }
111     }
112 
113 /////////////////////////////////////////////////////////////
114     //修改索引为 idx 的元素为 num
115     public void setByIndex(int idx,T num){
116         if(idx <0 || idx > this.size)
117             throw new IllegalArgumentException("idx 错误!");
118 
119         this.data[idx] = num;
120     }
121 
122 
123 /////////////////////////////////////////////////////////////
124     //获取整个数组的概括
125     //重写 toString()  当打印arr 时候,就会调用该方法!
126     @Override
127     public String toString(){
128         StringBuilder res = new StringBuilder();
129         res.append(String.format("Array:size = %d,capacity = %d 
",this.size,this.data.length));
130         res.append('[');
131         for (int i =0 ;i<this.size;i++){
132             res.append(data[i]);
133             if(i != this.size -1)
134                 res.append(", ");
135         }
136         res.append(']');
137         return res.toString();
138     }
139     //获取idx 索引位置的 元素
140     public T getByIndex(int idx){
141         if (idx < 0 || idx >this.size) {
142             throw new IllegalArgumentException("索引传入错误!");
143         }
144         return this.data[idx];
145     }
146 
147 //////////////////////包含///////////////////////////////////////
148     public boolean contains(T num){
149         for (int i=0;i<this.size;i++){
150             if(this.data[i] == num){
151                 return true;
152             }
153         }
154         return false;
155     }
156 //////////////////////寻找////////////////////////////////////
157     public int find(T num){
158         //寻找数组中是否有 num  ,如果有返回其索引,反之 返回-1
159         for (int i =0;i<this.size;i++){
160             if(this.data[i] == num){
161                 return i;
162             }
163         }
164         return -1;
165     }
166 //////////////////////实现动态数组  resize()////////////////////////////////////
167     private void resize(int new_capacity){  //之所以  用 private 是不让用户调用, 数组如果空间不足会自己扩展的。
168         T[] new_data =(T[]) new Object[new_capacity];
169 
170         for (int i =0;i<this.size;i++){
171             new_data[i] = this.data[i];
172         }
173         //
174         this.data = new_data;  //此时新数组的size 不变 , 新数组的length 也不用变
175     }
176 }
MyArray
 1 package cn.zcb.demo03;
 2 
 3 public class Test {
 4     public static void main(String[] args) {
 5         MyStack<Integer> stk = new MyStack();  //也可以给定容量 new MyStack(20);
 6         for (int i = 0;i<5;i++){
 7             //模拟5次入栈操作
 8             stk.push(i);
 9             System.out.println(stk);
10         }
11         //一次出栈操作
12         stk.pop();
13         System.out.println(stk);
14     }
15 }
Test.java

  

这样我们就写了一个基于动态数组而实现的栈!!!

(封装动态数组使之成为一个栈!)

栈的另一个应用:括号匹配:

通过Leetcode 中的第20题来说,

 1 package cn.zcb.demo04;
 2 
 3 import java.util.Stack;
 4 
 5 public class Test {
 6     public boolean isValid(String s){
 7         if(s.length() == 0) return true;
 8         char c_1 = s.charAt(0);
 9         if(c_1 ==']' || c_1 =='}' || c_1 ==')'){
10             return false;
11         }
12         Stack<Character> stk = new Stack<>();
13         for (int i =0;i<s.length();i++){
14             char c = s.charAt(i);
15             if(c == '{' || c =='[' || c=='('){
16                 stk.push(c);
17             }
18             if(c =='}'){
19                 if(!stk.isEmpty() && stk.peek() =='{') {
20                     stk.pop();
21                 }else{
22                     return false;
23                 }
24             }
25             if(c ==']'){
26                 if(!stk.isEmpty() && stk.peek() =='[') {
27                     stk.pop();
28                 }else{
29                     return false;
30                 }
31             }
32             if(c ==')'){
33                 if(!stk.isEmpty() && stk.peek() =='(') {
34                     stk.pop();
35                 }else{
36                     return false;
37                 }
38             }
39         }
40         return stk.isEmpty();
41     }
42 
43 }
我自己的代码,垃圾!
 1 class Solution {
 2     public boolean isValid(String s){
 3         // if(s.length() == 0) return true;  这行用没必要有!!!
 4         Stack<Character> stk = new Stack<>();
 5         for (int i =0;i<s.length();i++){
 6             char c = s.charAt(i);
 7             if(c == '{' || c =='[' || c=='('){
 8                 stk.push(c);
 9             }else{
10                 if(stk.isEmpty()) return false;
11                 char topChar = stk.pop();  //正常匹配的时候,每次都要弹出一个的。
12                 if(c == ']' && topChar !='['){
13                     return false;
14                 }
15                 if(c == '}' && topChar !='{'){
16                     return false;
17                 }
18                 if(c == ')' && topChar !='('){
19                     return false;
20                 }
21             }
22         }
23         return stk.isEmpty();
24     }
25 }
老师的代码

队列Queue:

队列概述:

FIFO  First In First Out

下面我们将写一个基于 我们自己的数组MyArray 的队列!

代码如下:

队列也是要支持泛型!

  1 package cn.zcb.demo04;
  2 
  3 public class MyArray<T> {
  4     private T[] data; //数组
  5     private int size;  //数组实际大小,
  6     //容量不用写,可以由data.length()得知
  7 
  8     //满参构造函数
  9     public MyArray(int capacity){
 10 //        this.data  = new int[capacity];
 11 //        this.data  = new T[capacity];  //不支持这样
 12         this.data =  (T[]) new Object[capacity];  //先new 出个 Object[]  类的,然后转为 T[]
 13         this.size = 0; //开始时 实际大小size =0
 14     }
 15     //空参构造器
 16     public MyArray(){
 17         this(10);  //如果是空参的话,调用有参构造器。默认是10
 18     }
 19 
 20     //获取数组中的元素的个数
 21     public int getSize(){
 22         return this.size;
 23     }
 24 
 25     //获取数组的容量
 26     public int getCapacity(){
 27         return this.data.length;
 28     }
 29 
 30     //数组中是否为空
 31     public boolean isEmpty(){
 32         return this.size ==0;
 33     }
 34     /////////////////////增////////////////////////////
 35     //向 数组的末尾添加一个新元素
 36     public void addLast(T num){
 37         /*
 38         if(size == this.data.length){
 39             //此时开辟的空间已经满了
 40             //这里先抛出一个异常
 41             throw new IllegalArgumentException("addLast failed.数组已经满了");
 42         }
 43         this.data[size] = num;
 44         size ++;  //要维持数组  指针。 size 是当前数组中的个数。
 45         */
 46         //此时其实可以直接复用 insert ()函数
 47         insert(this.size,num);
 48     }
 49     // 向数组头添加元素 (直接复用  向指定位置添加元素的函数 )
 50     public void addFirst(T num){
 51         insert(0,num);
 52     }
 53 
 54     //向 指定数组位置添加一个新元素
 55     public void insert(int idx,T num){
 56         if(this.size == this.data.length){
 57 //            throw new IllegalArgumentException("数组已经放满了");
 58             /*此时对其扩容*/
 59             resize(2*this.data.length);
 60 
 61 
 62         }
 63         if(idx <0 || idx > this.size )
 64             throw new IllegalArgumentException("索引非法");
 65 
 66         for(int i=this.size-1;i >= idx;--i){
 67             this.data[i+1] = this.data[i];
 68         }
 69         this.data[idx] = num;
 70         //更新size
 71         this.size ++;
 72     }
 73 
 74     /////////////////////////////////////////////////////////////
 75     //删除第一个
 76     public T removeFirst(){
 77         return remove(0);  //删除并将之返回出去
 78     }
 79 
 80     //删除最后一个
 81     public T removeLast(){
 82         return remove(this.size-1);
 83     }
 84 
 85     //删除指定位置  的元素
 86     public T remove(int idx){  //返回 删除的元素
 87         if(idx < 0 || idx > this.size -1)
 88             throw new IllegalArgumentException("idx错误!");
 89         T temp = this.data[idx];
 90         for (int i =idx;i<this.size - 1;i++){
 91             this.data[i] = this.data[i+1];
 92         }
 93         this.size -- ;
 94 
 95         //动态减少   当数组中的元素少于空间的四分之一的时候,缩减空间为原来的一半(这样可以有效的防止 复杂度震荡问题)。
 96         if(this.size == this.data.length / 4 && this.data.length/2 != 0 ){  //第二个条件是防止 length =1 的时候
 97             resize(this.data.length/2);
 98         }
 99         return temp;
100     }
101 
102     //删除 某个元素 不根据其位置  //它之所以返回void 是因为调用着已经知道的element ,所以没必要再返回出来了。
103     public void removeElement(T element){
104         //首先要 寻找是否有 element
105         int idx = find(element);
106         if(idx == -1){
107             throw new IllegalArgumentException("没有该元素");
108         }else{
109             remove(idx);
110         }
111     }
112 
113     /////////////////////////////////////////////////////////////
114     //修改索引为 idx 的元素为 num
115     public void setByIndex(int idx,T num){
116         if(idx <0 || idx > this.size)
117             throw new IllegalArgumentException("idx 错误!");
118 
119         this.data[idx] = num;
120     }
121 
122 
123     /////////////////////////////////////////////////////////////
124     //获取整个数组的概括
125     //重写 toString()  当打印arr 时候,就会调用该方法!
126     @Override
127     public String toString(){
128         StringBuilder res = new StringBuilder();
129         res.append(String.format("Array:size = %d,capacity = %d 
",this.size,this.data.length));
130         res.append('[');
131         for (int i =0 ;i<this.size;i++){
132             res.append(data[i]);
133             if(i != this.size -1)
134                 res.append(", ");
135         }
136         res.append(']');
137         return res.toString();
138     }
139     //获取idx 索引位置的 元素
140     public T getByIndex(int idx){
141         if (idx < 0 || idx >this.size) {
142             throw new IllegalArgumentException("索引传入错误!");
143         }
144         return this.data[idx];
145     }
146 
147     //////////////////////包含///////////////////////////////////////
148     public boolean contains(T num){
149         for (int i=0;i<this.size;i++){
150             if(this.data[i] == num){
151                 return true;
152             }
153         }
154         return false;
155     }
156     //////////////////////寻找////////////////////////////////////
157     public int find(T num){
158         //寻找数组中是否有 num  ,如果有返回其索引,反之 返回-1
159         for (int i =0;i<this.size;i++){
160             if(this.data[i] == num){
161                 return i;
162             }
163         }
164         return -1;
165     }
166     //////////////////////实现动态数组  resize()////////////////////////////////////
167     private void resize(int new_capacity){  //之所以  用 private 是不让用户调用, 数组如果空间不足会自己扩展的。
168         T[] new_data =(T[]) new Object[new_capacity];
169 
170         for (int i =0;i<this.size;i++){
171             new_data[i] = this.data[i];
172         }
173         //
174         this.data = new_data;  //此时新数组的size 不变 , 新数组的length 也不用变
175     }
176 }
MyArray.java
1 package cn.zcb.demo04;
2 
3 public interface MyInterface_Queue <T> {
4     public abstract void enqueue(T a);  //入队
5     public abstract T dequeue();  //出队
6     public abstract T getFront();  //队头
7     public abstract int getSize();  //得到队列中的实际长度
8     public abstract boolean isEmpty();  //是否为空。
9 }
MyInterface_Queue.java
 1 package cn.zcb.demo04;
 2 public class MyQueue <T> implements MyInterface_Queue <T> {
 3 
 4     //我们自己的 队列,实际内部的结构还是数组(我们自己写的数组MyArray)
 5     private MyArray <T> array;
 6     //构造函数
 7     public MyQueue(int capacity){
 8         array = new MyArray<>(capacity);
 9     }
10     public MyQueue(){
11         array = new MyArray<>();
12     }
13 
14     public void enqueue(T a){
15         array.addLast(a);
16     }
17     public T dequeue(){
18         return array.removeFirst();
19     }
20     public T getFront(){
21         return array.getByIndex(0);
22     }
23     public int getSize(){
24         return array.getSize();
25     }
26     public boolean isEmpty(){
27         return array.isEmpty();
28     }
29     public int getCapacity(){
30         return array.getCapacity();
31     }
32     @Override
33     public String toString(){
34         StringBuilder builder = new StringBuilder();
35         builder.append("Queue's data is below: 
[");
36         for (int i =0;i<array.getSize();i++){
37             builder.append(array.getByIndex(i));
38             if(i != array.getSize() -1){
39                 builder.append(" ,");
40             }
41         }
42         builder.append("] 队尾");
43         return builder.toString();
44     }
45 }
MyQueue.java
 1 package cn.zcb.demo04;
 2 public class Test {
 3     public static void main(String[] args) {
 4         MyQueue<Integer> quu = new MyQueue<>(24); //队列中有 24 个元素
 5         for (int i =0;i<10;i++){
 6             quu.enqueue(i);
 7         }
 8         System.out.println(quu);
 9         for (int i =0;i<5;i++){
10             quu.dequeue(); //出队 五个。
11             System.out.println(quu);
12         }
13         quu.enqueue(90); //新入队 一个
14         System.out.println(quu);
15     }
16 }
Test.java

时间复杂度分析:

入队受到 MyArray 的动态变化长度的影响,所以 是均摊!

出队就不是了,我们每次将第一个拿出后,后面的都要往前移动一个位置,所以是O(n)

那么这种出队的高复杂度的情况如何优化呢?

下节的循环队列会解决这个问题!

循环队列:

循环队列概述:

上面是数组队列 下面看循环队列

基于这样的想法,就有了循环队列的实现方式!

当队列为的时候(此时的front 不知道指向谁(本应指第一个),只好和tail 一样):

当队列不为空的时候:

五个元素,现在出队一个,只需要将a弹出,front 向后移动一个!

 

此时如果后面一直push() 元素的话,此时tail就不能++了。

其实此时可以把它看做是个环,0-7 形成一个环,7之后就是0了。

队列可以插入直到tail 比 front 小一个,即上图所示,就不能插入了(之所以不能继续插入,是因为front==tail 代表的是空队列!)。

综上给出如下定义:

当tail +1 = front 的时候就可以进行扩容了。(其实我们要知道,这里我们是有意识的浪费了一个空间!!!)

还有上述的tail +1  == front 是不准确的, 其实应该是 (tail +1 )% c == front%c  , c是队列的容量! 。   因为front 和tail 是一直可以增加的。

总结:front 是当前第一个元素。tail 是指向下一个插入的地方!

代码如下:

1 package cn.zcb.demo05;
2 
3 public interface MyInterface_Queue <T> {
4     public abstract void enqueue(T a);  //入队
5     public abstract T dequeue();  //出队
6     public abstract T getFront();  //队头
7     public abstract int getSize();  //得到队列中的实际长度
8     public abstract boolean isEmpty();  //是否为空。
9 }
MyInterface_Queue.java
 1 package cn.zcb.demo05;
 2 
 3 public class MyLoopQueue <T> implements MyInterface_Queue<T>  {
 4     private T[] data;
 5     private int front,tail;
 6     private int size; //其实不用它,也可以,是可以直接由front 和 tail 推出的,但是加上它会使得逻辑变得简单。
 7 
 8     //构造函数
 9     public MyLoopQueue(int capacity){
10         data =(T[] ) new Object[capacity +1] ;//因为循环队列会浪费一个空间, 所以开辟的时候要多开辟一个
11         this.front = 0;
12         this.tail = 0;
13         this.size = 0;
14     }
15     public MyLoopQueue(){
16         this(10);   //用户所认为的是 10  ,但是实际上是 11
17     }
18     private void resize(int newCapacity){
19         T[] newData = (T[]) new Object[newCapacity +1];
20         for (int i =0;i<this.size ;i++){  //将旧的队列的 front 放到新的队列中的0号位置。
21             newData[i] = this.data[(i+front)%this.data.length]; //取余是为了防止越界!
22         }
23         data = newData ;
24         front = 0;
25         tail = this.size;
26         //size不需要变
27     }
28     public  void enqueue(T a){
29         if( (tail +1)%this.data.length == front ){ //看是否队列 满了。(其实没有满,浪费了一个)
30             this.resize(this.getCapacity() *2); //注意这里不能是 this.data.length ,它们是有一个的差距的。
31         }
32         data[tail] = a;
33         tail = (tail +1)%this.data.length;  //取余是为了防止越界!
34         this.size ++;
35     }
36     public  T dequeue(){
37         if(this.isEmpty()){
38             throw new IllegalArgumentException("队列为空!!!");
39         }
40         T ret =this.data[front];
41         this.data[front] = null;  //出队!
42         front = (front+1)%this.data.length;  //维护front
43         this.size --;
44         //出队之后,也可以进行缩容。
45         if(this.size  == this.getCapacity() /4 && this.getCapacity()/2 !=0 ){  //当 size == 容量的1/4 时候,就将容量缩小为 1/2  这样做的目的是为了防止震荡的出现
46             this.resize(this.getCapacity()/2);  //缩容的值不应该等于0  !!!
47         }
48 
49         return ret;
50     }
51     public  T getFront(){
52         if(this.isEmpty())
53             throw new IllegalArgumentException("队列为空!!!");
54         return this.data[front];
55     }
56     public  int getSize(){
57         return this.size;
58     }
59     public  boolean isEmpty(){
60         return front == tail;
61     }
62 
63     public int getCapacity(){
64         return this.data.length-1;
65     }
66 
67     @Override
68     public String toString(){  //遍历队列的另一种方式,  resize的方式是其中的一种,这是另外一种!
69         StringBuilder builder = new StringBuilder();
70         builder.append("LoopQueue's data is below: 
front[");
71         for (int i =front ;i != tail ; i = (i+1)%this.data.length){
72             builder.append(this.data[i]);
73             if( (i+1)%this.data.length != tail )
74                 builder.append(" ,");
75         }
76         builder.append(String.format("] 队尾 Capacity: %d Size :%d", this.getCapacity(),this.size ));
77         return builder.toString();
78     }
79 }
MyLoopQueue.java
 1 package cn.zcb.demo05;
 2 
 3 public class Test {
 4     public static void main(String[] args) {
 5         MyLoopQueue <Integer> loopQueue = new MyLoopQueue<>(5);
 6         for (int i =0;i<7;i++){
 7             loopQueue.enqueue(i);
 8             System.out.println(loopQueue);
 9         }
10         loopQueue.dequeue();
11         System.out.println(loopQueue);
12         loopQueue.dequeue();
13         System.out.println(loopQueue);
14 
15         for (int i =0;i<20;i++){
16             loopQueue.enqueue(i);//再入队 20个元素
17             System.out.println(loopQueue);
18         }
19     }
20 
21 
22 
23 }
24 /*  输出:   我们也可以看出,从用户的角度来看,循环队列 一侧是队首,一侧是队尾,包括里面的capacity 是比真实空间小一个的,用户都不用关心。
25 LoopQueue's data is below:
26 front[0] 队尾 Capacity: 5 Size :1
27 LoopQueue's data is below:
28 front[0 ,1] 队尾 Capacity: 5 Size :2
29 LoopQueue's data is below:
30 front[0 ,1 ,2] 队尾 Capacity: 5 Size :3
31 LoopQueue's data is below:
32 front[0 ,1 ,2 ,3] 队尾 Capacity: 5 Size :4
33 LoopQueue's data is below:
34 front[0 ,1 ,2 ,3 ,4] 队尾 Capacity: 5 Size :5
35 LoopQueue's data is below:
36 front[0 ,1 ,2 ,3 ,4 ,5] 队尾 Capacity: 10 Size :6
37 LoopQueue's data is below:
38 front[0 ,1 ,2 ,3 ,4 ,5 ,6] 队尾 Capacity: 10 Size :7
39 LoopQueue's data is below:
40 front[1 ,2 ,3 ,4 ,5 ,6] 队尾 Capacity: 10 Size :6
41 LoopQueue's data is below:
42 front[2 ,3 ,4 ,5 ,6] 队尾 Capacity: 10 Size :5
43 LoopQueue's data is below:
44 front[2 ,3 ,4 ,5 ,6 ,0] 队尾 Capacity: 10 Size :6
45 LoopQueue's data is below:
46 front[2 ,3 ,4 ,5 ,6 ,0 ,1] 队尾 Capacity: 10 Size :7
47 LoopQueue's data is below:
48 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2] 队尾 Capacity: 10 Size :8
49 LoopQueue's data is below:
50 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3] 队尾 Capacity: 10 Size :9
51 LoopQueue's data is below:
52 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4] 队尾 Capacity: 10 Size :10
53 LoopQueue's data is below:
54 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5] 队尾 Capacity: 20 Size :11
55 LoopQueue's data is below:
56 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6] 队尾 Capacity: 20 Size :12
57 LoopQueue's data is below:
58 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7] 队尾 Capacity: 20 Size :13
59 LoopQueue's data is below:
60 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8] 队尾 Capacity: 20 Size :14
61 LoopQueue's data is below:
62 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9] 队尾 Capacity: 20 Size :15
63 LoopQueue's data is below:
64 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10] 队尾 Capacity: 20 Size :16
65 LoopQueue's data is below:
66 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11] 队尾 Capacity: 20 Size :17
67 LoopQueue's data is below:
68 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12] 队尾 Capacity: 20 Size :18
69 LoopQueue's data is below:
70 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13] 队尾 Capacity: 20 Size :19
71 LoopQueue's data is below:
72 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14] 队尾 Capacity: 20 Size :20
73 LoopQueue's data is below:
74 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15] 队尾 Capacity: 40 Size :21
75 LoopQueue's data is below:
76 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16] 队尾 Capacity: 40 Size :22
77 LoopQueue's data is below:
78 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17] 队尾 Capacity: 40 Size :23
79 LoopQueue's data is below:
80 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18] 队尾 Capacity: 40 Size :24
81 LoopQueue's data is below:
82 front[2 ,3 ,4 ,5 ,6 ,0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19] 队尾 Capacity: 40 Size :25
83 * */
Test.java

数组队列和循环队列的复杂度分析:

循环队列的时间复杂度分析:


下面我们看O(n) 的时间复杂度比 O(1) 的时间复杂度到底有多快。实际证明我们在数据结构中设计低的时间复杂度是有意义的。

下面用程序测试 数组队列和 循环队列的效率!

 1 package cn.zcb.demo05;
 2 
 3 import java.util.Random;
 4 public class Test {
 5     private static double getRuntime(MyLoopQueue queue,int opCount){
 6         long startTime = System.nanoTime(); //以纳秒为单位!
 7 
 8         Random random = new Random();
 9         for (int i =0;i<opCount; i++){
10             queue.enqueue(random.nextInt(Integer.MAX_VALUE));
11         }
12         for (int i =0;i<opCount; i++){
13             queue.dequeue();
14         }
15         long endTime = System.nanoTime();
16         return (endTime -startTime)/1000000000.0;
17     }
18 
19     public static void main(String[] args) {
20         int opCount = 100000;
21         MyLoopQueue<Integer> loopQueue = new MyLoopQueue<>();
22         double time = getRuntime(loopQueue,opCount);
23         System.out.println("循环队列所消耗的时间是: "+time);
24     }
25 }
26 /*  输出:循环队列所消耗的时间是: 0.0291362
27 * */
getRuntime()

上述十万次操作:

数组队列用了 8.42s
循环对类用了 0.06s

它们相差100多倍,所以分析时间复杂度是很有必要的。

原文地址:https://www.cnblogs.com/zach0812/p/11827922.html