栈和队列是两种特有的存储数据的结构,栈是后进先出的一种结构,队列是先进先出的一种结构。
由于这种特有的结构,在选择底层存储方式也有差异。由于栈是后进先出的结构,其实就是尾删,尾增操作,如果用顺序表来存储,尾删、尾增时间复杂度则是O(1)。链表则需考虑链表的结构,如果是单链表,找尾的时间复杂度就是O(n)。如果是记录尾的双向链表,时间复杂度是O(1)。Java中也封装了这样的集合结构(Stack),用的就是顺序表。由于队列是先进先出的结构,其实就是头删,尾增操作。如果用顺序表存储,头删时间复杂度是O(n),需要挪动数据。如果用链表存储,不论是单链表还是双向链表(记录尾),头删、尾增操作时间复杂度都是O(1)。Java中也封装了这样的结构(Queue),是LinkedList实现的接口,用的是双向链表。这里用的是记录尾的单链表实现。
两种不同的结构的存在,为我们实际处理一些数据提供了便利。
import java.util.Arrays;
public class Mystack {
private static final int DEFAULT_SIZE = 10;
private int[] elem;
private int top;
public Mystack() {
elem = new int[DEFAULT_SIZE];
}
//压栈
public int push(int val) {
if(size() == elem.length) {
this.elem = Arrays.copyOf(elem, elem.length * 2);
}
this.elem[top] = val;
this.top++;
return val;
}
public int pop() {
if(empty()) {
throw new NullStackExpection("栈为空");
}
return this.elem[--top];
}
public int peek() {
if(empty()) {
throw new NullStackExpection("栈为空");
}
return this.elem[top - 1];
}
public int size() {
return this.top;
}
public boolean empty() {
return size() == 0;
}
}
public class MyQueue {
static class ListNode {
private int val;
private ListNode next;
public ListNode(int val) {
this.val = val;
}
}
private int usedSize;
private ListNode head;
private ListNode tail;
public void offer(int date) {
ListNode newNode = new ListNode(date);
if(head == null) {
head = newNode;
tail = newNode;
this.usedSize++;
return;
}
tail.next = newNode;
tail = tail.next;
this.usedSize++;
}
public int poll() {
if(empty()) {
throw new NullQueueExpection("空队列");
}
//只有一个节点特殊处理
if(head.next == null) {
int tmp = head.val;
head = null;
tail = null;
this.usedSize--;
return tmp;
}
int tmp = head.val;
head = head.next;
this.usedSize--;
return tmp;
}
public int peek() {
if(empty()) {
throw new NullQueueExpection("空队列");
}
return this.head.val;
}
public int size() {
return this.usedSize;
}
public boolean empty() {
return this.usedSize == 0;
}
}
我们需熟悉这样的数据结构,理解底层存储原理,在实际对数据操作中才能得心应手。