安排解决河内塔

时间:2020-06-06 17:21:28

标签: java algorithm recursion towers-of-hanoi

已为我安排了有效的河内磁盘塔。排列方式为数组。

例如[2,2,0]:数组的索引是磁盘的标识符,按从小到大的顺序排列,数组的值是相应磁盘的位置(位置始终为0, 1或2)。对于[2,2,0],最小的两个磁盘位于第三极,而最大的磁盘位于第一极:

      |           |           |
      |           |          [0]
   [22222]        |         [111] 
  ----+----   ----+----   ----+----

另一个示例:[0,2,1]

      |           |           |
      |           |           |
     [0]       [22222]      [111] 
  ----+----   ----+----   ----+----

对于将所有磁盘移动到目标磁极(第二磁极)所需的其余步骤,是否有可能递归解决该问题?

public int solveForSteps(int[] disks){
    // Is there a possible way to solve with given arrangements?
}

2 个答案:

答案 0 :(得分:1)

要从任意位置求解河内塔,您可以使用类似于从标准起始位置开始工作的标准解决方案的递归程序。

它必须更一般一些。

编写一个递归过程 moveDisks(maxSize,targetPeg),将大小为<= maxSize 的所有磁盘移动到钉 targetPeg 这个:

  1. 查找最大磁盘 m ,以使 m.size <= maxSize m 不是 targetPeg 上。如果没有这样的磁盘,则返回,因为所有大小为<= maxSize 的磁盘都已放置在正确的位置。

  2. sourcePeg 为当前 m 的钉子,让 otherPeg 为非的钉子> sourcePeg targetPeg

  3. 递归调用 moveDisks(m.size-1,otherPeg),以获取较小的磁盘。

  4. m sourcePeg 移动到 targetPeg

  5. 递归调用 moveDisks(m.size-1,targetPeg)将较小的磁盘放在它们所属的位置。

在Java中,我会这样写:

func getImages(completion: @escaping([UIImage?]) -> Void) {

   var downloadedImages: [UIImage?] = []

   let downloadGroup = DispatchGroup()

   for i in 0...8 {

       downloadGroup.enter()

       let imagePath = "/img" + String(i)
       getImageFromStorage(imagePath: imagePath, completion: { myImage in
           downloadedImages.append(myImage)

           downloadGroup.leave()
       })
   }

   downloadGroup.notify(queue: .main) {
       // will be called when all requests are done
       completion(downloadedImages)
   }
}

...好吧,在现实生活中我不会像这样精确地写。实际上,您可以删除第二个递归调用和中断,因为循环中的其余迭代将完成相同的事情。

答案 1 :(得分:0)

我没有适合您的递归解决方案。当您查看河内塔的常用递归算法时,实际状态可能会在递归树的深处发生,并且如果您想像这样的状态已传递给函数,则从该状态进一步解决问题不需要不仅是递归调用,而且还会重建“外部”递归调用的堆栈。这似乎使它变得非常复杂。

但是您可以迭代地进行。这是一种实现方法:

static void solveForSteps(int[] disks) {
    int n = disks.length;
    // Calculate the next target rod for each of the disks
    int target = 2; // The biggest disk should go to the rightmost rod
    int[] targets = new int[n];
    for (int i = n - 1; i >= 0; i--) {
        targets[i] = target;
        if (disks[i] != target) {
            // To allow for this move, the smaller disk needs to get out of the way
            target = 3 - target - disks[i];
        }
    }
    int i = 0;
    while (i < n) { // Not yet solved?
        // Find the disk that should move
        for (i = 0; i < n; i++) {
            if (targets[i] != disks[i]) { // Found it
                target = targets[i]; // This and smaller disks should pile up here 
                System.out.format("move disk %d from rod %d to %d.\n", i, disks[i], target);
                disks[i] = target; // Make move
                // Update the next targets of the smaller disks
                for (int j = i - 1; j >= 0; j--) {
                    targets[j] = target;
                    target = 3 - target - disks[j];
                }
                break;
            }
        }
    }
}

这将打印将所有磁盘移至最右极所需的其余移动。相反,如果您要瞄准中心极点,则只需将int target = 2更改为int target = 1