【问题标题】:Java - Custom Singly Linked List , Delete all occurrences of elements with a particular valueJava - 自定义单链表,删除所有出现的具有特定值的元素
【发布时间】:2013-03-24 00:11:35
【问题描述】:

我在 java 中制作了一个自定义的 LinkedList,它有点像地图和列表之间的交叉。我做这个练习只是为了学习,我知道 HashMap 是一个更好更快的实现。我为 LinkedList 实现了 delete 方法,但对于哪种是编写方法的最佳方式有点困惑:deleteAll 基本上删除了特定元素的所有出现。

代码:

public class LinkedListMain
{
    public static void main(String[] args)
    {
        LinkedList linkedList = new LinkedList();

        System.out.println("isEmpty: " + linkedList.isEmpty());

        linkedList.insert("abc", 34);
        linkedList.insert("pqr", 44);
        linkedList.insert("xyz", 54);
        linkedList.insert("asd", 64);
        linkedList.insert("abc", 74);

        linkedList.print();

/*      System.out.println("delete: " + linkedList.delete("abc"));
        System.out.println("delete: " + linkedList.delete("pqr"));
        System.out.println("delete: " + linkedList.delete("xyz"));
        System.out.println("delete: " + linkedList.delete("asd"));
*/
        System.out.println("deleteAll: " + linkedList.deleteAll("abc"));
        System.out.println("isEmpty: " + linkedList.isEmpty());
    }
}

class LinkedList
{
    private ListNode first;
    private ListNode last;

    public LinkedList()
    {
        first = null;
        last = first;
    }

    public void insert(String d1, int d2)
    {
        ListNode node = new ListNode(d1, d2);

        if(first == null)
        {
            node.next = null;
            first = node;
            last = node;
        }

        else
        {
            last.next = node;
            node.next = null;
            last = node;
        }
    }

    public String deleteAll(String str)
    {
        return "To Be Implemented";
    }

    public String delete(String str)
    {
        ListNode slow = first;
        ListNode fast = first;

        int count = 0;

        while(fast != null)
        {
            if(count > 1)
            {
                slow = slow.next;
            }

            if(count <= 1)
            {
                count++;
            }

            if(fast.getVal()==str)
            {
                if(fast == first)
                {
                    first = first.next;
                }

                else
                {
                    if(fast.next != null)
                    {
                        slow.next = fast.next;
                    }

                    else
                    {
                        slow.next = null;
                    }
                }

                fast = null;
                return str;     // fast.getVal()
            }

            fast = fast.next;
        }

        return "not found";
    }

    public void print()
    {
        ListNode currentNode = first;

        while(currentNode != null)
        {
            currentNode.print();
            currentNode = currentNode.next;
        }
    }

    public boolean isEmpty()
    {
//      return ( ((first==null) ? (true) : (false)) && ((last==null) ? (true) : (false)));
        return (first==null) ? (true) : (false);
    }
}

class ListNode
{
    private String data1;
    private int data2;

    public ListNode next;

    public ListNode(String d1, int d2)
    {
        data1 = d1;
        data2 = d2;
    }

    public String getVal()
    {
        return data1;
    }

//  public void printMe(ListNode node)
    public void print()
    {
        System.out.println("data1: [" + data1 + "], data2: [" + data2 + "]");
    }
}

我有 3 个与此示例相关的问题:

  1. 理想的deleteAll函数应该重复使用我的delete函数吗?我是否应该更改我的删除功能以适应这种情况?
  2. 理想情况下,isEmpty 函数是否应该将 first 和 last 都与 null 进行比较?如果 last 应该与 null 进行比较,那么我应该如何更改我的 delete 和 deleteAll 函数才能实现它。我尝试使用当前的删除功能执行此操作,但遇到了一些问题。
  3. 一般来说,这段代码可以显着优化吗?不是说“如果你需要完美的链表,就用集合”,只是问如果可能的话,如何更精确地优化这个单链表?

【问题讨论】:

    标签: java algorithm optimization class linked-list


    【解决方案1】:

    deleteAll() 可能最适合将 previous 节点传递给节点删除方法。如果 delete() 预计会做任何事情,而不是明显的指针操作,则可以排除此操作。

    /** 
     Deletes all nodes that contain target_string. 
     Returns the number of nodes deleted.
    */
    public int deleteAll(String target_string) {
      int deleted_nodes_cnt = 0;
      ...
      if (prev_node.next.getVal().equals(target_string)) { // not ==
        deleteNextNode(prev_node); 
        prev_node = prev_node.next; 
        deleted_nodes_cnt += 1;
      }
      ...
      return deleted_nodes_cnt;
    }
    
    /** Delete the node after prev_node; prev_node.next != null */
    private void deleteNextNode(Node prev_node) {
      Node dead_node = prev_node.next;
      prev_node.next = prev_node.next.next;
      dead_node.afterDelete(); // any custom cleanup, if required
    }
    
    public boolean delete(String target_string) {
       ... 
    
      if (prev_node.next.getVal().equals(target_string)) { // looks familiar?
        deleteNextNode(prev_node);
        return true; 
      }
      ...
      return false;
    }
    

    您可能会注意到 delete()deleteAll() 使用相同的列表迭代逻辑,也可以很好地分解出来。

    /**
     Scan nodes, starting from this.
     Returns node X such that X.next.value equals target_string, or null. 
    */
    private Node findNodeBeforeMatching(String target_string) {
      Node prev_node = this;
      while (prev_node.next != null) {
        if (prev_node.next.getVal().equals(target_string)) return prev_node;
        else prev_node = prev_node.next;
      }
      return null;
    }
    

    要有效地使用此方法,您需要将LinkedList(本质上是'list head keeper')设为Node 的子类,或将它们设为公共类的子类。更好的是,让它们实现相同的接口,允许getNext()deleteNext()。或者,您可以从每个操作中返回一个Node,但这当然与Collection 接口不兼容。

    旧的、不正确的文本:deleteAll() 应该调用单独的 delete() 方法,除非这些方法可能在后代类中做一些特殊的事情。原因是这样的deleteAll()是O(1),在恒定时间内运行,同时遍历列表到delete()每个节点是O(n)。 AFAICT last 实例变量仅用于加速将元素附加到列表末尾(尽管 insert() 对它做了奇怪的事情)。所以isEmpty() 应该只检查first。如果first == null,方法可以 assert last == null。我假设拥有first == nulllast != null 意味着我们的簿记错误,我们的数据已损坏,我们无法继续。 WRT 优化,我看不出你的拜占庭delete() 应该如何工作。我不认为在你的情况下有两个指针可以加快速度。以不同的“速度”运行两个指针是检测周期的已知方法,但我看不出它在这里如何适用。

    如果您想加快排序数据的速度,请阅读skip list,或使用树。对于未排序的数据,最好的选择是简单的顺序扫描。

    对我来说,整个 LinkedList 类的长度应该是原来的一半,因为它在逻辑上非常简单。

    您的ListNodeLinkedListinsert() 中不必要地紧密耦合,恕我直言,这应该接受一个实例,而不是构造它。更好的是,ListNodeLinkedList 应该是同一个东西,就像在经典实现中一样。

    获取Wirth's book on data structures and algorithms,它非常平易近人。 (当你完全理解之后,试着继续看 Knuth 的书,如果你真的很勇敢,可以看 Okasaki 的书。)

    【讨论】:

    • deleteAll - 正如我在帖子中提到的:删除特定元素的所有出现。例如,deleteAll("xyz") 将删除多个 xyz,而不是所有元素,因此它应该是 O(n)。我保留了快速和慢速指针,以便如果快速到达 xyz 元素,我可以将 slow.next(慢速比快速慢 1)指向 fast.next,并快速删除,以便前一个元素指向下一个元素,否则我必须遍历一次,找到位置,然后再次遍历直到一个元素才能执行此操作。而且,你能否展示代码如何“接受一个实例,而不是构造它”,这样它们就不会“耦合”
    猜你喜欢
    • 1970-01-01
    • 2012-01-26
    • 2017-12-28
    • 1970-01-01
    • 2018-07-08
    • 2021-05-14
    • 2018-06-04
    • 1970-01-01
    • 2020-08-22
    相关资源
    最近更新 更多