如何从Java的链表中删除特定值?

时间:2019-02-07 17:28:12

标签: java algorithm linked-list

如何从链表Java中删除特定值?
我试图在实现中实现它,但这并不容易。

这就是我想要做的:

//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;

}

这些是预设置:

 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;
   }

主要功能:

 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);
   }
  } 

我得到这样的结果。完全没有改变。.

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

仅擦除值c。我不知道为什么。

4 个答案:

答案 0 :(得分:1)

根据问题的详细信息,我假设您在Java中还很陌生。 您的要求和所显示的详细信息完全不同。

  1. LinkedQueue<String> q = new LinkedQueue<String>(); 仅当LinkedQueue是流派类而不是Item类型类的特定隐式时才适用。即您不是在创建LinkedQueue<Item>类的对象。 LinkedQueue<String> and LinkedQueue<Item>是不同的。

  2. cur.equals(item)缺乏同等合同知识和== vs equal中的差异。即您正在比较两个完全不同的对象。一个是节点,另一个是Item类对象。

建议:明确基础知识,请阅读cathy Sierra.Scjp Sun Java 6认证程序员的书

至于答案,您实际上不是在调用main的remove(通过打印进行测试)  删除方法中的语句)。这就是为什么您总是得到相同答案的原因。

注意:即使我们告诉您,您也确实无法消化真正的解决方案。

答案 1 :(得分:1)

以下代码段包含各种remove()方法,这些方法取自我的LinkedList实现之一,以Java编写。


代码

LinkedList.java (部分)

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 (部分)
(单元测试,通过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,
    }
}

所有测试用例都会通过。


说明

方法:

  • T remove(int k),按索引删除。
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),按值删除,仅删除第一个匹配项。
    逻辑类似于按索引删除。
    区别在于:

    • 在初始搜索时,比较元素而不是循环索引以找到目标。
    • 返回布尔值,指示是否已删除,而不是已删除的值,
  • int removeAllValue(T v),按值删除所有内容,删除所有出现的内容。
    这类似于按值删除。

    差异:

    • [内部while()]
    • 它将搜索所有出现的事件,直到结束。
    • 删除事件后,它“继续”以重新检查当前节点。 因为当前节点已实际替换为下一个节点。
    • 如果删除的节点结束,则返回。
      该继电器基于removeNode()的返回值。
    • 它记录已删除事件的计数。
    • [返回值]
    • 它返回删除的事件的计数,而不是布尔值。
  • boolean removeNode(LinkedListNode node, LinkedListNode preNode),按节点删除,并指定preNode。
    删除给定的节点(保证存在),并给定先前的节点(该节点可能为null)。
    返回值指示删除的节点是否为末端,主要用于支持removeAllValue()

  • T removeHead()T removeEnd(),卸下头/端。
    只需调用通过索引删除,并传递相应的索引0size - 1

提示:

  • LinkedList代表链表,具有字段sizeheadend和通用类型T(用于节点中的值类型),它不是线程-安全。
  • checkElementIndex()方法,检查给定的索引,如果超出范围则抛出异常。
  • LinkedListNode,代表链表中的节点。字段为valuenext

复杂度

  • 删除单个:O(k)
  • 按值删除所有内容:O(n)

位置:

  • k是目标的索引。
  • n是链表的大小。

答案 2 :(得分:1)

自Java 8开始,您可以使用removeIf(Predicate<? super E> filter)方法来设置自己的条件。

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

答案 3 :(得分:0)

在if语句中,您正在检查cur Node是否等于传递的Item中的if (cur.equals(item))

我认为您应该检查Item cur中存储的Node是否等于传递给函数Item的{​​{1}}。 / p>