How to remove a specific value from linked list in

2020-07-18 10:31发布

问题:

How to remove a specific value from a linked list java?
I tried to make it in my implementation, but it wasn't easy..

Here is what I'm trying to make:

//How to do this...;<..
int remove(Item item) {
    Node cur = first.next;
    Node prev = first;
    while (cur !=null) {
        if (cur.item.equals(item)) {
            item = dequeue();
        }
        cur = cur.next;

        // TODO 
    }


return 0;

}

These are the pre-setup:

 public class LinkedQueue<Item> implements Iterable<Item> {
   private int N;         // number of elements on queue
   private Node first;    // beginning of queue
   private Node last;     // end of queue

  // helper linked list class
 private class Node {
    private Item item;
    private Node next;
}

/**
 * Initializes an empty queue.
 */
public LinkedQueue() {
    first = null;
    last  = null;
    N = 0;
    assert check();
}

  public Item dequeue() {
      if (isEmpty()) throw new NoSuchElementException("Queue 
    underflow");
    Item item = first.item;
    first = first.next;
    N--;
    if (isEmpty()) last = null;   // to avoid loitering
    assert check();
    return item;
   }

And the main function:

 public static void main(String[] args) {
    LinkedQueue<String> q = new LinkedQueue<String>();

    q.enqueue("a");
    q.enqueue("b");
    q.enqueue("c");
    q.enqueue("a");
    q.enqueue("b");
    q.enqueue("d");
    q.enqueue("b");
    q.enqueue("abba");
    q.enqueue("a");
    q.enqueue("z");
    q.enqueue("a");
    System.out.println(q);
    System.out.println("Remove some of elements.");

    q.remove("a");
    q.remove("f");
    q.remove("c");
    System.out.println(q);
   }
  } 

And I have a result like this. It doesn't change at all..

         
 a b c a b d b abba a z a 
 Remove some of elements.
 a b d b abba a z a 

It only erase value c. I don't know why.

回答1:

As per the details of question,i assume you are fairly new in Java. What you are asking and the details you are showing are totally different.

  1. LinkedQueue<String> q = new LinkedQueue<String>(); is only applicable if LinkedQueue is a genreic class not a specific impl for Item type class. i.e you are not creating object of LinkedQueue<Item> class. LinkedQueue<String> and LinkedQueue<Item> is different.

  2. cur.equals(item) lack of knowledge of equal contract and difference in == vs equal. i.e you are comparing a two totally different object. One is a Node and other is Item class object.

Suggestion: clear basics, Read book by cathy Sierra.Scjp Sun Certified Programmer for Java 6

As for answer, you are literally not calling remove from main (test it via a print statement in remove method). That is why you keep getting same answer.

Note: Your really not can't digest real solution even if we tell.



回答2:

Since Java 8 there is removeIf(Predicate<? super E> filter) method where you can put your own condition.

list.removeIf(cur -> cur.item.equals(item));


回答3:

Following code snippet contains various remove() methods, taken from one of my LinkedList implementation, written in Java.


Code

LinkedList.java (partly)

private int size; // node count,
private LinkedListNode<T> head; // first node,
private LinkedListNode<T> end; // last node,

/**
 * Remove by index.
 *
 * @param k index, start from 0,
 * @return value of removed node, or null if not removed,
 */
@Override
public T remove(int k) {
    checkElementIndex(k);

    // find target node, and remember previous node,
    LinkedListNode<T> preNode = null;
    LinkedListNode<T> node = head;
    while (k-- > 0) {
        preNode = node;
        node = node.next;
    }

    T result = (T) node.value; // keep return value,

    removeNode(node, preNode); // remove

    return result;
}

/**
 * Remove by value, only remove the first occurrence, if any.
 *
 * @param v
 * @return whether removed,
 */
@Override
public boolean removeValue(T v) {
    // find target node, and remember previous node,
    LinkedListNode<T> preNode = null;
    LinkedListNode<T> node = head;
    while (true) {
        if (node == null) return false;// not found,
        if (node.getValue().compareTo(v) == 0) break; // value found,

        preNode = node;
        node = node.next;
    }

    removeNode(node, preNode); // remove

    return true;
}

/**
 * Remove by value, remove all occurrences.
 *
 * @param v
 * @return count of nodes removed,
 */
@Override
public int removeAllValue(T v) {
    int rc = 0;

    // find target node, and remember previous node,
    LinkedListNode<T> preNode = null;
    LinkedListNode<T> node = head;
    while (true) {
        if (node == null) return rc; // reach end,
        if (node.getValue().compareTo(v) == 0) { // value found,
            rc++;
            if (removeNode(node, preNode)) break; // remove, break if it's end,
            continue; // recheck this node, since it become the next node,
        }

        preNode = node;
        node = node.next;
    }

    return rc;
}

/**
 * Remove given node, which guarantee to exists. Also reduce the size by 1.
 *
 * @param node    node to delete,
 * @param preNode previous node, could be null,
 * @return indicate whether removed node is end,
 */
protected boolean removeNode(LinkedListNode node, LinkedListNode preNode) {
    LinkedListNode nextNode = node.next; // next node,
    boolean isEnd = (nextNode == null);
    if (isEnd) { // target is end,
        if (preNode == null) { // target is also head,
            head = null;
        } else { // target is not head, thus preNode is not null,
            preNode.next = null;
        }
        end = preNode;

    } else { // target is not end,
        // replace target with next node,
        node.next = nextNode.next;
        node.value = nextNode.value;
    }

    size--; // reduce size by 1,

    return isEnd;
}

/**
 * Remove head node,
 *
 * @return
 */
@Override
public T removeHead() {
    return remove(0);
}

/**
 * Remove end node,
 *
 * @return
 */
@Override
public T removeEnd() {
    return remove(size - 1);
}

LinkedListTest.java (partly)
(unit test, via TestNG)

import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * LinkedList test.
 *
 * @author eric
 * @date 1/28/19 6:03 PM
 */
public class LinkedListTest {
    private int n = 10;
    private LinkedList<Integer> llist; // linked list,
    private LinkedList<Integer> dupEvenLlist; // linked list, with duplicated even values,

    @BeforeMethod
    public void init() {
        // init llist,
        llist = new LinkedList(); // create linked list,
        Assert.assertTrue(llist.isEmpty());
        LinkedList.appendRangeNum(llist, 0, n); // append range,

        // init dupEvenLlist,
        dupEvenLlist = new LinkedList(); // create linked list,
        LinkedList.appendRangeNum(dupEvenLlist, 0, n); // append range,
        LinkedList.appendRangeNum(dupEvenLlist, 0, n, 2); // append range, again, with step as 2 (only even numbers),
        Assert.assertEquals(dupEvenLlist.size(), n + n / 2);
    }

    // non-remove related test cases ... are deleted,

    // remove(k) - remove by index,
    @Test
    public void testRemoveByIndex() {
        for (int i = 0; i < n; i++) {
            Assert.assertEquals(llist.removeEnd().intValue(), n - 1 - i); // remove by end, in turn it remove by index,
            Assert.assertEquals(llist.size(), n - 1 - i);
        }
        Assert.assertTrue(llist.isEmpty());
    }

    // remove(v) - remove by value,
    @Test
    public void testRemoveByValue() {
        Assert.assertFalse(llist.removeValue(n)); // not exists,

        for (int i = n - 1; i >= 0; i--) {
            Assert.assertTrue(llist.removeValue(i)); // remove by value,
            Assert.assertEquals(llist.size(), i);
        }
        Assert.assertTrue(llist.isEmpty());

        Assert.assertFalse(llist.removeValue(0)); // empty,

        // remove from list with duplicated value,
        for (int i = 0; i < n; i++) {
            Assert.assertTrue(dupEvenLlist.removeValue(i));
        }
        Assert.assertFalse(dupEvenLlist.isEmpty());
        Assert.assertEquals(dupEvenLlist.size(), n / 2);
    }

    // removeAll(v) - remove all occurrences by value,
    @Test
    public void testRemoveAllByValue() {
        Assert.assertEquals(dupEvenLlist.removeAllValue(n), 0); // not exists,

        int remainSize = dupEvenLlist.size();
        for (int i = 0; i < n; i++) {
            int rc = dupEvenLlist.removeAllValue(i); // remove all by value,
            Assert.assertEquals(rc, i % 2 == 0 ? 2 : 1);
            remainSize -= rc;
            Assert.assertEquals(dupEvenLlist.size(), remainSize);
        }
        Assert.assertTrue(dupEvenLlist.isEmpty());

        Assert.assertEquals(dupEvenLlist.removeAllValue(0), 0); // empty,
    }
}

All test cases would pass.


Explanation

Methods:

  • T remove(int k), remove by index.
Steps:
* loop to target node,
    * for each step,
        record:
        * previous node,
        * this node,
* get next node, of target node,
* get value of target node,
    as return value later,
* if target is end,
    * if also head,
        head = null;
    * if not head,
        preNode.next = null;
    * end = preNode;
* if targe is not end,
    replace it with its next node,
    logic:
    * node.value = nextNode.value;
    * node.next = nextNode.next;
* return previously tracked value of target node,
  • boolean removeValue(T v), remove by value, only remove first occurrence, if any.
    The logic is similar as remove by index.
    The differences are:

    • At initial search, compare element instead of loop to index, to find target.
    • Return boolean that indicate whether removed, instead of removed value,
  • int removeAllValue(T v), remove all by value, remove all occurrences.
    This is similar as remove by value.

    Differences:

    • [inside while()]
    • It will search all occurrence till end.
    • After removing an occurrence, it "continue" to recheck the current node. Because the current node has actual replaced by it's next.
    • If removed node is end, then return.
      This relay on the return value of removeNode().
    • It record count of removed occurrence.
    • [return value]
    • It return count of removed occurrence, instead of boolean.
  • boolean removeNode(LinkedListNode node, LinkedListNode preNode), remove by node, with preNode given.
    Remove given node, which is guaranteed to exists, with previous node given, which might be null.
    Return value indicate whether removed node is end, it's mainly used to support removeAllValue().

  • T removeHead(), T removeEnd(), remove head / end.
    Simply calls remove by index, with corresponding index 0 and size - 1 passed.

Tips:

  • LinkedList represent linkedlist, with fields size, head, end, and generic type T (for value type in node), it's not thread-safe.
  • checkElementIndex() method, check given index, and throw exception if out of range.
  • LinkedListNode, represent the node in linked list. with fields value, next.

Complexity

  • Remove single: O(k)
  • Remove all by value: O(n)

Where:

  • k, is the index of target.
  • n, is size of linkedlist.


回答4:

In your if statement you are checking if the cur Node is equal to the Item passed in: if (cur.equals(item)).

I think you should be checking if the Item stored in the cur Node is equal to the Item passed into your function: if (cur.item.equals(item)).