【问题标题】:How to remove a specific value from linked list in Java?如何从 Java 中的链表中删除特定值?
【发布时间】:2019-02-07 17:28:11
【问题描述】:

如何从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 阿巴 a z a 删除一些元素。 a b d b 阿巴 a z a

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

【问题讨论】:

    标签: java algorithm linked-list


    【解决方案1】:

    从 Java 8 开始有 removeIf(Predicate&lt;? super E&gt; filter) 方法,您可以在其中放置自己的条件。

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

    【讨论】:

      【解决方案2】:

      根据问题的详细信息,我假设您是 Java 新手。 你问的和你展示的细节完全不同。

      1. LinkedQueue&lt;String&gt; q = new LinkedQueue&lt;String&gt;(); 仅当 LinkedQueue 是类型类而不是 Item 类型类的特定 impl 时才适用。即您没有创建 LinkedQueue&lt;Item&gt; 类的对象。 LinkedQueue&lt;String&gt; and LinkedQueue&lt;Item&gt; 不同。

      2. cur.equals(item) 不知道== vs equal 中的平等契约和差异。即您正在比较两个完全不同的对象。一个是 Node,另一个是 Item 类对象。

      建议:明确基础,阅读 cathy Sierra.Scjp Sun Certified Programmer for Java 6 的书籍

      至于答案,您实际上并没有从 main 调用 remove (通过打印测试它 remove 方法中的语句)。这就是为什么你总是得到相同的答案。

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

      【讨论】:

        【解决方案3】:

        以下代码 sn-p 包含各种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),按索引删除。
        脚步: * 循环到目标节点, * 对于每一步, 记录: * 前一个节点, * 这个节点, * 获取目标节点的下一个节点, * 获取目标节点的值, 稍后作为返回值, * 如果目标是结束, * 如果也是头, 头=空; * 如果不是头, preNode.next = null; * 结束 = 前节点; * 如果目标没有结束, 用下一个节点替换它, 逻辑: * node.value = nextNode.value; * node.next = nextNode.next; * 返回之前跟踪的目标节点值,
        • boolean removeValue(T v),按值删除,仅删除第一个匹配项(如果有)。
          逻辑类似于按索引删除。
          区别在于:

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

          区别:

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

        • T removeHead(), T removeEnd(), 删除头/尾。
          只需按索引调用remove,并传递相应的索引0size - 1

        提示:

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

        复杂性

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

        地点:

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

        【讨论】:

          【解决方案4】:

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

          我认为您应该检查存储在cur Node 中的Item 是否等于传递给您的函数的Itemif (cur.item.equals(item))

          【讨论】:

          • prev.next = cur.next; 对我来说似乎也没有多大意义。 prev 始终是while 循环中的last
          • 我改了问题。
          猜你喜欢
          • 1970-01-01
          • 2018-07-08
          • 1970-01-01
          • 2023-04-08
          • 2020-07-23
          • 2020-02-11
          • 2022-01-22
          • 2012-10-17
          相关资源
          最近更新 更多