给定正整数(以数组数组)。我们被允许交换给定数字中的一对数字。 我们需要返回可以获得的最小可能整数。注意,它应该是一个有效的整数,即不应该包含前导0。
例如: -
此问题是否有O(n)
算法。我想到了几个方法: -
minDIgit
)(0除外)并将其与MSB交换,如果MSB != minDigit
。如果MSB==minDigit
,则找到下一分钟。数字并用最重要但1位数字交换它,依此类推。在最坏的情况下,这可能是O(n^2)
。array/vector
数字和索引的std::pair
并按递增顺序对其进行排序(根据数字值;首先保留较低的索引以匹配数字值)。遍历排序的数组。用第一个数字交换MSB。如果最小数字具有相应的索引作为MSB,则交换MSB,但是将1个位置与下一个最小数字交换。如果下一个最小数字具有相应的MSB索引但是1个位置,那么交换MSB但是在下一个min处交换2个位置。数字等。这应该是O(nlog(n))
。有人可以建议更好的算法。
更新1:
在思考了一下之后,我提出的第二个算法将完美地工作(可能除了少数角落情况,可以单独处理)。此外,我可以使用计数排序(根据数字值)对对(数字,索引)进行排序,这是O(n)
时间内的稳定排序。我的论点有缺陷吗?
更新2:
我的第二个算法将工作(虽然对角落情况和0的检查更多),O(n)
时间counting sort
也是如此。但@GaborSch给出的解决方案要简单得多,所以我不会为我的算法提供合适的代码。
答案 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);
}