Java:如何实现 Dancing Links 算法(使用 DoublyLinkedLists)?

Posted

技术标签:

【中文标题】Java:如何实现 Dancing Links 算法(使用 DoublyLinkedLists)?【英文标题】:Java: How to implement Dancing Links algorithm (with DoublyLinkedLists)? 【发布时间】:2017-02-17 18:42:09 【问题描述】:

我正在尝试用 Java 实现 Knuth 的 Dancing Links 算法。

根据 Knuth 的说法,如果 x 是一个节点,我可以通过 C 中的以下操作完全取消链接一个节点:

L[R[x]]<-L[x]
R[L[x]]<-R[x]

并通过以下方式恢复取消链接:

L[R[x]]<-x
R[L[x]]<-x

我在 main 方法中做错了什么?

如何在 Java 中实现取消链接和还原?

这是我的主要方法:

      ///////////////

      DoublyLinkedList newList = new DoublyLinkedList();

      for (int i = 0; i < 81; i++) 
        HashSet<Integer> set = new HashSet<Integer>();
        set.add(i);
        newList.addFirst(set);
      

      newList.displayList();

      // start at 69
      newList.getAt(12).displayNode();

      //HOW TO IMPLEMENT UNLINK?
      //newList.getAt(12).previous() = newList.getAt(12).next().previous().previous();
      //newList.getAt(12).next() = newList.getAt(12).previous().next().next();

      newList.displayList();

      //HOW TO IMPLEMENT REVERT UNLINK?
      //newList.getAt(12) = newList.getAt(12).next().previous();
      //newList.getAt(12) = newList.getAt(12).previous().next();

      System.out.println();

      ///////////////

这里是 DoubleLinkedList 类:

public class DoublyLinkedList<T> 

  public Node<T> first = null;
  public Node<T> last = null;

  static class Node<T> 
    private T data;
    private Node<T> next;
    private Node<T> prev;

    public Node(T data) 
      this.data = data;
    

    public Node<T> get() 
      return this;
    

    public Node<T> set(Node<T> node) 
      return node;
    

    public Node<T> next() 
      return next;
    

    public Node<T> previous() 
      return prev;
    

    public void displayNode() 
      System.out.print(data + " ");
    

    @Override
    public String toString() 
      return data.toString();
    
  

  public void addFirst(T data) 
    Node<T> newNode = new Node<T>(data);

    if (isEmpty()) 
      newNode.next = null;
      newNode.prev = null;
      first = newNode;
      last = newNode;

     else 
      first.prev = newNode;
      newNode.next = first;
      newNode.prev = null;
      first = newNode;
    
  

  public Node<T> getAt(int index) 
    Node<T> current = first;
    int i = 1;
    while (i < index) 
      current = current.next;
      i++;
    
    return current;
  

  public boolean isEmpty() 
    return (first == null);
  

  public void displayList() 
    Node<T> current = first;
    while (current != null) 
      current.displayNode();
      current = current.next;
    
    System.out.println();
  

  public void removeFirst() 
    if (!isEmpty()) 
      Node<T> temp = first;

      if (first.next == null) 
        first = null;
        last = null;
       else 
        first = first.next;
        first.prev = null;
      
      System.out.println(temp.toString() + " is popped from the list");
    
  

  public void removeLast() 
    Node<T> temp = last;

    if (!isEmpty()) 

      if (first.next == null) 
        first = null;
        last = null;
       else 
        last = last.prev;
        last.next = null;
      
    
    System.out.println(temp.toString() + " is popped from the list");
  

【问题讨论】:

投了赞成票:一个很好的问题 【参考方案1】:

我不熟悉 Knuth 的 Dancing Links 算法,但发现 this article 让我很清楚。特别是我发现这非常有帮助:

Knuth 利用了双向链表的基本原理。 从列表中删除对象时,只需要两个操作:

x.getRight().setLeft(x.getLeft()) x.getLeft().setRight(> x.getRight() )

但是,当将对象放回列表中时,所有 需要的是做相反的操作。

x.getRight().setLeft(x) x.getLeft().setRight(x)

所有的一切 需要把对象放回去是对象本身,因为对象 仍然指向列表中的元素。除非 x 的指针是 改了,这个操作很简单。

为了实现它,我添加了用于链接/取消链接的设置器。见 cmets:

import java.util.HashSet;

public class DoublyLinkedList<T> 

      public Node<T> first = null;
      public Node<T> last = null;

      static class Node<T> 
        private T data;
        private Node<T> next;
        private Node<T> prev;

        public Node(T data) 
          this.data = data;
        

        public Node<T> get() 
          return this;
        

        public Node<T> set(Node<T> node) 
          return node;
        

        public Node<T> next() 
          return next;
        

        //add a setter
        public  void setNext(Node<T> node) 
              next = node;
        
        public Node<T> previous() 
          return prev;
        

        //add a setter
        public  void setPrevious(Node<T> node) 
              prev = node;
        

        public void displayNode() 
          System.out.print(data + " ");
        

        @Override
        public String toString() 
          return data.toString();
        
      

      public void addFirst(T data) 
        Node<T> newNode = new Node<T>(data);

        if (isEmpty()) 
          newNode.next = null;
          newNode.prev = null;
          first = newNode;
          last = newNode;

         else 
          first.prev = newNode;
          newNode.next = first;
          newNode.prev = null;
          first = newNode;
        
      

      public Node<T> getAt(int index) 
        Node<T> current = first;
        int i = 1;
        while (i < index) 
          current = current.next;
          i++;
        
        return current;
      

      public boolean isEmpty() 
        return (first == null);
      

      public void displayList() 
        Node<T> current = first;
        while (current != null) 
          current.displayNode();
          current = current.next;
        
        System.out.println();
      

      public void removeFirst() 
        if (!isEmpty()) 
          Node<T> temp = first;

          if (first.next == null) 
            first = null;
            last = null;
           else 
            first = first.next;
            first.prev = null;
          
          System.out.println(temp.toString() + " is popped from the list");
        
      

      public void removeLast() 
        Node<T> temp = last;

        if (!isEmpty()) 

          if (first.next == null) 
            first = null;
            last = null;
           else 
            last = last.prev;
            last.next = null;
          
        
        System.out.println(temp.toString() + " is popped from the list");
      

      public static void main(String[] args) 

          ///////////////

          DoublyLinkedList newList = new DoublyLinkedList();

          for (int i = 0; i < 81; i++) 

              HashSet<Integer> set = new HashSet<Integer>();
              set.add(i);
              newList.addFirst(set);
          

          newList.displayList();

          // start at 69
          Node node = newList.getAt(12);
          node.displayNode(); System.out.println();

          //HOW TO IMPLEMENT UNLINK?
          node.previous().setNext(node.next);
          node.next().setPrevious(node.previous());

          //The 2 statements above are equivalent to
          //Node p = node.previous();
          //Node n = node.next();
          //p.setNext(n);
          //n.setPrevious(p);

          newList.displayList();

          //HOW TO IMPLEMENT REVERT UNLINK?
          node.previous().setNext(node);
          node.next().setPrevious(node);

          newList.displayList(); System.out.println();

          ///////////////
      
    

【讨论】:

哇,谢谢!多么棒的解决方案。我想我得重温一下我的吸气剂和二传手了哈哈:P 我很高兴它有帮助。

以上是关于Java:如何实现 Dancing Links 算法(使用 DoublyLinkedLists)?的主要内容,如果未能解决你的问题,请参考以下文章

ZOJ 3209 Treasure Map dancing links

Dancing Links 专题总结

dancing links

HDU 5046 Airport ( Dancing Links 反复覆盖 )

HDU 3335 Divisibility dancing links 重复覆盖

HDU5046 Airport dancing links 重复覆盖+二分