java集合源码解读---Queu和Stack的数组和链式实现

本文介绍了栈和队列这两种基本的数据结构,并提供了详细的Java代码实现,包括使用数组和链表实现栈和队列的方法。

栈和队列

所谓的栈,是一个含有至少两个基本操作的抽象数据类型:插入新的元素;删除最近时间插入的元素。遵循FILO(First in,last out,先进后出)的原则。
所谓的队列,也是一个含有至少两个基本操作的抽象数据类型:插入新的元素;删除最久时间插入的元素。遵循FIFO(First in,first out,先进先出)的原则。
关于栈和队列的具体实现,我们即可以借助于数组,也可以采用链表来实现。

1) 栈的数组实现方式

java代码

public class MyStack <E>{

    public int count;
    public Object[] items;

    public MyStack()
    {
        items = new Object[20];
        count = 0;
    }
    public MyStack(int len)
    {
        items = new Object[len];
        count = 0;
    }
    //判断栈是否为空
    boolean isEmpty(){return count==0;}

    //重整数组的大小
    private void resize(int size)
    {
        Object[] newItems = new Object[size];
        for(int i =0;i<count;i++)
            newItems[i] = items[i];
        items = newItems;
    }
    //进栈
    public void push(E e)
    {
        //如果栈满,则重整数组大小。
        if(count == items.length) resize(2*items.length);
        items[count++] = e;//数组的起始索引是0,入栈后,数组的大小应增加1
    }
    //出栈
    public E pop()
    {
        if(count==0) return null; //如果栈为空
        E item = (E)items[count-1];
        items[count-1] = null;
        count--;
        //重整数组大小,节省存储空间。
        if(count>0&&count<=items.length/4) resize(items.length/2); 

        return item;
    }

    //返回栈中的最后一个元素
    public E peek()
    {
        if(count==0) return null;

        E item = (E)items[count-1];
        return item;
    }
}

2)栈的链式实现方式

java代码

public class MyStack<E> {
    Node head;//表示栈顶
    private class Node<E>
    {
        E item;
        Node next;
    }
    public boolean isEmpty()
    {
        return head==null;
    }
    public void push(E e)
    {
        Node<E> node = new Node();
        node.item = e;
        node.next = head; //栈的物理连接顺序是从上到下
        head = node;
    }
    public E pop()
    {
        if(head==null) return null;
        E e = (E)head.item;//栈顶元素先出
        head=head.next;
        return e;
    }
    public E peek()
    {
        if(head == null) return null;
        else 
            return (E)head.item;
    }
}

3)队列的数组实现方式

java代码

//用数组实现循环队列
 public class ArrayQueue <E>
{
    private int front;
    private int rear;
    private int capacity;
    private int count;
    private int capacityIncrement;
    private Object[] itemArray;

    public ArrayQueue(){
        front = 0;
        rear = 0;
        count = 0;
        capacity = 10;
        capacityIncrement = 5;//新增队列的长度。
        itemArray = new Object[capacity];
    }

    boolean isEmpty() {return count==0;}

    //1.入队
    public void insert(E e)
    {
        //1.如果队列已满,就先扩充队列,后插队。
        if(count==capacity) {
            capacity+=capacityIncrement;
            Object[] newArray = new Object[capacity];
        /**
         * 将原队列中的元素原样拷贝到新队列中。如果不是原样拷贝,则会破坏队列的FIFO特性。
         */
        //如果队列中的元素位于itemArray[font.....rear-1]中
        if(front<rear){
            for(int i = front;i<rear;i++){
                newArray[i] = itemArray[i];
            }           
        }
        else{
            //因队列已满,此时front==rear,应该将队列分成两个区间:[0:rear-1] 和[front:count-1],分别拷贝到新数组的两端。
            //在原队列的中间插入一段新的存储空间。
            //否则,如果在原队列的后面插入新的空间,那么再次进行入队操作时,原来的元素就会被覆盖掉。
            for(int i =0;i<rear;i++){newArray[i] = itemArray[i];}
            for(int i = front;i<count;i++)
            {newArray[i+capacityIncrement] = itemArray[i];}
            front+=capacityIncrement;//然后,将front改为指向其新位置. 
        }
        itemArray = newArray;
       }

        //2.如果队列未满,直接插队。
        itemArray[rear]=e;  
        rear=(rear+1)%capacity; 
        count++;
    }

    //2.出队
    public E remove()
    {
        if(count==0) return null;
        else{
            E item = (E)itemArray[front];
            itemArray[front] = null;
            front = (front+1)%capacity;
            count--;
            return item;
        }
    }
}
//另外一种数组实现循环队列的简单实现
import java.util.NoSuchElementException;  

public class SimpleArrayQueue {  
    protected Object [] data;  
    protected int size,  
                  head,  
                  tail;  
    public SimpleArrayQueue(){  
        final int INITIAL_LENGTH=10;  
        data=new Object[INITIAL_LENGTH];  
        size=0;  
        head=0;  
        tail=-1;  
    }  
    //获取队列的大小和判断队列是否为空  
    public int theSize(){ return size;}  
    public boolean isEmpty(){ return size==0;}  

    //获取队头元素
    public Object front(){  
        if(size==0) throw new NoSuchElementException();  
        return data[head];  
    } 

    //入队操作 
    public void enqueue(Object element){  
        if(size==data.length){  

            //在java中允许将一个数组变量拷贝给另外一个数组变量。这时,两个变量将引用同一个数组。 
            Object [] oldData=data;//保存原数组data
            data=new Object[data.length*2]; //double the length of data 

            //将数组分成两个区间进行拷贝:区间1:[head:OldData.length-1]和区间2:[0:tail]
            //当head==0时,原数组只有区间1这一段需要被拷贝,当head>0时,原数组有区间1和区间2这两段需要被拷贝。
            //copy oldData[head:OldData.length-1] to data[0:OldData.length-1-head]
            System.arraycopy(oldData, head,data,0,oldData.length-head);  
            /*
             * 如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用Arrays类中的copyOf()方法:
             *  int[] copiedLckyNumbers = Arrays.copyOf(luckyNumbers,2*luckyNumbers.length);
             *  第二个参数是新数组的长度,这个方法通常用来增加数组的大小。
             *  如果数组的元素是数值型,那么多余的元素将被赋值为0;如果是boolean,多余的元素将会被赋值为false;
             *  相反,如果长度小于原始数组的长度,则只会拷贝最前面的数据元素。 
             */

            if(head>0)//拷贝区间2  
                //copy oldData[0:tail] to data [oldData.length-head:oldlenght-1]  
               //System.arraycopy(oldData, 0, data, head+1, tail+1); //错误写法 
            System.arraycopy(oldData, 0, data,oldData.length-head, tail+1);//正确写法
            head=0;  
            tail=oldData.length-1;  
        }  

        tail=(tail+1)%data.length;  
        data[tail]=element;  
        size++;  
    }  

    //出队操作 
    public Object dequeue(){
         /* 注意 :
          * if(size--==0){throw new NoSuchElementException();}//先执行if(size==0),后执行size--;
          * 放置的顺序。
          * 不能写成if(--size==0){ throw new NoSuchElementException(); }
          * 因为当队列中就剩下一个元素时,--size;会使size为0,此时抛出异常,并没有对该异常进行处理。
          * 程序中断,后面的语句无法执行。       
          */
        Object element=data[head];  
        head=(head+1)%data.length;//当队列满的时候size==data.length,即data.length就是当前size的值。
                                 //如果size变小后,head就会跳过下一个元素,而不是顺移下一个,导致下一个元素被”遗忘“了。
        if(size--==0){ throw new NoSuchElementException(); } //先执行if(size==0),后执行size--;
        return element; //程序最后一句一定要是return语句。
    }  

}  

4)队列的链式实现方式

java代码

public class ListQueue <E>{
    private int count;
    private Node<E> front,rear;

    private class Node<E>
    {
        E item;
        Node link;
    }

//   //提供java类的空参数的构造函数。
//   ListQueue()
//  {
//      count =0;
//      front = null;
//      rear = null;
//      Node<E> node = new Node<E>();
//  }

    public boolean isEmpty(){ return count==0;}

    //入队
    public void insert(E e)
    {
        Node<E> newNode = new Node<E>();//初始化为空节点。
        newNode.item = e;

        //如果队为空
        if(rear==null) 
            front = rear = newNode;
        else{
            rear.link = newNode;//新节点连接到队尾处
            rear = newNode;//新节点变成队尾
        }
        count++;
    }
    //出队
    public E remove()
    {
        //如果队为空
        if(count==0) return null;
        else{
            E item = front.item;
            front = front.link;

            if(front==null) { rear = null; }//队列为空。
            count--;
            return item;
        }
    }
    //克隆队列
    public ListQueue<E> clone()
    {
        ListQueue<E>Q = new ListQueue<E>();
        for(Node<E>t=front;t!=null;t=t.link)//for循环的这种写法。等价于while循环。
        Q.insert(t.item);
        return Q;
    }
}
Java 中,通常使用的 `Queue` 是基于链表实现的,例如 `LinkedList` 或 `ArrayDeque`,它们并不直接支持从队尾(rear)开始遍历。标准的队列操作只允许从队头(front)取出添加元素,即通过 `poll()`、`offer()`、`peek()` 等方法操作队头。如果希望从队尾开始遍历,可以通过以下方式实现: ### 使用 `ArrayDeque` 并借助迭代器 `ArrayDeque` 提供了 `descendingIterator()` 方法,可以获取一个从队尾开始的迭代器,从而实现逆序遍历: ```java import java.util.ArrayDeque; import java.util.Iterator; import java.util.Queue; public class TraverseFromRear { public static void main(String[] args) { Queue<Integer> queue = new ArrayDeque<>(); queue.offer(1); queue.offer(2); queue.offer(3); queue.offer(4); Iterator<Integer> iterator = ((ArrayDeque<Integer>) queue).descendingIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } } ``` ### 使用自定义队列结构并维护 `rear` 指针 如果使用的是自定义的顺序队列(基于数组实现),可以像引用中提到的那样,通过 `front` `rear` 指针来控制遍历方向[^2]。以下是一个简单的实现示例: ```java public class CustomQueue { private int front; private int rear; private int[] arrays; public CustomQueue(int size) { arrays = new int[size]; front = 0; rear = 0; } // 入队操作 public boolean enqueue(int value) { if ((rear + 1) % arrays.length == front) { return false; // 队列已满 } arrays[rear] = value; rear = (rear + 1) % arrays.length; return true; } // 从队尾开始遍历 public void traverseFromRear() { int i = rear; while (i != front) { i = (i - 1 + arrays.length) % arrays.length; // 逆序移动指针 System.out.println(arrays[i]); } } public static void main(String[] args) { CustomQueue queue = new CustomQueue(5); queue.enqueue(10); queue.enqueue(20); queue.enqueue(30); queue.traverseFromRear(); // 从队尾开始输出 } } ``` ### 注意事项 - 在使用 `ArrayDeque` 时,由于其内部结构是循环数组,因此不能直接访问其内部索引,只能通过迭代器方式实现逆序遍历。 - 对于自定义队列,需要特别注意 `front` `rear` 的移动逻辑,尤其是在队列为空或满时的边界条件处理[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值