数组
链表
栈
队列
1、数组实现,简易版,队尾入,队首出,没有加边界限制条件。
时间复杂度比较高,入队是O(1),出队都O(n)
public class MyQueue {
private int size; //元素个数
private int[] data; //存储元素的数组
public MyQueue(int cap){
this.size = 0;
this.data = new int[cap];
}
//入队
public void push(int n){
if(size == data.length){
resize();
}
data[size] = n;
size++;
}
//扩容,每次扩大2倍
private void resize() {
int[] temp = new int[data.length * 2];
for (int i = 0; i < data.length; i++) {
temp[i] = data[i];
}
this.data = temp;
}
//出队,从对首出,这样每出一次都需要移动一次数组
public int pop(){
if(size == 0){
return -1;
}
int re = data[0];
for (int i = 0; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
return re;
}
//测试
public static void main(String[] args) {
MyQueue myQueue = new MyQueue(10);
System.out.println(myQueue.size);
myQueue.push(12);
myQueue.push(45);
myQueue.push(25);
myQueue.push(66);
myQueue.push(36);
for (int i = 0; i <5; i++) {
System.out.println(myQueue.pop());
System.out.println("size:"+ myQueue.size);
}
System.out.println(myQueue.size);
}
}
2、数组实现,升级版,引入头指针,提升时间复杂度
上一种实现里,有一个size表示元素个数,可以表示队列的尾结点,这里引入一个表示头结点的指针,每次入队出队直接操作头尾指针位置的元素,时间复杂度都是O(1),当尾结点移动到数组尾部时需要进行移动数据,时间复杂度为O(n),也就是说,假使初始化大小为100,那么前面100次push的时间复杂度都是O(1),再次进行push的时候进行移动元素或者扩容,时间复杂度为O(n),等于是100次O(1)+1次O(n)。整体来说有不少提升
public class MyQueuePlus {
private int[] data;
private int head; //头指针,用于出队
private int tail; //尾指针,用于入队
public MyQueuePlus(int cap){
this.data = new int[cap];
this.head = 0;
this.tail = 0;
}
//入队
public void push(int n){
//判断扩容
if(tail == data.length){
resize();
}
data[tail] = n;
tail++;
}
//扩容,出站后数组前面的位置就空出来了,如果元素个数不多,不需要扩容,只需重新整理一下队列中的元素位置即可
private void resize() {
int n = tail - head;
int resize = Math.max(n * 2,data.length);
int[] temp = new int[resize];
for (int i = 0; i < tail-head; i++){
temp[i] = data[head+i];
}
head = 0;
tail = n;
this.data = temp;
}
//出队
public int pop(){
return data[head++];
}
public static void main(String[] args) {
MyQueuePlus queue = new MyQueuePlus(5);
queue.push(2);
queue.push(5);
queue.push(3);
queue.push(4);
queue.pop();
queue.pop();
queue.push(8); //尾指针已到达数组最后
queue.push(15); //触发一次整理(扩容)
System.out.println(queue.data.length);
System.out.println(queue.head);
System.out.println(queue.tail);
}
}
3、用链表实现
用链表实现比较简单,而且时间复杂度也只有O(1),还不涉及到扩容问题,但是正因为这样,链表具有天然的扩展性,使用的时候更需要注意内存问题,只要你不限制,理论上可以占完整个内存。所以可以加在初始化的时候给一个大小,在入队列的时候进行判断一下。这里没有实现。
public class MyQueueWithLinked {
private int size; //记录节点数量
private Node head; //头结点,用于出队
private Node tail; //尾结点,用户入队
//初始化
public MyQueueWithLinked(){
this.size = 0;
this.head = null;
this.tail = null;
}
//入队
public void push(int n){
Node node = new Node(n);
if(size == 0){ //队列为空时
head = node;
tail = node;
}else { //队列为空时,直接插入到队尾
tail.next = node;
tail = node;
}
size++;
}
//出队列
public int pop(){
int re;
if(size == 0){ //队列为空
re = -1;
}else {
re = head.value; //从头节点出队,
head= head.next; //将头结点指向下一个节点
size--;
}
return re;
}
//测试
public static void main(String[] args) {
MyQueueWithLinked queue = new MyQueueWithLinked();
queue.push(23);
queue.push(5);
queue.push(66);
queue.pop();
queue.push(17);
queue.push(99);
queue.push(33);
for (int i = 0; i < 5; i++) {
System.out.println(queue.pop());
}
}
}
//用来表示链表的节点,其实本质上来说,一个链表就是一个节点,因为永远可以通过next来找到下一个节点,直到找到所有节点。
class Node{
int value;
Node next;
public Node(int value){
this.value = value;
this.next = null;
}
}