删除单链表中特定对象的第二个外观

时间:2014-11-08 17:21:13

标签: java linked-list singly-linked-list

我试图在单链表中删除特定对象的第二个外观。

我的节点有这个代码:

public class Node {
    Node next;

    Object data;

    public Node(Object _data)
    {
        next = null;
        data = _data;
    }

    public Node(Object _data, Node _next)
    {
        next = _next;
        data = _data;
    }

    public Object getData()
    {
        return data;
    }

    public void setData(Object _data)
    {
        data = _data;
    }

    public Node getNext()
    {
        return next;
    }

    public void setNext(Node _next)
    {
        next = _next;
    }
}

这是我删除的功能:

public void removeSecondAppear(Object data)
{
    Node temp = new Node(data);
    Node current = head;

    boolean found = false;

    for(int i = 1; i < size(); i++)
    {
        current = current.getNext();

        if(current.getData().equals(temp.getData()))
        {
            if(found == true)
            {
                // remove element
                current.setNext(current.getNext().getNext());
                listCount--;
                break;
            }
            else if(found == false)
            {
                found = true;
            }

        }
    }

}

由于某种原因,它不会删除该元素。找到它的方法工作正常,但我不知道为什么它不会删除元素。我有一个类似的功能来删除特定索引的元素,工作正常:

public boolean remove(int index)
{
    if(index < 1 || index > size())
    {
        return false;
    }

    Node current = head;

    for(int i = 1; i < index; i++)
    {
        if(current.getNext() == null)
        {
            return false;
        }
        current = current.getNext();
    }

    current.setNext(current.getNext().getNext());

    listCount--;

    return true;
}

我使用相同的方法,但在我的方法中删除第二个外观不起作用。什么帮助我在做什么?

public int indexOf(Object data)
{
    Node temp = new Node(data);
    Node current = head.getNext();

    for(int i = 0; i < size(); i++)
    {
        if(current.getData().equals(temp.getData()))
        {
            return i;
        }
        current = current.getNext();
    }

    return -1;
}

我的实施:

LinkedList LL = new LinkedList();

LL.add(1);
LL.add(2);
LL.add(3);
LL.add(4);
LL.add(4);
LL.add(5);
LL.removeSecondAppear("4");

我的添加方法:

public void add(Object data)
{
    Node temp = new Node(data);
    Node current = head;

    while(current.getNext() != null)
    {
        current = current.getNext();
    }

    current.setNext(temp);

    listCount++;
}

我的构造函数:

public LinkedList()
{
    head = new Node(null);
    listCount = 0;
}

3 个答案:

答案 0 :(得分:2)

您的问题可以在这里找到(在几个地方):

当您循环播放时,您将继续前进current,直到找到两个数据相等的实例,然后将其删除。您的删除操作无效,因为您实际上并没有删除所需的节点,而是您要删除的 next 节点,由于您已经遍历了列表,因此不一定相同并失去了以前的平等节点。

current = current.getNext();

if(current.getData().equals(temp.getData()))
{
    if(found == true)
    {
        // remove element
        current.setNext(current.getNext().getNext()); // this isn't actually removing 'current'...
        listCount--;
        break;
    }
    else if(found == false)
    {
        found = true;
    }

}

首先,在找不到相同的节点后,你没有重置。 在if (equals)块之后,添加:

else {
    found = false;
}

假设你解决了这个问题,这就是你最终的结果。 请看以下示例:

[3] -> [4] -> [4] -> [5] -> [6]

在您的算法中,您将迭代此列表中的每个元素,如下所示:

通过1:

found = false
[3] -> [4] -> [4] -> [5] -> [6]
 ^
 current 
 found = false

通过2:

found = false
[3] -> [4] -> [4] -> [5] -> [6]
        ^
        current  
        found = true

通过3:

found = true
[3] -> [4] -> [4] -> [5] -> [6]
               ^
              current  

当您到达此处时,您将current.next设置为current.next.next,这有效地从列表中删除了[5],而不是4.(因此,这也会导致您的NPE ..当你到达列表的末尾并且没有next.next

时考虑效果

您要做的是找到重复节点的索引并调用现有方法以通过索引删除元素,或者保留previous节点以保存来自 current之前,当您删除时,请设置previous.setNext(current.getNext()),这将有效删除current

其次,您已使用equals Object方法,该方法使用最具辨别力的方法来确定相等性,因为它只返回true两个比较对象指的是同一个对象。虽然这不一定是个问题,但这可能会导致问题,具体取决于您存储的数据类型。对任何对象调用equals将默认为最接近的equals实现,用于该对象表示的实际数据类型,因此如果找不到该数据,则默认为{{1}实现,如果对象不相同,几乎总会给出错误的结果。

  

类Object的equals方法实现最具辨别力   对象可能的等价关系;也就是说,对于任何非null   引用值x和y,当且仅当x时,此方法返回true   和y引用相同的对象(x == y的值为true)。

除此之外,您可能想要更改比较对象数据的方式,但我认为这不会真正引起您太多问题。

最后,您可能希望进行一些Object检查并稍微处理您的循环算法,因为如果重复项位于列表的头部,这个会有问题,但这应该让您指出正确的方向。

这是一种方法,可以帮助阐明我所说的内容:

null

编辑:我使用OP的public void removeSecondAppear(Object data) { Node temp = new Node(data); Node current = head; Node previous = null; boolean found = false; while(current != null) { // for the sake of argument, let's say this will return true if you find equal data if( current.getData() != null && current.getData().equals(temp.getData())) { if(found) { // remove element previous.setNext(current.getNext()); listCount--; break; } else { found = true; } } else { found = false; } previous = current; current = current.getNext(); } } 类定义编写了一个LinkedList实现的一小部分,并使用了一个小测试来确保我的Node方法有效。

removeSecondAppear

使用此测试:

public class LinkedList {
    private Node head;

    public LinkedList() {
        head = new Node(0);
    }
    public LinkedList(Node node) {
        head = node;
    }

    public void add(Node node) {
        Node ptr = head;
        while ( ptr.getNext() != null ) {
            ptr = ptr.getNext();
        }
        ptr.setNext(node);
    }

    ... /// added removeSecondAppear here, but left out to keep it short(er)

    // provided a print() method
}

我的输出是:

public class Test {
    public static void main(String[] args) {
        LinkedList list = new LinkedList(new Node(1));
        list.add(new Node(2));
        list.add(new Node(4));
        list.add(new Node(4));
        list.add(new Node(5));
        list.print();

        list.removeSecondAppearance(4);
        list.print();
    }
}

答案 1 :(得分:1)

添加到pmac89的答案中,如果您对节点类进行通用化,以便您可以对数据类型使用正确的.equals()方法,那会更好:

public class Node<T> {
    Node<T> next;

    T data;

    ...

}

从那时起,您基本上可以将Object替换为T,将Node替换为Node<T>。创建节点时,请指定其类型。然后,说你做了Node<String>。当您对数据发送.equals()时,它会使用String.equals() 而不是Object.equals()

您不想调用Object.equals()的原因是,正如pmac89所说,因为您正在检查是否是同一个对象。你真正想要检查的是它们是否具有相同的值

编辑:
正如Ryan J所提到的,如果您的数据是Object的子类,则默认为该类型的equals()实现(如果有)。

如果你不熟悉它们,这是泛型教程: http://docs.oracle.com/javase/tutorial/java/generics/

答案 2 :(得分:0)

所以问题是你的Node类......

public class Node {
     ...
     Object data;
     ....
}

所以当你打电话时

if(current.getData().equals(temp.getData()))
你的removeSecondAppear中的

,它们将不相等。请记住,对象上的Object.equal()正在比较内存位置。列表项中没有一个彼此相等,只有项目本身。

编辑:你也想要

previous.setNext(current.getNext()) //哎呀,这里也搞错了!!!

编辑2: 你也不排除你正在寻找的东西,所以你找到了自己。详细说明一下,以这种方式思考;

我有清单1,2,3,4,4,5。它找到4次?我猜一次。原因是每个列表项都有一个与另一个不匹配的地址,因为这就是说数据是在内存中分配了一个位置的对象。因此,当您调用Object.equals(someOtherObject)时,您会询问它们在内存中是否具有相同的位置。它们在内存中的位置不同。您在removeSecondAppear期间仅在支票中找到1秒的外观的原因是因为您再次浏览整个列表而不排除您要查找的节点。