通过交换给定整数中的一对数字来查找最小整数的算法

时间:2013-06-18 16:23:12

标签: algorithm

给定正整数(以数组数组)。我们被允许交换给定数字中的一对数字。 我们需要返回可以获得的最小可能整数。注意,它应该是一个有效的整数,即不应该包含前导0。

例如: -

  1. 93561返回13569
  2. 596返回569
  3. 10234返回10234
  4. 120返回102
  5. 10091返回10019
  6. 98761111返回18761119
  7. 此问题是否有O(n)算法。我想到了几个方法: -

    1. 找到分钟。给定整数中的数字(minDIgit)(0除外)并将其与MSB交换,如果MSB != minDigit。如果MSB==minDigit,则找到下一分钟。数字并用最重要但1位数字交换它,依此类推。在最坏的情况下,这可能是O(n^2)
    2. 创建array/vector数字和索引的std::pair并按递增顺序对其进行排序(根据数字值;首先保留较低的索引以匹配数字值)。遍历排序的数组。用第一个数字交换MSB。如果最小数字具有相应的索引作为MSB,则交换MSB,但是将1个位置与下一个最小数字交换。如果下一个最小数字具有相应的MSB索引但是1个位置,那么交换MSB但是在下一个min处交换2个位置。数字等。这应该是O(nlog(n))
    3. 有人可以建议更好的算法。


      更新1: 在思考了一下之后,我提出的第二个算法将完美地工作(可能除了少数角落情况,可以单独处理)。此外,我可以使用计数排序(根据数字值)对对(数字,索引)进行排序,这是O(n)时间内的稳定排序。我的论点有缺陷吗?


      更新2: 我的第二个算法工作(虽然对角落情况和0的检查更多),O(n)时间counting sort也是如此。但@GaborSch给出的解决方案要简单得多,所以我不会为我的算法提供合适的代码。

7 个答案:

答案 0 :(得分:18)

作为准备,我们遍历数字,并标记数组中数字的最后位置[10](称之为last)(包括0 S)。那是O(n)。

接下来,我们开始从左到右迭代数字。对于每个位置,我们尝试找到 last 位置大于当前位置(位置约束)的最小数字。该数字也必须小于当前数字。

如果我们处于第一位置,我们会从last 1开始循环(否则从0开始),直到当前数字的值(不包括)。< / p>

如果我们找到这样的数字(关于位置约束),我们交换(并打破循环)。如果我们不这样做,我们会前进到下一个数字。成本最多为O(n * 9),即O(n)。

总成本为O(n)+ O(n)* O(9)= O(n)。

算法如何处理示例:

93561 ->   it can swap in the first cycle

596   ->   skipping the first cycle, 
           then finds '6' because of the position constraint 
           (comparing position '1' with last[5] = 0, last[6] = 2)

10234 ->   does not find anything because of the position constraint

93218910471211292416 -> finds the last occurrence of '1' to replace '9'

98761111 -> it can swap in the first cycle
            (last[1] = 7, so it will change the last occurrence)

555555555555555555596 -> iterates until the '9', then you skip last[5]
            but finds last[6] as a good swap

120 ->      at pos 0 (1) cannot find a non-zero element less than 1, so skip
            at pos 1 (2) can find 0 on position 2, so we swap

再一次,这里我们对数字进行一次迭代(用于预解析数据),然后进行一次迭代以找到MSB。在第二次迭代中,我们迭代last,它是恒定大小(最多9个)。

您可以通过在last上启动循环值时跟踪最小值来进一步优化算法,但这已经是优化了。 prevoius版本包含,如果您有兴趣,请查看历史记录:)

答案 1 :(得分:11)

首先计算每个数字,将其存储在数组(counts[10])中。

从左侧开始,检查数字(以下是循环的描述):

检查counts中的数字是否小于它。选择最小的一个。例外:第一个数字不允许0

  • 如果有一个,交换,你就完成了(退出循环!)。
  • 否则递减counts中的数字,然后转到下一个数字。

对于每个数字,你做O(1)工作。所以整个算法都是O(n)。

对于交换,您希望使用最低有效数字(进一步向右)。您可以在初始查找中存储这些位置,也可以在交换之前搜索从头开始的第一个匹配数字。

答案 2 :(得分:5)

我会从右端开始迭代数组。将数字存储在右侧作为最小数字和最大数字并开始向左移动,如果您点击一个新的较小数字,则将其称为最小数字。如果你继续向左移动并找到一个较小的数字,那么将较小的数字设为潜在的数字。如果找到一个更大的数字,可以使最小的int小一些,并将较大的一个存储为最大数字。每当你达到比最小数字更大的数字时,将其设为新的最大数字。如果你到了最后,交换最大数字和最小数字。 在python中(这是有效的,是O(n))

def swap_max(digits):
    i = len(digits) - 1
    while i > 0:
        if digits[i] == 0:
            i-= 1
        else:
            break
    max_i = i
    min_i = i
    pot_i = i
    z_i   = -1
    nz_i  = i
    i = len(digits) - 1
    while i >= 0:
        if digits[i] > digits[pot_i]:
            max_i = i
            min_i = pot_i
        if digits[i] < digits[min_i] and digits[i] != 0:
            pot_i = i
        if digits[i] == 0 and z_i == -1:
            z_i = i
        if digits[i] != 0 and i > 0:
            nz_i = i
        i -= 1
    if z_i != -1 and max_i != 0 and max_i < z_i:
        min_i = z_i
        i = nz_i
        max_i = i
    elif max_i == min_i and z_i != -1:
        i = nz_i
        if i < z_i:
            min_i = z_i
            max_i = i

    v = digits[min_i]
    digits[min_i] = digits[max_i]
    digits[max_i] = v
    return digits


#TESTING THE FUNCTION
tests =   [93561,596,10234,120,10091,98761111,1001,1010,1103,120,93218910471211292416]
results = [13569,569,10234,102,10019,18761119,1001,1001,1013,102,13218910471211292496]
tests = map(list,map(str,tests))
results = map(list,map(str,results))
for i in range(len(tests)):
    res ="".join(map(str,swap_max(map(int,tests[i]))))
    print res,"".join(results[i])
    if res=="".join(results[i]):
        print "PASSED\n"
    else:
        print "FAILED\n"

这最终适用于所有示例。它还具有O(1)内存使用的优势。

答案 3 :(得分:2)

这是一个简单的O(n)算法:

- Record 'false' for each of ten digit values, 0 through 9
- Work through the number's digits, left-to-right
    - If the digit value is associated with 'true' go to the next digit and continue
    - Record 'true' for this digit
    - Search all the digits to the right for the right-most, smallest digit
      (except zero for the first digit in the number)
      and swap if the lowest digit found (if any) is less than the current digit
    - If swapped, report success and stop
    - If not swapped, go to the next digit and continue
- If we reach the end of the digit list, report a lack of success and stop

在第一次检查时可能看起来不是O(n),但是在意识到内循环可以执行不超过十次之后,很明显这是O(n),因为O(n - 10 + 10*n) = O(11*n - 10) = O(n)

答案 4 :(得分:1)

PseudoCode: O(n)

1)将数字分成单个数字,比如数字[10](如另一个答案所述)。初始incPos = -1

2)从最右边的数字遍历,找到最左边的增长点(incPos)。 即遍历将k + 1元素与第k个元素进行比较。对于,每个数字[k]≠0,如果digit[k] >= digit[k+1],则将incPos标记为k。遍历到最左边,找到最少的incPos。

4)如果incPos == -1则返回num,否则遍历从incPos到n以找到Right-Most-Minimum数字(如下面的BLOCK所述), swap 与最右边 - 最小数字和返回。 (肯定会有至少1位数。)

E.g  
93561 ->                IncPos = 0,  Right most minimum : 1 at pos 4 
596   ->                IncPos = 1,  Right most minimum : 6 at pos 2 
10234 ->                IncPos = -1, return 10234  
93218910471211292416 -> IncPos = 0,  Right most minimum : 1 at pos 18 
98761111 ->             IncPos = 0,  Right most minimum : 1 at pos 7 

5)用新数字形成数字。退货号码。

答案 5 :(得分:0)

Karoly Horvath算法的微小变化

您可以对O(n)中的数字数组进行基数排序。

现在我们有2个列表:已排序和实际。实际是我们原来的阵列。

从左到右迭代实际,

表示每个值, 弹出Sorted中的元素,直到我们达到原始数组中的位置为&lt;实际位置[i]

如果排序列表的值的头部是&lt;实际[i]然后我们交换,我们完成了。 其他 继续。

在O(n)时间内完成排序。最多我们弹出排序列表总数的n个元素,并且我们只迭代原始列表一次,因此整个算法在时间和空间上应该是O(n)。

当然,有一些特殊情况检查是否与最左边的元素交换0,但这不会影响复杂性。

答案 6 :(得分:0)

以下是满足上述所有测试案例的上述问题的Java代码。

public static String smallestNumber(String num) {
    int length = num.length();
    char[] c = num.toCharArray();
    Map<Character, Integer> map = new HashMap<>();
    for (int i = 0; i < length; i++) {
        map.put(c[i], i);
    }
    int count = 0;
    boolean flag = true;
    int ind = -1;
    for (int i = 0; i < length; i++) {
        int min = c[i];
        for (int j = i + 1; j < length; j++) {
            if (flag) {
                if (min > c[j] && c[j] != '0') {
                    min = c[j];
                    ind = j;
                }
            } else {
                if (min > c[j]) {
                    min = c[j];
                    ind = j;
                }
            }
        }
        if (ind != -1) {
            char temp = c[i];
            int index = map.get(c[ind]);
            c[i] = c[ind];
            c[index] = temp;
            count++;
        }
        flag = false;
        if (count == 1)
            break;
    }
    return String.valueOf(c);
}