删除链表中所有出现的元素

Delete all occurrences of an element in a linkedlist

为了理解数据结构,我开始在 java.The 中实现它们 deleteAll 的时间复杂度为 O(n + n^2)。我如何改进 deleteAll 方法?

/*
 * Singly linked list
 */
package linkedlisttest;

class Node {

    int data;
    Node next;

    public Node(int data) {
        this.data = data;
    }

}

class LinkedList {

    Node head;
    int size;

    /**
     *
     * @param data element to add to list 
     * Time Complexity : O(n)
     */
    public void add(int data) {
        if (head == null) {
            head = new Node(data);
            size += 1;
            return;
        }

        Node current = head;
        while (current.next != null) {
            current = current.next;
        }
        current.next = new Node(data);
        size += 1;
    }

    /**
     *
     * @return size of list 
     * Time Complexity: O(1) 
     * This is because we use a class
     * variable size to keep track of size of linked list
     */
    public int getSize() {
        return size;
    }
    /**
     * 
     * @param data element to insert 
     * @param index position at which to insert the element (zero based)
     * Time Complexity : O(n)
     */
    public void add(int data, int index) {

        if (index > getSize()) {
            return; // invalid position
        }

        Node current = head; //iterate through whole list 
        int pos = 0;
        Node newNode = new Node(data);

        if (index == 0) // special case, since its a single reference change!
        {
            newNode.next = head;
            head = newNode; // this node is now the head
            size += 1;
            return;
        }
        while (current.next != null) {
            if (pos == index - 1) {
                break;
            }
            pos++;
            current = current.next;
        }
        // These are 2 reference changes, as compared to adding at index 0
        newNode.next = current.next; // here we are changing a refernce
        current.next = newNode; // changing a reference here as well
        size += 1;

    }
    /**
     * Find the first occurrence of an element  
     * @param data element to find
     * @return index at which element is found , -1 if not exist
     * Time Complexity: O(n)
     */
    public int find(int data) {
        Node current = head;
        int pos = 0;
        int index = -1;
        if(head == null) { //empty list
            return index;
        }
        while(current != null) {
            if (current.data == data) {
                index = pos;
                break;
            }
            pos++;
            current = current.next;
        }
        return index;
    }

    /**
     * Delete the first occurrence of data 
     * @param data element to delete 
     * Time complexity : O(n)
     */
    public void delete(int data) {
        Node current = head;
        if (head == null) { // list is empty
            return;
        }
        if(head.data == data) { // if we want to delete the head , make next node head
            head = head.next;
            size -= 1;
            return;
        }
        while(current.next != null) {
            if (current.next.data == data) {
                current.next = current.next.next;
                size -= 1;
                return;
            }
            current = current.next;
        }
    }

    /**
     * Delete all occurrences of data 
     * @param data element to delete 
     * 
     */
    public void deleteAll(int data) {
        Node current = head;
        if (head == null) { // list is empty
            return;
        }
        //while loop to delete consecutive occurances of data
        while(head.data == data) { // if we want to delete the head , make next node head
            head = head.next;
            size -= 1;
        }
        while(current.next != null) {
            //while loop to delete consecutive occurances of data
            while (current.next.data == data) { 
                current.next = current.next.next;
                size -= 1;
            }
            current = current.next;
        }
    }

    public void reverse() {

    }



    /**
     * Prints the whole linked list 
     * Time Complexity : O(n)
     */
    public void print() {

        if(head == null) { //list is empty
            return;
        }
        Node current = head;
        while (current.next != null) {
            System.out.print(current.data + "->");
            current = current.next;
        }
        System.out.print(current.data + "\n"); 
    }
}

public class LinkedListTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        LinkedList lt = new LinkedList();
        lt.print();
        lt.add(3);
        lt.add(5);
        lt.add(6);
        lt.print();
        lt.add(4, 1);
        lt.print();
        lt.add(4, 7);// 7 is an invalid index
        lt.add(8, 3);
        lt.add(8, 4);
        lt.print();
        System.out.println("Position : " + lt.find(8));
        lt.delete(5);
        lt.print();
        lt.deleteAll(8);
        lt.print();
        System.out.println("Size : " + lt.getSize());
    }

}

请注意,Java 中的所有内容都是引用,除了那些原始类型。所以current还是停留在原来的head.

没有必要将连续出现的情况视为特例。使用 O(n) 对链表进行简单的迭代和判断 - 线性复杂度将完全满足您的要求。

此外,嵌套循环并不一定意味着它一定会花费 O(n^2)。如果你每次移动current,它仍然以线性时间遍历链表。

另一个个人建议:如果你在处理链表时需要写类似node.next.next的东西,是时候设置另一个引用或重构你的代码了。

您实施的时间复杂度是 O(n),而不是您认为的 O(n + n^2)。 尽管嵌套循环是 O(n^2) 的常见标志,但情况并非总是如此。 重要的一点是迭代次数。 在你的情况下, 如果您在嵌套循环中采取 k 步, 将外循环中的剩余步骤减少 k。 全面的, 您仍需要 n 步才能到达终点。

但是你的实现有一些错误, 还可以改进:

  • 错误:你赋值current = head,但是如果head.data == data那么它会被删除,current仍然指向它
  • 不需要嵌套循环。对头部进行特殊处理后,可以简单的跟随节点,删除匹配项

像这样:

public void deleteAll(int data) {
    while (head != null && head.data == data) {
        head = head.next;
        size -= 1;
    }

    if (head == null) {
        return;
    }

    Node current = head;
    while (current.next != null) {
        if (current.next.data == data) {
            current.next = current.next.next;
            size -= 1;
        } else {
            current = current.next;
        }
    }
}

顺便说一句,头部的特殊处理有点烦人。 一个优雅的替代方法是创建一个指向 head:

的虚拟对象
public void deleteAll(int data) {
    Node dummy = new Node();
    dummy.next = head;

    Node current = dummy;
    while (current.next != null) {
        if (current.next.data == data) {
            current.next = current.next.next;
            size -= 1;
        } else {
            current = current.next;
        }
    }

    head = dummy.next;
}