【问题标题】:What is the minimum number of adjacent swaps needed to segregate a list of 0s and 1s?隔离 0 和 1 列表所需的相邻交换的最小数量是多少?
【发布时间】:2020-12-10 07:03:03
【问题描述】:

我正在尝试解决数据结构和算法问题,该问题指出,给定一组 1 和 0,将数字分组,使所有 0 在一起,所有 1 在一起。如果只能交换两个相邻元素,则完成此操作所需的最小交换次数是多少?哪一组在哪一端并不重要。

例如:

[0,1,0,1] = [0,0,1,1] 1 次交换

[1,1,1,1,0,1,0] = [1,1,1,1,1,0,0] 1 次交换

[1, 0, 1, 0, 0, 0, 0, 1] = = [1,1,1,0,0,0,0,0] 6 次交换

请注意,这与此处提出的问题不同:

Find the minimum number of swaps required such that all the 0s and all the 1s are together

我没有对数组进行排序,我只是想将所有的 0 和所有的 1 组合在一起,无论哪个在哪一端都没有关系。

我真的不知道从哪里开始。有人可以帮我吗?

【问题讨论】:

  • 不确定你是如何计算掉期的。在您的第一个示例中,我可以通过一次交换获得该结果,交换元素 1 和 2。
  • @MarkRansom 啊,是的,这是一个错误。编辑了!任何帮助将不胜感激!
  • 与第二个示例相同,而且输出中似乎多了一个1
  • 稍作调整即可尝试冒泡排序。
  • @MarkRansom 啊!我粗心了!对不起!

标签: algorithm data-structures


【解决方案1】:

使用冒泡排序的简单方法需要 O(n2),如下所示:

public class MainClass {

    public static void main(String[] args) {
        int[] arr = new int[]{1, 0, 0, 0, 0, 0, 0, 1, 0};
        int minSwaps = minimumSwaps(arr);
        System.out.println("Minimum swaps required: " + minSwaps);
    }

    public static int minimumSwaps(int[] array) {
        int[] arr1 = array.clone(), arr2 = array.clone();
        int swapsForRight = 0, swapsForLeft = 0;

        boolean sorted = false;

        while (!sorted) {
            sorted = true;
            for (int i = 0; i < arr1.length - 1; i++) {
                if (arr1[i + 1] < arr1[i]) {
                    int temp = arr1[i + 1];
                    arr1[i + 1] = arr1[i];
                    arr1[i] = temp;
                    sorted = false;
                    swapsForRight++;
                }
            }
        }
            
        sorted = false;
        while (!sorted) {
            sorted = true;
            for (int i = 0; i > arr2.length - 1; i++) {
                if (arr2[i + 1] < arr2[i]) {
                    int temp = arr2[i + 1];
                    arr2[i + 1] = arr2[i];
                    arr2[i] = temp;
                    sorted = false;
                    swapsForLeft++;
                }
            }
        }
        return swapsForLeft > swapsForRight ? swapsForRight : swapsForLeft;
    }
}

【讨论】:

  • 这没有回答问题。问题是计算所需交换的数量,而不是实际对列表进行排序。
  • 现在 OP 已经改变了问题......顺序并不重要......等一下。
  • 你不需要对数组进行排序来计算交换次数。
  • @Dave,您仍然需要对其进行排序,否则您将无法使用 isSorted 的标志,并且您永远无法确定您需要多少交换。
  • @GiorgiTsiklauri 查看我的回答。我按要求提供了代码。您可以在不排序的情况下计算所需的排序。
【解决方案2】:

让我们专注于零。每次掉期都会将单个零移动到更接近最终订单的单个位置。然后我们可以通过查找移位的零的数量以及移位的严重程度来找到交换的次数。

让我们首先假设零在数组的开头结束。我们将跟踪两件事:count_of_ones 和位移,两者都初始化为零。每次我们找到一个 1,我们增加 count_of_ones。每次我们找到一个 0,我们将位移增加 count_of_ones。

然后我们在另一个方向这样做。两种方式都是线性的,所以这是线性的。

例如1010001

1: count_of_ones: 0 -> 1
0: displacement: 0 -> 1
1: count_of_ones: 1 -> 2
0: displacement: 1 -> 3
0: displacement: 3 -> 5
0: displacement: 5 -> 7
1: count_of_ones: 2 -> 3

这个方向的答案是最终的位移,即 7。反之则得到 5。最终答案是 5。

事实上,最终位移的总和(以全零开始与以全零结束)将始终等于 num_zeroes * num_ones。这使工作减半(尽管它仍然是线性的)。


从 cmets 看来,有些人不明白我的回答。这是一个让事情更清晰的 Ruby 实现。

def find_min_swaps(arr)
  count_of_ones = 0
  displacement = 0
  arr.each do |v|
    count_of_ones += 1 if v == 1
    displacement += count_of_ones if v == 0
  end

  count_of_zeroes = arr.length - count_of_ones
  reverse_displacement = count_of_ones * count_of_zeroes - displacement
  return [displacement, reverse_displacement].min
end

如果位移 reverse_displacement,则在右侧。

【讨论】:

  • 很抱歉,你是怎么得到 5 的?还是 7 个?
  • 这对我来说很不清楚.. 1)最后的顺序是什么? 2)在哪里移动零?正确的?剩下?; 3)位移可能会有所不同,具体取决于您对数组进行排序的顺序,相应地,位移的计数会导致不同的结果; 4) 为什么你以零为方向?对不起,可能是我,但我什至很难理解你的答案的逻辑......甚至没有达到伪代码的水平。你可能知道你在这里有什么,但解释肯定很差......好吧,至少对我来说。剪掉一些代码会很棒。
  • @GiorgiTsiklauri 提供代码。
  • @ashkan117 位移总和来自这样一个事实,即如果所有零都在一侧,则将它们全部移动到另一侧将需要将每个零与每个零交换。这些交换的顺序无关紧要。因此,任何一侧没有全零的状态都可以被视为这种转换中的部分状态。
  • @ashkan117 听起来像是在这里提出新问题或者可能在数学溢出板上的一个很好的候选人。
【解决方案3】:

SUM0 是所有零的(从0 开始的)索引的总和,让SUM1 是所有零的索引的总和。每次交换10 -> 01SUM0 下降 1,SUM1 上升 1。当您交换 01 -> 10 时,它们会走另一条路。

假设您有N0 零和N1 。如果零在数组的开头打包在一起,那么您将拥有SUM0 = N0*(N0-1)/2。这是您可以拥有的最小的SUM0

由于单个相邻交换可以将SUM0 恰好减少一,因此恰好需要SUM0 - N0*(N0-1)/2 交换将前面的零打包在一起。同样,前面需要SUM1 - N1*(N1-1)/2 swaps 才能将它们打包在一起。

您的答案是以下数字中较小的一个:min( SUM0 - N0*(N0-1)/2 , SUM1 - N1*(N1-1)/2 )

这些值都很容易在线性时间内计算出来。

【讨论】:

    猜你喜欢
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 2011-02-28
    • 1970-01-01
    • 2017-01-09
    • 1970-01-01
    相关资源
    最近更新 更多