棧(Stack)
- 棧也是一種線性結構
- 相比數組,棧對應的操作是數組的子集
- 只能從一端添加元素,也只能從一端取出元素,這一端稱爲棧頂
- 棧是一種後進先出的數據結構–Last in First out(LIFO)
結合數組篇的實現來完成棧的基本實現(ArrayStack):
Stack接口類:
/**
* @author ymn
* @version 1.0
* @date 2020\4\8 0008 13:54
*/
public interface Stack<T> {
//獲取棧中元素的個數
int getSize();
//判斷棧是否爲空
boolean isEmpty();
//向棧中添加元素
void push(T e);
//刪除棧頂的元素
T pop();
//返回棧頂的元素
T peek();
}
ArrayStack實現類:
/**
* @author ymn
* @version 1.0
* @date 2020\4\8 0008 14:02
*/
public class ArrayStack<T> implements Stack<T> {
private Array<T> array;
//有參構造函數
public ArrayStack(int capacity) {
array = new Array<>(capacity);
}
public ArrayStack() {
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
//查看當前靜態數組的容積,ArrayStack特有的方法
public int getCapacity(){
return array.getCapacity();
}
@Override
public void push(T e) {
array.addLast(e);
}
@Override
public T pop() {
return array.removeLast();
}
@Override
public T peek() {
return array.getLast();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack:");
res.append("[");
for (int i = 0; i < array.getSize(); i ++){
res.append(array.get(i));
if (i != array.getSize() - 1){
res.append(",");
}
}
res.append("] top");
return res.toString();
}
}
棧的應用:編輯器的撤銷(undo)操作,操作系統中程序調用的系統棧等。
例:leetCode第二十題:
import java.util.Stack;
/**
* @author ymn
* @version 1.0
* @date 2020\4\8 0008 15:48
*/
class Solution {
//解決括號匹配的算法
public boolean isValid(String s){
if (s.isEmpty()){
return true;
}
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i ++){
char c = s.charAt(i);
if (c == '(' || c == '[' || c == '{'){
stack.push(c);
}
else {
//如果還爲空就說明是以右括號開始的
if (stack.isEmpty())
return false;
char topChar = stack.pop();
if (c == ')' && topChar != '(')
return false;
if (c == ']' && topChar != '[')
return false;
if (c == '}' && topChar != '{')
return false;
}
}
//遍歷完如果棧中還有元素返回false,如還有單個左括號未匹配的情況
return stack.isEmpty();
}
}
隊列 Queue
- 隊列也是一種線性結構
- 相比數組,隊列對應的操作是數組的子集
- 只能從一端(隊尾)添加元素,只能從另一端(隊首)取出元素
- 隊列是一種先進先出的數據結構–First In First Out(FIFO)
結合數組篇的實現來完成隊列的基本實現:
Queue接口類:
/**
* @author ymn
* @version 1.0
* @date 2020\4\9 0009 9:57
*/
public interface Queue<T> {
//獲取隊列中元素的個數
int getSize();
//隊列是否爲空
boolean isEmpty();
//進隊
void enqueue(T e);
//出隊
T dequeue();
//獲取隊首元素
T getFront();
}
ArrayQueue實現類:
/**
* @author ymn
* @version 1.0
* @date 2020\4\9 0009 10:26
*/
public class ArrayQueue<T> implements Queue<T> {
private Array<T> array;
public ArrayQueue(int capacity){
array = new Array<>(capacity);
}
public ArrayQueue() {
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity(){
return array.getCapacity();
}
@Override
public void enqueue(T e) {
array.addLast(e);
}
@Override
public T dequeue() {
return array.removeLast();
}
@Override
public T getFront() {
return array.getFirst();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Queue:");
res.append("front [");
for (int i = 0; i < array.getSize(); i ++){
res.append(array.get(i));
if (i != array.getSize() - 1){
res.append(",");
}
}
res.append("] tail");
return res.toString();
}
}
數組隊列的複雜度分析:
int getSize() : O(1)
boolean isEmpty() : O(1)
T getFront() : O(1)
T dequeue () : O(n)
void enqueue(T e) : O(1)
因爲數組隊列的出隊操作的時間複雜度是O(n)級別的,所以當數據量變大且需要進行頻繁的出隊操作時,使用數組隊列是非常消耗性能的,所以當這種情況我們可以使用出隊操作時間複雜度爲O(1)的循環隊列:
LoopQueue實現類:
import java.util.NoSuchElementException;
/**
* @author ymn
* @version 1.0
* @date 2020\4\9 0009 14:54
*/
public class LoopQueue<T> implements Queue<T> {
private T[] data;
//front表示隊首元素,tail表示元素即將進入的位置,也就是隊尾後的第一個位置
private int front,tail;
//size表示隊列中的元素個數
private int size;
//有參構造方法
public LoopQueue(int capacity) {
data = (T[])new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
public int getCapacity(){
return data.length - 1;
}
@Override
public int getSize() {
return size;
}
/**
* front等於tail時隊列爲空 (tail + 1) % 隊列總長度 = front 隊列爲滿
* @return
*/
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public void enqueue(T e) {
//(tail + 1) % 隊列總長度 = front 隊列爲滿,調用resize方法進行擴容
if((tail + 1) % data.length == front){
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}
@Override
public T dequeue() {
//當隊列爲空時,拋出異常
if (isEmpty()){
throw new NoSuchElementException("Cannot dequeue from an empty queue");
}
T ret = data[front];
data[front] = null;
//維護front
front = (front + 1) % data.length;
size --;
//懶釋放和不能讓縮容的大小爲0
if (size == getCapacity() / 4 && getCapacity() / 2 != 0){
resize(getCapacity() / 2);
}
return ret;
}
@Override
public T getFront() {
//當隊列爲空時,拋出異常
if (isEmpty()){
throw new NoSuchElementException("Cannot dequeue from an empty queue");
}
return data[front];
}
private void resize(int newCapacity){
//先聲明一個新的泛型數組
T[] newData = (T[])new Object[newCapacity];
for (int i = 0; i < size; i ++){
//把元素放在擴容後的數組中
newData[i] = data[(i + front) % data.length];
}
data = newData;
//維護front 與 tail
front = 0;
tail = size;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
//%d: 佔位符; \n: 換行符
res.append(String.format("Queue: size = %d, capacity = %d\n",size,getCapacity()));
res.append("front [");
for (int i = front;i != tail;i = (i + 1) % data.length){
res.append(data[i]);
if ((i + 1) % data.length != tail)
res.append(",");
}
res.append("] tail");
return res.toString();
}
}