基礎知識
棧
棧(stack)又名堆棧,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。
棧是一種後進先出(LIFO)的數據結構。
隊列
隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱爲隊尾,進行刪除操作的端稱爲隊頭。
隊列是一種先進先出(FIFO)的數據結構。
制定手寫方案
如果我們要手寫一個棧或者隊列,先要確定用什麼數據結構保存數據,需要實現哪些功能?
選取數據結構
爲了更好的理解棧和隊列的原理,儘量採用簡單的數據結構來保存數據,這樣實現的邏輯會變得簡單易懂,所以本次採用的是數組。
實現哪些功能
對於棧:
- 入棧和出棧
- 獲取棧頂元素但不刪除此元素
- 獲取棧是否爲空的方法
- 獲取棧大小的方法
對於隊列:
- 進隊列和出隊列
- 獲取隊列頭元素但不刪除此元素
- 獲取隊列是否爲空的方法
- 獲取隊列大小的方法
使用數組實現棧:
import java.util.Arrays;
import java.util.EmptyStackException;
class Stack<E> {
private E[] _data; // 數據
private int _size; // 數組長度
private int _realLength; // 數組已用長度
@SuppressWarnings("unchecked")
public Stack(int initSize) {
this._size = initSize;
this._data = (E[]) new Object[initSize];
this._realLength = 0;
}
/**
* 默認數組長度爲20
*/
public Stack() {
this(20);
}
/**
* 入棧
*
* @param element 添加的元素
*/
public void push(E element) {
if (size() >= _size) {
grow();
}
_data[_realLength++] = element;
}
/**
* 出棧
*
* @return 返回棧頂的元素
*/
public E pop() {
if (size() < 1) {
throw new EmptyStackException();
}
E top = _data[_realLength - 1];
_data[--_realLength] = null;
return top;
}
/**
* 獲取棧頂元素
*
* @return 返回的是棧頂元素
*/
public E peek() {
if (size() < 1) {
throw new EmptyStackException();
}
return _data[_realLength - 1];
}
/**
* 判斷棧是否爲空棧
*
* @return true代表空棧
*/
public boolean isEmpty() {
return _realLength == 0;
}
/**
* 棧的大小
*
* @return 大小值
*/
public int size() {
return _realLength;
}
/**
* 數組的擴容,擴容大小爲原先的2倍
*/
private void grow() {
// 擴容後size也要變化
_size = _size * 2;
_data = Arrays.copyOf(_data, _size);
}
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>(1);
stack.push(0);
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println("stack is empty: " + stack.isEmpty());
System.out.println("stack size is: " + stack.size());
System.out.println("stack pop is: " + stack.pop());
System.out.println("stack top is: " + stack.peek());
// stack is empty: false
// stack size is: 4
// stack pop is: 3
// stack top is: 2
}
}
需要注意的幾個點:
- 數組的擴容,做法是每次擴容原先長度的2倍
- 出棧後數組的變更,元素出棧後需要將之前的元素置空,便於
GC
- 對數組越界問題的把握,只要涉及到獲取元素的操作,都需要對下標進行是否越界判斷,可以返回
null
,也可以直接拋出異常,具體看個人
使用數組實現隊列:
import java.util.Arrays;
import java.util.NoSuchElementException;
/**
* FIFO
*/
class Queue<E> {
private E[] _data;
private int _realLength;
private int _size;
@SuppressWarnings("unchecked")
public Queue(int initSize) {
this._data = (E[]) new Object[initSize];
this._size = initSize;
this._realLength = 0;
}
/**
* 默認數組長度爲20
*/
public Queue() {
this(20);
}
/**
* 向隊列尾端添加元素
*
* @param element 元素
*/
public void offer(E element) {
if (size() >= _size) {
grow();
}
_data[_realLength++] = element;
}
/**
* 取出隊列頭元素,並且從隊列中刪除它
*
* @return 頭元素
*/
public E poll() {
if (isEmpty()) {
throw new NoSuchElementException("queue is empty");
}
E first = _data[0];
_data = Arrays.copyOfRange(_data, 1, _size);
_realLength--;
return first;
}
/**
* 取出隊列頭元素,但是不刪除它
*
* @return 頭元素
*/
public E element() {
if (isEmpty()) {
throw new NoSuchElementException("queue is empty");
}
return _data[0];
}
/**
* 判斷隊列是否爲空
*
* @return true代表空隊列
*/
public boolean isEmpty() {
return _realLength == 0;
}
/**
* 隊列的大小
*
* @return 大小值
*/
public int size() {
return _realLength;
}
/**
* 數組的擴容,擴容大小爲原先的2倍
*/
private void grow() {
// 擴容後size也要變化
_size = _size * 2;
_data = Arrays.copyOf(_data, _size);
}
public static void main(String[] args) {
Queue<Integer> queue = new Queue<Integer>();
queue.offer(0);
queue.offer(1);
queue.offer(2);
queue.offer(3);
System.out.println("queue is empty: " + queue.isEmpty());
System.out.println("queue size is: " + queue.size());
System.out.println("queue poll: " + queue.poll());
System.out.println("queue poll: " + queue.poll());
System.out.println("queue poll: " + queue.poll());
System.out.println("queue poll: " + queue.poll());
// queue is empty: false
// queue size is: 4
// queue poll: 0
// queue poll: 1
// queue poll: 2
// queue poll: 3
}
}
使用數組來實現隊列相比較棧更加的容易,添加元素、判空和 size()
兩者都相似,只是在出隊列的時候做法與出棧不相同而已,出隊列出的是頭部元素,也就是最先添加的元素,出隊列之後需要重新調整數組,使用系統提供的 Arrays.copyOfRange(_data, 1, _size)
可輕鬆的複製數組一段元素。
手寫棧和隊列不僅可以用數組來實現,也可以用鏈表來實現,用鏈表可以可以更好的控制添加和刪除操作,畢竟鏈表採用的是指針方案。有興趣的可以自行實現一波。
如果本文章你發現有不正確或者不足之處,歡迎你在下方留言或者掃描下方的二維碼留言也可!