基於鏈表底層實現棧

鏈表頭的時間複雜度分析滿足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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章