一、棧的經典應用—瀏覽器的前進和後退
瀏覽器前進和後退底層就是用兩個棧來實現的。
前提:同一瀏覽器的一個窗口。
每訪問某個網址的時候瀏覽器都會將這個網址壓入棧1,用戶看到的實際就是棧1頂部的網址。
當用戶點擊後退的時候,會將棧1頂部的網址彈出並壓入棧2,此時瀏覽器又會將棧1頂部的網址呈現給用戶。
當你按下前進按鈕的時候,本質是從棧2彈出一個網址壓到棧1裏面,不過如果棧2是空的話就無法點擊前進按鈕。
當棧2不爲空,此時用戶又訪問了一個新的網址,則這時候瀏覽器會將棧2中的所有內容都清空。
舉例子:依次訪問京東、百度、淘寶,點擊兩次後退按鈕,點擊前進按鈕,再訪問騰訊。
點擊兩次後退按鈕棧情況:
點擊前進按鈕棧情況:
再訪問騰訊棧情況:
二、Stack
1、棧明細
棧是一種特殊的線性表,只能在棧頂插入和刪除元素(入棧和出棧),相當於只有棧頂一端纔對用戶可見。遵循後進先出LIFO原則(Last In First Out)與FILO原則(First In Last Out)。
考慮到棧只在一端操作數據的時間複雜度,底層可以用動態數組或者雙向鏈表實現,jdk源碼用的是動態數組實現的棧,只不過繼承的是Vector<E>
這個泛型,和動態數組唯一不同就是線程安全。
public class Stack<E> {
private ArrayList<E> list; //組合優於繼承
public Stack() {
list = new ArrayList<>();
}
public boolean isEmpty() {
return list.size() == 0;
}
public void push(E element) {
list.add(element);
}
public E pop() {
E element = list.get(list.size() - 1);
list.remove(list.size() - 1);
return element;
}
/**
* 返回棧頂元素
*
* @return
*/
public E peek() {
return list.get(list.size() - 1);
}
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(11);
stack.push(22);
stack.push(33);
stack.pop();
stack.push(44);
System.out.println(stack.peek());
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
2、測試結果
三、Queue
1、普通隊列明細
普通隊列也是特殊的線性表,只能在隊尾插入元素(入隊),在隊頭刪除元素(出隊),但是隻有隊頭爲用戶可見。遵循後進先出LILO原則(Last In Last Out)與FIFO原則(First In First Out)。
考慮到隊列需要在兩端操作數據的時間複雜度,底層最適用雙向鏈表實現,jdk源碼用的也是LinkedList
實現的隊列,LinkedList
底層還是雙向鏈表啦。
其實普通隊列完全可以用兩個棧來模擬。
public class Queue<E> {
private DoubleLinkedList<E> list;
public Queue() {
list = new DoubleLinkedList<>();
}
public boolean isEmpty() {
return list.size() == 0;
}
public void offer(E element) {
list.add(element);
}
public E remove() {
E element = list.get(0);
list.remove(0);
return element;
}
/**
* 返回隊列頭元素
*
* @return
*/
public E peek() {
return list.get(0);
}
public static void main(String[] args) {
Queue<Integer> queue = new Queue<>();
queue.offer(11);
queue.offer(22);
queue.offer(33);
queue.remove();
queue.offer(44);
System.out.println(queue.peek());
while (!queue.isEmpty()) {
System.out.println(queue.remove());
}
}
}
2、測試結果
3、雙端隊列明細
雙端隊列不僅可以在隊列頭刪除元素還可以在隊列尾刪除元素(頭尾都可出隊),同時添加元素既可以在隊列尾也可以在隊列頭(頭尾都可入隊)。隊頭和隊尾都爲用戶可見。
jdk源碼用的也是LinkedList
實現的隊列,LinkedList
底層還是雙向鏈表啦。
public class Deque<E> {
private DoubleLinkedList<E> list; //組合優於繼承
public Deque() {
list = new DoubleLinkedList<>();
}
public boolean isEmpty() {
return list.size() == 0;
}
public void offerFirst(E element) {
list.add(0, element);
}
public void offerLast(E element) {
list.add(element);
}
public E removeFirst() {
E element = list.get(0);
list.remove(0);
return element;
}
public E removeLast() {
E element = list.get(list.size() - 1);
list.remove(list.size() - 1);
return element;
}
public E peekFirst() {
return list.get(0);
}
public E peekLast() {
return list.get(list.size() - 1);
}
public static void main(String[] args) {
Deque<Integer> deque = new Deque<>();
deque.offerFirst(22);
deque.offerFirst(11);
deque.offerLast(33);
deque.offerLast(44);
deque.offerFirst(0);
deque.removeFirst();
while (!deque.isEmpty()) {
System.out.println(deque.removeLast());
}
}
}