帶頭結點的連結串列操作題

_Git發表於2018-01-23

主體

/**
* 帶頭結點的單連結串列
*
* @param <T>
*/
public class LinkWithHead<T> {
    private Node<T> head;

    private int size;

    private class Node<T> {
        public Node<T> next;
        public T data;

        public Node(T data, Node<T> next) {
            this.next = next;
            this.data = data;
        }
    }

    /**
     * 建構函式
     * 這裡是關鍵,區別了有頭結點和無頭結點的連結串列
     * 這裡的head≠null,但是head的value是null,head的next結點是null
     */
    public LinkWithHead() {
        this.head = new Node<T>(null, null);
        this.size = 0;
    }
}
複製程式碼

判斷連結串列是否為空

	/**
     * 判斷連結串列是否為空
     *
     * @return
     */
    private boolean isEmpty() {
        return head.next == null;
    }
複製程式碼

新增一個結點

    /**
     * 新增一個結點
     *
     * @param node
     */
    private void insert(Node<T> node) {
        Node<T> temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = node;
        size++;
    }
複製程式碼

在指定位置新增一個結點

 /**
     * 指定位置新增一個結點
     *
     * @param node
     * @param index
     */
    private void insert(Node<T> node, int index) {
        if (index > size + 1 || size < 0) {
            throw new ArrayIndexOutOfBoundsException();
        }
        if (head == null && index == 0)
            head.next = node;
        else {
            int currentIndex = 0;
            Node<T> temp = head;
            while (currentIndex < index) {
                temp = temp.next;
                currentIndex++;
            }
            node.next = temp.next;
            temp.next = node;
            size++;
        }
    }
複製程式碼

刪除一個結點(迴圈法)

/**
     * 移除一個結點(迴圈)
     *
     * @param node
     */
    private void remove(Node<T> node) {
        if (head == node) {
            head = null;
            size = 0;
        } else {
            Node<T> temp = head;
            while (temp.next != node) {
                temp = temp.next;
            }
            temp.next = temp.next.next;
            size--;
        }
    }
複製程式碼

刪除指定位置的結點

/**
     * 刪除指定索引的結點
     *
     * @param index
     */
    private void removeByIndex(int index) {
        if (index < 1 || index > size - 1) {
            throw new ArrayIndexOutOfBoundsException();
        }
        int current = 0;
        Node<T> temp = head;
        while (current < index) {
            temp = temp.next;
            current++;
        }
        temp.next = temp.next.next;
    }
複製程式碼

刪除結點時間複雜度O(1)

 /**
     * 刪除結點2
     * 此處需要確認
     *
     * @param node
     */
    private void remove2(Node<T> node) {
        if (head == node) {
            head = null;
            size = 0;
        } else {
            Node<T> temp = node.next;
            node.data = temp.data;
            node.next = temp.next;
            size--;
        }
    }
複製程式碼

用棧的方式刪除指定位置結點

 /**
     * 用棧的方式刪除指定索引的結點
     *
     * @param index
     */
    private void removeByStack(int index) {
        Stack<Node<T>> stack = new Stack<>();
        stack.push(head);
        int current = 0;
        while (head != null) {
            head = head.next;
            current++;
            if (current != index)
                stack.push(head);
        }
        while (!stack.isEmpty()) {
            stack.peek().next = head;
            head = stack.pop();
        }
    }
複製程式碼

刪除連結串列中的重複資料

 /**
     * 刪除重複資料
     */
    private void removeDuplication() {
        HashSet<T> set = new HashSet<>();
        Node<T> previous = head;
        Node<T> current = head.next;
        set.add(head.data);
        while (current != null) {
            if (!set.contains(previous.data)) {
                set.add(previous.data);
                previous = current;
                current = current.next;
            } else {
                previous.next = current.next;
                current = current.next;
            }
        }
    }

複製程式碼

刪除連結串列中倒數第K個結點


 /**
     * 移除倒數第K個結點
     *
     * @param k
     */
    private void removeLastKthNode(int k) {
        if (k < 0 || k > size)
            throw new ArrayIndexOutOfBoundsException();
        Node<T> temp = head;
        for (int i = 0; i < k; i++) {
            temp = temp.next;
        }
        Node<T> previous = head;
        while (temp.next != null) {
            temp = temp.next;
            previous = previous.next;
        }
        previous.next = previous.next.next;
    }
複製程式碼

反轉連結串列

    /**
     * 反轉連結串列遞迴
     */
    private Node<T> reverse(Node<T> node) {
        if (node == null || node.next == null)
            return node;
        Node<T> previous = reverse(node.next);
        node.next.next = node;
        node.next = null;
        return previous;
    }

    /**
     * 連結串列反轉非遞迴
     *
     * @param head
     * @return
     */
    private Node<T> reverse2(Node<T> head) {
        Node<T> previous = null;
        while (head != null) {
            Node<T> temp = head.next;
            head.next = previous;
            previous = head;
            head = temp;
        }
        return previous;
    }

    private void reverse3(Node<T> head) {
        Stack<Node<T>> stack = new Stack<>();
        while (head != null) {
            stack.push(head);
            head = head.next;
        }
        Node<T> first = null;
        while (!stack.isEmpty()) {
            Node<T> temp = stack.pop();
            first.next = temp;
            first = temp;
        }
    }
複製程式碼

查詢一個未知長度連結串列的中間項

 /**
     * 查詢一個未知長度的連結串列的中間結點
     *
     * @return
     */
    private Node<T> findMiddle() {
        Node<T> p = head;
        Node<T> q = head;
        while (q != null && q.next != null && q.next.next != null) {
            p = p.next;
            q = q.next.next;
        }
        return p;
    }
複製程式碼

判斷兩個連結串列是否相交

 /**
     * 判斷兩個連結串列是否相交
     * 參考http://blog.csdn.net/zengzhen_csdn/article/details/49761869
     * 此方法時間複雜度為O(m+n)
     *
     * @param h1
     * @param h2
     * @return
     */
    private boolean isIntersect(Node<T> h1, Node<T> h2) {
        if (h1 == null || h2 == null)
            return false;
        while (h1 != null) {
            h1 = h1.next;
        }
        while (h2 != null) {
            h2 = h2.next;
        }
        return h1 == h2;
    }
複製程式碼

判斷連結串列是否有環,有環的話找出環的入口

private boolean isLoop() {
        Node<T> fast = head;
        Node<T> slow = head;
        while (slow.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast)
                return true;
        }
        return false;
    }


    private Node<T> findLoopNode() {
        Node<T> fast = head;
        Node<T> slow = head;
        while (slow.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast)
                break;
        }
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
複製程式碼

相關文章