鏈表頭的時間複雜度分析滿足O(1)
- 如果只對鏈表頭進行增刪的話,它的時間複雜度是O(1)的
- 只查鏈表頭的元素,它的時間複雜度也是O(1)的
滿足以上兩個條件的數據結構是棧,對於棧是後進先出的, 只對棧的一端,棧頂進行操作。無論是添加元素,刪除元素,還是查看元素都在棧頂進行。
所以可以把鏈表頭當作棧頂,用鏈表底層實現一個棧。
棧的接口
Stack.java
public interface Stack<E> {
void push(E e); // 向棧中添加一個元素,入棧
E pop(); // 拿出棧頂的元素,出棧
E peek(); // 看一下棧頂的元素是誰
int getSize(); // 看一下棧裏面一共有多少個元素
boolean isEmpty(); // 看一下棧是否爲空
}
複用鏈表類LinkedList
關於鏈表類,在我的另一篇文章《實現一個屬於自己的鏈表類》進行了底層實現,直接將第七節的完整代碼拿來複用。
LinkedList.java
// 支持泛型
public class LinkedList<E> {
// 節點設計成鏈表類中的內部類
// 設計私有類,只有在鏈表數據結構內纔可以訪問到Node
private class Node {
// 設計成public 在LinkedList中可以隨意訪問操作 不需要設置get,set方法
public E e; // 存放元素
public Node next; // 指向Node的引用
public Node(E e, Node next) {
// 將用戶傳來的數據交給節點
this.e = e;
this.next = next;
}
// 用戶只傳來e
public Node(E e) {
this(e, null);
}
// 用戶什麼都不傳
public Node() {
this(null, null);
}
// 對每一個節點設置toString方法
@Override
public String toString() {
// 每一個節點,直接打印e所對應的toString
return e.toString();
}
}
// 修改爲dummyHead設置虛擬頭節點
private Node dummyHead;
// 用戶不能在外部直接修改size
private int size; // 來記錄鏈表中有多少個元素
// 鏈表構造函數
public LinkedList() {
// 對於一個空的鏈表來說,它是存在一個節點的,這個節點就是唯一的虛擬頭節點
dummyHead = new Node(null, null);
size = 0;
}
// 獲取鏈表中元素的個數
public int getSize() {
return size;
}
// 返回鏈表是否爲空
public boolean isEmpty() {
// size是否爲0
return size == 0;
}
// 使用虛擬頭節點添加元素
public void add(int index, E e) {
// 需要先判斷index的合法性
if (index < 0 || index > size) {
throw new IllegalArgumentException("Add failed.Illegal index.");
}
// 此時dummyHead指向的是0元素之前一個的位置的節點
Node prev = dummyHead;
// 只需要遍歷到index就可以了,因爲是從dummyHead開始遍歷的
for (int i = 0; i < index; i++) {
// 當前prev存的節點的下一個節點放進prev中
// 遍歷一直挪動prev位置存放下一個節點
prev = prev.next;
}
prev.next = new Node(e, prev.next);
// 插入之後維護size
size++;
}
// 在鏈表頭添加新的元素
public void addFirst(E e) {
add(0, e);
}
// 在鏈表的末尾添加一個新的元素e
public void addLast(E e) {
// 複用add(),只需要在size添加即可
add(size, e);
}
// 查詢操作
public E get(int index) {
// get之前先判斷合法性
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Add failed.Illegal index.");
}
// 遍歷鏈表,是從索引爲0開始的,從當前的開始遍歷
Node cur = dummyHead.next;
for (int i = 0; i < index; i++)
cur = cur.next;
// 最終cur裏存儲的e就是需要查找的元素
return cur.e;
}
// 獲取鏈表第一個元素
public E getFirst() {
return get(0);
}
// 獲取鏈表最後第一個元素
public E getLast() {
return get(size - 1);
}
// 鏈表的更新,修改
public void set(int index, E e) {
// 判斷合法
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Add failed.Illegal index.");
}
// 進行一次遍歷,找到第index元素進行替換
Node cur = dummyHead.next;
for (int i = 0; i < index; i++)
cur = cur.next;
// 最終cur裏存儲的e等於新的e
cur.e = e;
}
// 查找鏈表中是否存在元素e
public boolean contains(E e) {
// 設置cur從第一個節點開始
Node cur = dummyHead.next;
// 不知道循環多少次使用while
// cur 節點不等他null的話,意味着當前cur節點是一個有效節點
while (cur != null) {
// cur.e是用戶傳來的e,返回true
if (cur.e.equals(e))
return true;
// 否則就看下一個節點
cur = cur.next;
// 直到cur爲空,說明把整個鏈表遍歷了一遍
}
return false;
}
// 刪除鏈表中index位置元素 返回刪除的元素
public E remove(int index) {
// 判斷合法
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Add failed.Illegal index.");
}
// prev存了待刪除節點之前的節點
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
// 將刪除的節點保存進retNode;
Node retNode = prev.next;
// 將當前的前一個元素的節點指向當前後一個節點
prev.next = retNode.next;
// 讓刪除元素的節點的指向爲空
retNode.next = null;
// 維護size
size--;
// 將刪除的元素返回
return retNode.e;
}
// 刪除第一個元素,返回刪除的元素
public E removeFirst() {
return remove(0);
}
// 刪除第一個元素,返回刪除的元素
public E removeLast() {
return remove(size - 1);
}
@Override
public String toString() {
// 需要遍歷一邊整個鏈表中的所有的元素
StringBuilder res = new StringBuilder();
for (Node cur = dummyHead.next; cur != null; cur = cur.next) {
res.append(cur.e + "->");
}
// 最後跟一個空表示達到了鏈表的結尾
res.append("NULL");
return res.toString();
}
}
通過鏈表實現棧
通過實現接口,實現棧LinkedListStack<E>
LinkedListStack.java
public class LinkedListStack<E> implements Stack<E> {
// 私有鏈表對象
private LinkedList<E> list;
// 由於是鏈表棧,底層實現鏈表沒有容基的概念,一個構造函數就夠了
public LinkedListStack() {
list = new LinkedList<>();
}
// 棧裏面一共有多少個元素
@Override
public int getSize() {
return list.getSize();
}
// 看一下棧是否爲空
@Override
public boolean isEmpty() {
return list.isEmpty();
}
// 向棧中添加一個元素,入棧
@Override
public void push(E e) {
list.addFirst(e);
}
// 拿出棧頂的元素,出棧
@Override
public E pop() {
return list.removeFirst();
}
// 看一下棧頂的元素是誰
@Override
public E peek() {
return list.getFirst();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack: top [");
res.append(list);
res.append("]");
return res.toString();
}
}
測試類Main.java
public class Main {
public static void main(String[] args) {
LinkedListStack<Integer> stack = new LinkedListStack<>();
// 入棧測試
for (int i = 0; i < 5; i++) {
stack.push(i);
System.out.println(stack);
}
// 進行一次出棧
stack.pop();
System.out.println(stack);
}
}
測試結果
棧類中toString()中添加了top來標記棧頂的位置,直觀結果如下:
Stack: top [0->NULL]
Stack: top [1->0->NULL]
Stack: top [2->1->0->NULL]
Stack: top [3->2->1->0->NULL]
Stack: top [4->3->2->1->0->NULL]
Stack: top [3->2->1->0->NULL]