• 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多倍,所以分析时间复杂度是很有必要的。

  • 相关阅读:
    B01-java学习-阶段2-面向对象
    A11-java学习-二维数组-面向对象概念-类的编写-测试类的编写-创建对象-使用对象-递归
    09-java学习-数组-冒泡排序-选择排序-数组工具类编写-查找-扩容
    08-java学习-数组-增强for循环-数组与方法-main函数参数
    07-java学习-方法重载-idea集成开发工具学习-项目-模块-包
    执行SQL语句---SELECT
    执行SQL语句---INSERT/UPDATE/DELETE
    undefined reference to `mysql_init'解决办法
    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
    网络编程(三)---数据报套接字
  • 原文地址:https://www.cnblogs.com/zach0812/p/11827922.html
Copyright © 2020-2023  润新知