Как удалить конкретное значение из связанного списка в Java? - PullRequest
0 голосов
/ 07 февраля 2019

Как удалить конкретное значение из связанного списка 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 голосов
/ 08 февраля 2019

Начиная с Java 8 существует removeIf(Predicate<? super E> filter) метод, в котором вы можете поставить свое собственное условие.

list.removeIf(cur -> cur.item.equals(item));
0 голосов
/ 07 февраля 2019

Что касается деталей вопроса, я предполагаю, что вы довольно новы в 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

Что касается ответа, вы буквально не вызываете remove из main (проверьте это с помощью оператора print в методе remove)Вот почему вы продолжаете получать один и тот же ответ.

Примечание: вы действительно не сможете переварить реальное решение, даже если мы скажем.

0 голосов
/ 08 февраля 2019

Следующий фрагмент кода содержит различные remove() методы, взятые из одной из моих LinkedList реализаций, написанные в Java.


Code

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.
    Удалить данный узел, который гарантированно существует, с указанным предыдущим узлом, который может быть нулевым.
    Возвращаемое значение указывает, является ли удаленный узел конечным, это главным образомиспользуется для поддержки removeAllValue().

  • T removeHead(), T removeEnd(), удаление заголовка / конца.
    Просто вызывает удаление по индексу с соответствующим индексом 0 и size - 1 прошло.

Советы:

  • LinkedList представляют связанный список с полями size, head, end иуниверсальный тип T (для типа значения в узле), он не является потокобезопасным.
  • checkElementIndex() метод, проверьте заданный индекс и выведите исключение, если выходит за пределы диапазона.
  • LinkedListNode, представляет узел в связанном списке.с полями value, next.

Сложность

  • Удалить один: O(k)
  • Удалить все по значению: O(n)

Где:

  • k, это индекс цели.
  • n, это размер связанного списка.
0 голосов
/ 07 февраля 2019

В вашем операторе if вы проверяете, равен ли cur Node переданному в Item: if (cur.equals(item)).

Я думаю, вам следует проверить, сохранено ли Itemв cur Node равно Item, переданному в вашу функцию: if (cur.item.equals(item)).

...