我们如何才能有效地从数组中的连接整数中找到最小整数?

时间:2014-08-20 04:31:27

标签: performance algorithm sorting data-structures concatenation

问题:给定一个可以包含重复项的正整数数组,找到通过连接整数获得的最小数。例如:[3, 32, 321]返回321323

除了尝试所有n!连接排列,我似乎无法找到解决这个问题的好方法。我知道下面有一个很好的解决方案,但我很难理解为什么它是真的(如果你想尝试解决这个问题,请停止阅读):

我已经阅读了一个解决方案,我们可以使用比较器对数组进行排序,比较器通过比较连接m和连接的数值来比较两个数字nmn nm,排序的数组将是给出最小数字的串联,但我无法弄清楚为什么这是真的。有什么想法吗?

4 个答案:

答案 0 :(得分:2)

你可以使用类似于冒泡排序的东西来解决这个问题。

  • 首先,我们注意到最终结果的长度是固定的。

  • 其次,结果是您可以创建的最小词典字符串(因为所有可能的字符串的长度是固定的,所以最小字典也是最小数字)。

假设我们有两个号码nm,如果nm< mn,所以n应该总是在m的前面。因为如果我们有一个字符串是m ... n,那么我们总是可以通过将其交换为n ... m来获得更小的字符串。

所以我们继续交换数字直到没有任何东西可以交换,这是最终的答案

答案 1 :(得分:0)

这是一个数学问题,而不是计算机问题。

将连接操作定义为C(a1,... an),其中(a1,... an)是有序数组。然后C满足

C(a1,... an)= C(C(a1,... ax),C(a(x + 1),... an))

对于任何1< x< Ñ

使用数学归纳法,将F定义为处理n个变量数组的函数。

F(2)= min(C(a1,a2),C(a2,a1))定义明确。

对于F(n),给定F(n-1)被最小化,问题变为找到最佳位置,使得F(n)最小化。然后很容易证明

  • 对于C(ax,an)>的任何斧头。 C(an,ax),C(a1,... ax,an,a(x + 1),... a(n-1))< C(a1,... a(x - 1),an,ax,a(x + 1),... a(n - 1))。

  • 对于C(ax,an)< C(an,ax),C(a1,... ax,an,a(x + 1),... a(n-1))> C(a1,... a(x - 1),an,ax,a(x + 1),... a(n - 1))。

所以最好的解决方案是插入一个位置C(ax,an)< C(an,ax)和C(a(x + 1),a)> C(an,a(x + 1))。因此,您描述的算法成立。

答案 2 :(得分:0)

如果它们都是单位数字,解决方案很简单:对它们进行排序并选择结果中最重要(最左边的数字)的最小数字。例如在3之前选择2因为23小于32。

类似于多位数字,按最左边的数字排序。例如在45之前选择123,因为12345小于45123。

最棘手的部分是关系。如果最左边的数字相同,则比较第二个数字,然后比较第三个数字......例如因为32134小于34321,所以在34之前选择321。

最后,如果有一个平局并且你的数字用完了怎么办?因为3213小于3321,所以在3之前选择321。

因此,使用Java作为一种语言,使用这些规则编写一个Comparator(请注意,您可能更好地将数字视为字符串,而不是数字),排序,然后连接。其他语言也类似。

比较器的Java代码: See also Gist with a main() for testing请注意,我能够重组并简化规则。此代码可能更好地针对速度进行了优化。 (注意:假设所有的整数首先转换为字符串以提高效率)

public class SO25396760 implements Comparator<String> {

   @Override
   public int compare(String s1, String s2) {
      int minLength = Math.min(s1.length(), s2.length());
      int result = s1.substring(0, minLength).compareTo(s2.substring(0, minLength));
      if (result != 0)
         return result;

      int lenDiff = s1.length() - s2.length();
      if (lenDiff == 0)
         return 0;  // Strings are equal

      if (lenDiff < 0)
         return compare(s1, s2.substring(minLength));
      else
         return compare(s1.substring(minLength), s2);
   }
}

当输入为{“321”,“3”,“35”,“321334”,“333”,“2”}时;排序的数组是 [2,321,321334,3,333,35] 和连接的值是 2321321334333335.通过检查测试这是最小的。

答案 3 :(得分:0)

<'成为有问题的比较器,以便m <' n当且仅当。{ mn < nm假设<'strict weak ordering , 其他答案是关于为什么按<'排序的原因 好主意。这是一个仔细的证据。假设相反的是两个 (可能是不相邻的)输入字符串m <' n是乱序的,所以 输出看起来像

...n...p...m...

通过对nm之间距离的归纳,我们证明了这一点 输出不是最小的。在基本情况下,当n位于m旁边时,我们 可以交换它们。自mn < nm以来,新输出较小。电感, 让p成为nm之间输出中的输入字符串。 如果是p <' m,那么p <' n是传递性的。如果p <' n,那么我们调用 np上的归纳假设,它比nm更近 n <' p。如果是m <' p,那么m <' p是传递性的。如果p,那么我们 调用mn上的归纳假设,它们比...更近 mn。如果这些情况都不适用,那么pp就是 无与伦比,而mn是无与伦比的。通过传递性 不可比性,mn <' m是无法比拟的,这与之相矛盾 事实是m,因此前四个案例中的一个适用。

确定最小输出中输入字符串的顺序 最多在等价块内置换等效字符串。以来 等同于n的{​​{1}}意味着mn = nm,我们得到相同的输出 无论排列如何,这个输出因此是最小的。

正确性证明的有趣部分证明<' 首先是严格的弱序。事实上,除非是这样 我们排除了空字符串,这会造成严重破坏。对于所有非空的 字符串m,让key(m)成为由m组成的无限字符串 经常无限重复。表明<'是一个严格的弱序 在非空字符串上,只有{if}才足以显示m <' n key(m) < key(n)

首先假设m <' n。然后是mn < nm。让|m|为长度 m的{​​{1}},重复(m^|n|)(n^|m|) < (n^|m|)(m^|n|) 应用不等式mn < nm。以来 |m^|n|| = |m||n| = |n^|m||,我们得出m^|n| < n^|m|的结论 key(m) < key(n)

现在假设m <' n不成立。如果nm < mn,那么我们认为是 在key(n) < key(m)之前,因此key(m) < key(n)没有 保持。否则,mn = nm。自(m^|n|)(n^|m|) = (n^|m|)(m^|n|)以来,我们 得出结论m^|n| = n^|m|,因而key(m) = key(n)