java實現鏈表


鏈表是非常常用的數據結構,常見的鏈表有單鏈表、雙向鏈表和雙向循環鏈表。

一個比一個復雜,但實際運用中,越往后越好用。

下面我們使用java分別實現:

一、單鏈表

單鏈表特點:
1.單鏈表的head結點指向第一個數據節點,存數據,沒有tail結點
2.單鏈表的每個節點都有next指針指向下一個節點,但是沒有指針指向前驅節點

/**
 * @Author : wangbin
 * @Date : 2/7/2022 11:17 AM
 * @Description: 單鏈表特點:
 * 1.單鏈表的head結點指向第一個數據節點,存數據,沒有tail結點
 * 2.單鏈表的每個節點都有next指針指向下一個節點,但是沒有指針指向前驅節點
 */
public class SinglyLinkedList<E> implements LinkedList<E> {
    private int size;
    private Node<E> head;

    public SinglyLinkedList() {
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean add(E e) {
        if (head == null) {
            head = new Node<>(e, null);
            size++;
            return true;
        }
        Node<E> cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = new Node<>(e, null);
        size++;
        return true;
    }

    @Override
    public boolean remove(E e) {
        Node<E> cur = head;
        Node<E> curPrev = head;
        while (!cur.item.equals(e) && cur.next != null) {
            curPrev = cur;
            cur = cur.next;
        }
        if (cur.next == null) {
            return false;
        }
        curPrev.next = cur.next;
        size--;
        return true;
    }

    @Override
    public E get(int index) {
        Node<E> curNode = getNode(index);
        return curNode.item;
    }

    private Node<E> getNode(int index) {
        checkIndex(index);
        Node<E> curNode = head;
        int curIdx = 0;
        while (curNode.next != null && curIdx != index) {
            curIdx++;
            curNode = curNode.next;
        }
        return curNode;
    }

    private Node<E> getPrevNode(int index) {
        checkIndex(index);
        Node<E> curNode = head;
        int curIdx = 0;
        while (curIdx != index - 1) {
            curIdx++;
            curNode = curNode.next;
        }
        return curNode;
    }

    @Override
    public E set(int index, E element) {
        checkIndex(index);
        Node<E> node = getNode(index);
        E oldElement = node.item;
        node.item = element;
        return oldElement;
    }

    @Override
    public void add(int index, E element) {
        if (!isPositionIndex(index)) {
            throw new IndexOutOfBoundsException();
        }
        Node<E> prevNode = getPrevNode(index);
        Node<E> curNode = prevNode.next;
        prevNode.next = new Node<>(element, curNode);
        size++;
    }

    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

    @Override
    public E remove(int index) {
        if (index == 0) {
            E item = head.item;
            head = head.next;
            size--;
            return item;
        }
        Node<E> prevNode = getPrevNode(index);
        Node<E> curNode = prevNode.next;
        prevNode.next = curNode.next;
        size--;
        return curNode.item;
    }

    @Override
    public int indexOf(E o) {
        int curIndex = 0;
        for (Node<E> curNode = head; curIndex < size; curNode = curNode.next) {
            if (curNode.item.equals(o)) {
                return curIndex;
            }
            curIndex++;
        }
        return -1;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int nextIndex = 0;

            @Override
            public boolean hasNext() {
                return nextIndex < size;
            }

            @Override
            public E next() {
                return get(nextIndex++);
            }
        };
    }

    private void checkIndex(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
    }

    private static class Node<E> {
        E item;
        Node<E> next;

        public Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }
}

二、雙向鏈表

雙向鏈表的特點:
1.雙向鏈表既有head節點,也有tail節點;
2.head節點指向第一個節點,head.prev==null;
3.tail節點指向最后一個節點,tail.next==null;
4.相比雙向循環鏈表,雙向鏈表沒有使用哨兵,所以需要處理復雜的邊界條件
/**
 * @Author : wangbin
 * @Date : 2/7/2022 11:18 AM
 * @Description: 雙向鏈表的特點:
 * 1.雙向鏈表既有head節點,也有tail節點;
 * 2.head節點指向第一個節點,head.prev==null;
 * 3.tail節點指向最后一個節點,tail.next==null;
 * 相比雙向循環鏈表,雙向鏈表沒有使用哨兵,所以需要處理復雜的邊界條件
 */
public class DoublyLinkedList<E> implements LinkedList<E> {
    private int size = 0;
    private Node<E> head;
    private Node<E> tail;

    public DoublyLinkedList() {
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean add(E e) {
        if (head == null) {
            Node<E> newNode = new Node<>(e, null, null);
            this.head = newNode;
            this.tail = newNode;
            size++;
        } else {
            Node<E> curNode = head;
            while (curNode.next != null) {
                curNode = curNode.next;
            }
            Node<E> newNode = new Node<>(e, null, curNode);
            curNode.next = newNode;
            this.tail = newNode;
            size++;
        }
        return true;
    }

    @Override
    public boolean remove(E e) {
        for (Node<E> curNode = head; curNode != null; curNode = curNode.next) {
            if (curNode.item.equals(e)) {
                Node<E> prev = curNode.prev;
                Node<E> next = curNode.next;
                if (prev == null) {
                    head = next;
                } else {
                    prev.next = next;
                }
                if (next == null) {
                    tail = prev;
                } else {
                    next.prev = prev;
                }
                size--;
                return true;
            }
        }
        return false;
    }

    @Override
    public E get(int index) {
        checkIndex(index);
        if (index < size / 2) {
            Node<E> curNode = head;
            for (int curIndex = 0; curIndex < index; curIndex++) {
                curNode = curNode.next;
            }
            return curNode.item;
        } else {
            Node<E> curNode = tail;
            for (int curIndex = size - 1; curIndex > index; curIndex--) {
                curNode = curNode.prev;
            }
            return curNode.item;
        }
    }

    private void checkIndex(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
    }

    @Override
    public E set(int index, E element) {
        checkIndex(index);
        Node<E> curNode = getNode(index);
        E item = curNode.item;
        curNode.item = element;
        return item;
    }

    private Node<E> getNode(int index) {
        checkIndex(index);
        if (index < size / 2) {
            Node<E> curNode = head;
            for (int curIndex = 0; curIndex < index; curIndex++) {
                curNode = curNode.next;
            }
            return curNode;
        } else {
            Node<E> curNode = tail;
            for (int curIndex = size - 1; curIndex > index; curIndex--) {
                curNode = curNode.prev;
            }
            return curNode;
        }
    }

    @Override
    public void add(int index, E element) {
        checkIndex(index);
        Node<E> oldNode = getNode(index);
        Node<E> prev = oldNode.prev;
        Node<E> newNode = new Node<>(element, null, null);
        if (prev == null) {
            head = newNode;
        } else {
            prev.setNext(newNode);
            newNode.setPrev(prev);
        }
        newNode.setNext(oldNode);
        oldNode.setPrev(newNode);
        size++;

    }

    @Override
    public E remove(int index) {
        checkIndex(index);
        Node<E> node = getNode(index);
        Node<E> next = node.next;//next和prev對象都有可能為null,所有下面需要進行判斷
        Node<E> prev = node.prev;
        E item = node.item;

        if (prev == null) {
            head = next;
        } else {
            prev.next = next;
        }
        if (next == null) {
            tail = prev;
        } else {
            next.prev = prev;
        }
        size--;
        return item;
    }

    @Override
    public int indexOf(E o) {
        int curIndex = 0;
        for (Node<E> curNode = head; curNode.next != null; curNode = curNode.next) {
            if (curNode.item.equals(o)) {
                return curIndex;
            }
            curIndex++;
        }
        return -1;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            private int nextIndex;

            @Override
            public boolean hasNext() {
                return nextIndex < size;
            }

            @Override
            public E next() {
                return get(nextIndex++);
            }
        };
    }

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        public Node(E item, Node<E> next, Node<E> prev) {
            this.item = item;
            this.next = next;
            this.prev = prev;
        }

        public void setNext(Node<E> next) {
            this.next = next;
        }

        public void setPrev(Node<E> prev) {
            this.prev = prev;
        }
    }
}

三、雙向循環鏈表

雙向循環鏈表特點:
1.有head節點,無tail節點
2.與單鏈表和雙向鏈表不同,為簡化操作,雙向循環鏈表使用一個空哨兵節點作為head節點
3.head節點的next指針指向第一個節點,head節點的prev指針指向最后一個節點;
4.最后一個節點的next指針指向head節點,
5.判斷是否為空。head==head.next
/**
 * @Author : wangbin
 * @Date : 2/7/2022 11:18 AM
 * @Description: 雙向循環鏈表特點:
 * 1.有head節點,無tail節點
 * 2.與單鏈表和雙向鏈表不同,為簡化操作,雙向循環鏈表使用一個空哨兵節點作為head節點
 * 3.head節點的next指針指向第一個節點,head節點的prev指針指向最后一個節點;
 * 4.最后一個節點的next指針指向head節點,
 * 5.判斷是否為空。head==head.next
 */
public class CircularLinkedList<E> implements LinkedList<E> {
    private int size = 0;
    private Node<E> head;

    public CircularLinkedList() {
        this.head = new Node<>(null, null, null);
        this.head.prev = this.head;
        this.head.next = this.head;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean add(E e) {
        if (head.next == head) {
            Node<E> newNode = new Node<>(e, null, null);
            this.head.next = newNode;
            this.head.prev = newNode;
            newNode.setNext(head);
            newNode.setPrev(head);
            size++;
        } else {
            Node<E> curNode = head;
            while (curNode.next != head) {
                curNode = curNode.next;
            }
            Node<E> newNode = new Node<>(e, head, curNode);
            curNode.next = newNode;
            head.prev = newNode;
            size++;
        }
        return true;
    }

    @Override
    public boolean remove(E e) {
        for (Node<E> curNode = head.next; curNode.item != null; curNode = curNode.next) {
            if (curNode.item.equals(e)) {
                Node<E> prev = curNode.prev;
                Node<E> next = curNode.next;
                prev.next = next;
                next.prev = prev;
                return true;
            }
        }
        return false;
    }

    @Override
    public E get(int index) {
        checkIndex(index);
        if (index < size / 2) {
            Node<E> curNode = head.next;
            for (int curIndex = 0; curIndex < index; curIndex++) {
                curNode = curNode.next;
            }
            return curNode.item;
        } else {
            Node<E> curNode = head.prev;
            for (int curIndex = size - 1; curIndex > index; curIndex--) {
                curNode = curNode.prev;
            }
            return curNode.item;
        }
    }

    private void checkIndex(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
    }

    @Override
    public E set(int index, E element) {
        checkIndex(index);
        Node<E> curNode = getNode(index);
        E item = curNode.item;
        curNode.item = element;
        return item;
    }

    private Node<E> getNode(int index) {
        checkIndex(index);
        if (index < size / 2) {
            Node<E> curNode = head.next;
            for (int curIndex = 0; curIndex < index; curIndex++) {
                curNode = curNode.next;
            }
            return curNode;
        } else {
            Node<E> curNode = head.prev;
            for (int curIndex = size - 1; curIndex > index; curIndex--) {
                curNode = curNode.prev;
            }
            return curNode;
        }
    }

    @Override
    public void add(int index, E element) {
        checkIndex(index);
        Node<E> oldNode = getNode(index);
        Node<E> prev = oldNode.prev;
        Node<E> newNode = new Node<>(element, oldNode, prev);
        prev.setNext(newNode);
        oldNode.setPrev(newNode);
        size++;
    }

    @Override
    public E remove(int index) {
        checkIndex(index);
        Node<E> node = getNode(index);
        Node<E> next = node.next;
        Node<E> prev = node.prev;
        E item = node.item;
        prev.next = next;
        next.prev = prev;

        size--;
        return item;
    }

    @Override
    public int indexOf(E o) {
        int curIndex = 0;
        for (Node<E> curNode = head.next; curNode.next != head; curNode = curNode.next) {
            if (curNode.item.equals(o)) {
                return curIndex;
            }
            curIndex++;
        }
        return -1;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            private Node<E> curNode = head.next;

            @Override
            public boolean hasNext() {
                return curNode.item != null;
            }

            @Override
            public E next() {
                E item = curNode.item;
                curNode = curNode.next;
                return item;
            }
        };
    }

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        public Node(E item, Node<E> next, Node<E> prev) {
            this.item = item;
            this.next = next;
            this.prev = prev;
        }

        public void setNext(Node<E> next) {
            this.next = next;
        }

        public void setPrev(Node<E> prev) {
            this.prev = prev;
        }
    }
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM