最小唯一数组总和
Posted
技术标签:
【中文标题】最小唯一数组总和【英文标题】:Minimum unique array sum 【发布时间】:2016-07-14 21:26:24 【问题描述】:我有一个面试问题,我的算法只通过给定的示例测试用例,并没有通过所有测试用例。
问题:给定一个排序的整数数组,返回数组的总和,使每个元素都是唯一的,方法是向重复元素添加一些数字,使唯一元素的总和最小。
即,如果数组中的所有元素都是唯一的,则返回总和。 如果某些元素是重复的,则将它们递增以确保所有元素都是唯一的,从而使这些唯一元素的总和最小。
一些例子:
input1[] = 2, 3, 4, 5
=> return 19
= 2+3+4+5(所有元素都是唯一的,所以只需将它们相加即可)
input2[] = 1, 2, 2
=> return 6
= 1+2+3(索引 2 是重复的,所以增加它)
input3[] = 2, 2, 4, 5
=> return 14
= 2+3+4+5(索引 1 是重复的,所以增加它)
这三个是问题中的示例,我的简单算法如下并通过了给定的三个示例,但没有通过其他看不到输入的情况。
static int minUniqueSum(int[] A)
int n = A.length;
int sum = A[0];
int prev = A[0];
for( int i = 1; i < n; i++ )
int curr = A[i];
if( prev == curr )
curr = curr+1;
sum += curr;
else
sum += curr;
prev = curr;
return sum;
我看不到该算法失败的其他输入。 我能想到的其他输入示例是
1, 1, 1, 1 --> 1, 2, 3, 4
1, 1, 2, 2, 3, 3, 3 --> 1, 2, 3, 4, 5, 6, 7
1, 2, 4, 4, 7, 7, 8 --> I think this should be 1, 2, 3, 4, 6, 7, 8 and my algorithm fails in this example because my algorithm has 1, 2, 4, 5, 7, 8, 9 whose sum is not minimum
还有哪些其他测试用例和可以通过所有用例的算法?
有些人抱怨问题不清楚。我想让你知道这个问题。如果只允许正数或正数和负数,则没有关于添加的数字的明确描述。给定三个带有输入和输出的示例,以及其他一些您不允许看到的输入和输出案例,编写一个程序来传递所有其他看不见的输入/输出案例。这就是问题所在。
【问题讨论】:
在您的最后一个测试用例中,您正在从第二个索引中删除 1。但是,在您的问题中,您说您只能通过“如果重复添加最小数量”来使其唯一。 如果只允许加数字,1, 2, 4, 4, 7, 7, 8
应该如何变成1, 2, 3, 4, 6, 7, 8
?
所以只要数组保持排序,就可以添加负数?
一个测试用例可能是一个 int,它在 Integer 的最大范围内。导致溢出。您可能需要在递增之前向上转换很久以避免这种情况。您的总和可能还需要是一个大整数或双精度数。
【参考方案1】:
在重复值较多的情况下,您的算法将失败,例如
2、2、2
你会得到 7 而不是 9。
使用您的算法的最小修复是:
static int minUniqueSum(int[] A)
int n = A.length;
int sum = A[0];
int prev = A[0];
for( int i = 1; i < n; i++ )
int curr = A[i];
if( prev >= curr )
curr = prev+1;
sum += curr;
prev = curr;
return sum;
*正如 cmets 中所指出的,无需对已排序的数组进行排序。
【讨论】:
@kremzeek 您的示例未排序【参考方案2】:在 javascript 中
var list = [1, 1, 1, 10, 3, 2];
function minUniqueSum(arr)
const temp = arr.reduce((acc, cur) =>
while (acc.includes(cur)) cur++;
acc.push(cur);
return acc;
, []);
console.log(temp); // [1, 2, 3, 10, 4, 5]
return temp.reduce((acc, cur) => acc + cur, 0);
var result = minUniqueSum(list);
console.log(result); // 25
【讨论】:
【参考方案3】:我确实喜欢这个,没有排序。
// Complete the getMinimumUniqueSum function below.
static int getMinimumUniqueSum(int[] arr)
int sum = 0;
ArrayList < Integer > arrayList = new ArrayList < Integer > (arr.length);
arrayList.add(arr[0]);
for (int i = 1; i < arr.length; i++)
int val = arr[i];
while (arrayList.contains(val))
val++;
arrayList.add(val);
for (int i = 0; i < arrayList.size(); i++)
sum += arrayList.get(i);
return sum;
它通过了所有 (13) 个测试用例。
【讨论】:
Given a sorted integer array
,你不需要排序并且使用 Collection.contains() 是矫枉过正的。 Arraylist.contains() 对性能有害。【参考方案4】:
虽然这个解决方案是基于 java 的,但思维过程可以应用到任何地方。
您的解决方案几乎是正确和优化的。使用多个 for 循环会减慢很多速度,因此应尽可能避免!由于您的数组已经预先排序,因此您有足够的 1 个 for 循环。
您认为最后一个测试用例错误的假设似乎并不正确,因为增量意味着您只能做 +1(实际上大多数问题将此分配限制为仅增量。)
您错过的是整数的最大范围。
如果他们传递一个 Integer.MAX_VALUE,那么您的总和将溢出并变为负数。所以你的 sum 变量需要是一个更大的类型。 double 或 BigInteger 应该可以工作(最好是 BigInteger)。
此外,当它们两次通过 MAX_VALUE 时,您的 curr+1 也会溢出变为负数。所以你希望你的 curr 和 prev 也是一个更大的类型。 long 应该这样做。
public static double calculateMinSumSorted(int[] input)
double sum = input[0];
long prev = input[0];
long cur;
for(int i = 1 ; i < input.length ; i++)
cur = input[i];
if(cur <= prev)
cur = ++prev;
prev = cur;
sum += cur;
return sum;
这是我使用的一些测试用例:
@Test
public void testSimpleArray()
double test1 = muas.calculateMinSumSorted(new int[]1,2,3,4);
Assert.assertEquals(10, test1, 0.1);
@Test
public void testBeginningSameValues()
double test1 = muas.calculateMinSumSorted(new int[]2,2,3,4);
Assert.assertEquals(14, test1, 0.1);
@Test
public void testEndingSameValues()
double test1 = muas.calculateMinSumSorted(new int[]1,2,4,4);
Assert.assertEquals(12, test1, 0.1);
@Test
public void testAllSameValues()
double test1 = muas.calculateMinSumSorted(new int[]1,1,1,1);
Assert.assertEquals(10, test1, 0.1);
@Test
public void testOverMaxIntResult()
double test1 = muas.calculateMinSumSorted(new int[]1,2,3,3,4,4,4,4,4,Integer.MAX_VALUE);
System.out.println(test1);
Assert.assertEquals(2147483692.0, test1, 0.1);
@Test
public void testDoubleMaxIntArray()
double test1 = muas.calculateMinSumSorted(new int[]2,2,3,4,5,6,7,8,9, Integer.MAX_VALUE, Integer.MAX_VALUE);
Assert.assertEquals(4294967349.0, test1, 0.1);
@Test
public void testDoubleMinIntArray()
double test1 = muas.calculateMinSumSorted(new int[]Integer.MIN_VALUE, Integer.MIN_VALUE,2,2,3,4,5,6,7,8,9);
Assert.assertEquals(-4294967241.0, test1, 0.1);
【讨论】:
在考虑边界条件和提供单元测试方面非常好。缺少(doc-)cmets,有点冗长。我会坚持使用long sum
- An array with length n can be indexed by the integers 0 to n-1
… An attempt to access an array component with a long index value results in a compile-time error
,所以最多有Integer.MAX_VALUE
的价值成分Integer.MAX_VALUE
导致总和约为 3/2 Integer.MAX_VALUE
² - 在范围内 完全准确.【参考方案5】:
如果您可以向任何输入添加负值,那么最小值就是第 N 个三角形数,其中 N 是数组中的元素数。 (我假设我们只处理调整后数组的正数,因为否则我们可以使其任意小(负)。
所以你的算法只是寻找一对相同的连续值。如果未找到,则返回总和,否则返回 N * (N + 1) / 2
。
如果确实只能调整重复的元素,那么方法是在连续元素之间找到空洞,并用以前“排队”的值填充它们。 “排队”元素的实际值无关紧要,只需要一个计数器。下面是一个 C# 解决方案,我假设对元素的调整必须是正值。所以这意味着我们不能倒退并填补未使用的漏洞,从而简化问题。
int F()
int[] a = 2, 2, 2, 3, 8, 9; // sorted list
int n = 0; /* num */ int p = 0; /* prev; zero is an invalid value in the list */
int q = 0; /* queue */ int s = 0; /* sum */
for (int i = 1; i < a.Length; i++)
n = a[i];
if (n == p)
q++; // increment queue on duplicate number
else
// for every hole between array values, decrement queue and add to the sum
for (int j = 1; q > 0 && j < n - p; j++, q--)
s += p + j;
s += (p = n);
// flush the queue
for (; q > 0; q--)
s += ++n;
return s;
您的示例1, 2, 4, 4, 7, 7, 8
表明先前的假设无效。所以我继续写了一个版本,它使用队列来存储跳过的孔以供以后填充。它并没有那么痛苦,而且在结构上也非常相似,但对于大多数面试来说可能还是太过分了。
using System.Collections.Generic;
int F2()
int[] a = 1, 1, 8, 8, 8, 8, 8; // sorted list
int n = 0; /* num */ int p = 0; // prev; zero is an invalid value in the list
int q = 0; /* queue */ int s = 0; // sum
Queue<int> h = new Queue<int>(); // holes
for (int i = 1; i < a.Length; i++)
n = a[i];
if (n == p)
q++; // increment queue on duplicate number
else
for (int j = 1; j < n - p; j++)
if (h.Count <= q + a.Length - i) // optimization
h.Enqueue(p + j);
s += (p = n);
// flush the queue
for (; q > 0; q--)
s += h.Count > 0 ? h.Dequeue() : ++n;
return s;
在这里尝试它们:http://rextester.com/APO79723
【讨论】:
你是对的,这个问题没有具体说明“数字”是什么,所以我们可以例如假设它是负面的(你做了什么)并添加例如IEEE 754“负无穷大”或最小的实数双精度数,或者-如果限制为正值-将差值添加到下一个最小浮点数等。但是,如果存在例如,您的答案是有缺陷的。只有一个重复的数字[1,5,5,9]
您只能修改其中一个5
元素,这将不允许填充其他“洞”并且总和不可能是N * (N + 1) / 2
。
@le_m 该问题未指定允许修改哪些值。 “添加一些数字”有点不清楚,我确实在我的回答中陈述了这个假设
恕我直言,这里的问题很准确:“通过向重复元素添加一些数字” - 好吧,有人可能会争辩说只能修改一个或两个重复元素,但我们仍然不能保证您的以上关于最小值的假设。总和。
@le_m 你是对的。我专注于下面的重述。但如前所述,我对此没有信心,我陈述了我的假设。
对,整个问题不是很清楚,需要拆开——可能不是面试时最好的做法,但谁愿意为一个连工资都拿不到的雇主工作问题对吗? :)【参考方案6】:
int a[] = 1,2,2,3,5,6,6,6,6 ; So what would be elements in array for sum
As per above problem statement it would be 1,2,3,4,5,6,7,8,9
Solution
public static void uniqueSum()
int a[] = 1,2,2,3,5,6,6,6,6 ;
int n = a.length;
int sum = a[0];
int prv=a[0];
for(int i=1; i<n;i++)
int cur = a[i];
if(cur==prv)
cur = cur+1;
sum+= cur;
System.out.print("--"+cur);
else
if(cur<prv)
cur = prv +1;
sum += cur;
prv = cur;
System.out.println("===================== "+sum);
【讨论】:
【参考方案7】:你可以试试下面的代码。
int a[] = 1, 1 , 1;
ArrayList<Integer> arr = new ArrayList<Integer>();
HashMap hash = new HashMap();
for(int i=0;i<a.length;i++)
arr.add(a[i]);
int sum = 0;
hash.put(0, arr.get(0));
sum = (int) hash.get(0);
for(int i=1;i<arr.size();i++)
for(int j=1;j<=a.length;j++)
if(hash.containsValue((arr.get(i))))
arr.set(i, arr.get(i)+1);
else
hash.put(i, arr.get(i));
sum += (int) hash.get(i);
break;
System.out.println(sum);
PS:即使我在面试中得到了这个问题,上面的代码也通过了所有的测试用例。
【讨论】:
介意用你的代码解释你的理由吗? 0
的预期结果是什么,这段代码会产生什么以及为什么?【参考方案8】:
公共静态 int minSum(int arr[])
for(int i=0; i<arr.length-1;i++)
if(arr[i]==arr[i+1])
arr[i+1]= arr[i+1]+1;
int sum=0;
for(int i=0; i<arr.length;i++)
sum=sum+arr[i];
System.out.println("sum: "+sum);
return sum;
【讨论】:
【参考方案9】:根据您对隐藏 I/O 的描述,这可能是一道 HackerRank 测试题。说明问题的更好方法是“给定一个排序的数字数组,通过递增它们(一次 num++)使数字不同,从而使数组总和最小化。”
该问题只允许增量,即一次将数字增加 1。这也确保了数组始终保持排序。 所以 1, 2, 4, 4, 7, 7, 8 --> 1, 2, 4, 5, 7, 8, 9
这是解决方案的问题。 https://www.geeksforgeeks.org/making-elements-distinct-sorted-array-minimum-increments/
【讨论】:
【参考方案10】:工作解决方案 (JAVA 7) :
public static int getMinimumUniqueSum(List <Integer> arr)
int sum = 0, val = 0;
ArrayList < Integer > arrayList = new ArrayList < Integer > (arr.size());
arrayList.add(arr.get(0));
for (int i = 1; i < arr.size(); i++)
val = arr.get(i);
while (arrayList.contains(val))
val++;
arrayList.add(val);
for (int i = 0; i < arrayList.size(); i++)
sum += arrayList.get(i);
return sum;
【讨论】:
【参考方案11】:import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
/* No sorting required. Works even with random list */
public static int getMinUniqueSum(List<Integer> list)
Set<Integer> set = new HashSet<Integer>();
int sum = 0;
for (Integer val : list)
if(!set.add(val))
while(true)
Integer temp = val + 1;
if(set.add(temp))
sum = sum + temp;
break;
else
sum = sum + val;
return sum;
public static void main(String[] args)
Scanner s = new Scanner(System.in);
System.out.println("Enter size of the list");
int n = s.nextInt();
List<Integer> list = new ArrayList<Integer>(n);
System.out.println("Enter " + n + " elements of the list");
for(int i = 0; i < n; i++)
list.add(s.nextInt());
s.close();
System.out.println("MinUniqueSum = " + getMinUniqueSum(list));
【讨论】:
Given a sorted integer array
,反正不需要排序。【参考方案12】:
在 C++ 中:
int64_t getMinimumUniqueSum(std::vector<int> arr)
std::sort(arr.begin(), arr.end());
int64_t sum = arr[0];
size_t i = 0;
size_t j = 1;
size_t gap_i = j;
int avail_val = arr[j] + 1;
while (j < arr.size())
// find next gap with available values
if (j > gap_i)
gap_i = j;
avail_val = arr[gap_i] + 1;
while (gap_i < arr.size() && arr[gap_i] <= avail_val)
avail_val = arr[gap_i] + 1;
gap_i++;
if (arr[i] == arr[j])
// update duplicated value
arr[j] = avail_val;
avail_val++;
else
// move index of prev value - i
i = j;
sum += arr[j];
j++;
return sum;
使用哈希集的直接解决方案会更慢:
int64_t getMinimumUniqueSum_Slow(std::vector<int> arr)
std::unordered_set<int> s;
int64_t sum = 0;
for (int a : arr)
while (s.find(a) != s.end())
a++;
s.insert(a);
sum += a;
return sum;
Slow 版本大约需要 10s 来处理具有 10^5 个数字的数组。
虽然已优化,但处理具有 10^7 个数字的数组大约需要 0.5 秒。
虽然缓慢的解决方案显然是正确的 - 我们可以用它来测试优化的解决方案:
std::vector<int> random_vec(size_t size, int min_val, int max_val)
std::random_device rnd_device;
std::mt19937 mersenne_engine rnd_device();
std::uniform_int_distribution<int> dist min_val, max_val;
auto gen = [&dist, &mersenne_engine]()
return dist(mersenne_engine);
;
std::vector<int> arr(size);
generate(begin(arr), end(arr), gen);
return arr;
int main()
for (int i = 0; i < 1000; i++)
printf("%d\n", i);
auto arr = random_vec(i*10+1, -5, 5);
int64_t x = getMinimumUniqueSum(arr);
int64_t y = getMinimumUniqueSum_Slow(arr);
if (x != y)
printf("Results not match: fast -> %lld, slow -> %lld !!!\n\n", x, y);
return 1;
return 0;
【讨论】:
【参考方案13】:在 Haskell 中:
countdups _ _ [] = []
countdups first prev (x:xs)
| (prev >= x) && (first /= True) = (prev+1) : countdups False (prev+1) xs
| otherwise = x: countdups False x xs
minsum list = sum $ countdups True 0 (sort list)
这是我使用的一些测试用例:
countdups True 0 [2, 3, 4, 5]
[2,3,4,5]
minsum = 14
countdups True 0 [1, 2, 2]
[1,2,3]
minsum = 6
countdups True 0 [2, 2, 4, 5]
[2,3,4,5]
minsum = 14
countdups True 0 [1,2,2,3,7]
[1,2,3,4,7]
minsum = 17
countdups True 0 [1,1,1,2,3,10]
[1,2,3,4,5,10]
minsum = 25
countdups True 0 [1,1,1,1]
[1,2,3,4]
minsum= 10
countdups True 0 [1,2,3,3,4,4,4,4,4,2147483647]
[1,2,3,4,5,6,7,8,9,2147483647]
minsum= 2147483692
【讨论】:
【参考方案14】:// 1,1,2,3 -> 1,2,2,3 -> 1,2,3,3 -> 1,2,3,4 => 10
// 2,2,2 -> 2,3,2 -> 2,3,3 -> 2,3,4 => 9
public int calculateMinSumSorted(int[] input)
int sum = input[0];
for (int i = 1, v = sum; i < input.length; v = input[i++])
if (input[i] <= input[i - 1])
input[i--] = ++v;
else
sum += input[i];
return sum;
【讨论】:
【参考方案15】:在 java 中使用集合有很大帮助, 这里我使用 HashMap,因为它存储每个唯一键的值
我在 hashmap 中的 Key 是数组元素,value 是 no。出现在数组中的计数。
package uniquesum;
import java.util.*;
public class Uniquesum
static HashMap<Integer, Integer> hp = new HashMap<Integer, Integer>();
static int Sum(int arr[])
int sum=0;
Arrays.sort(arr);
hp.put(arr[0], 1);
for(int i=1; i<arr.length; i++)
if(hp.containsKey(arr[i]))
Integer val = hp.get(arr[i]);
hp.put(arr[i], val+1);
hp.put(arr[i]+val, 1);
else
hp.put(arr[i], 1);
for(Map.Entry m:hp.entrySet())
sum = sum + (int)m.getKey();
return sum;
public static void main(String[] args)
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int arr[] = new int [n];
for(int i=0; i<n;i++)
arr[i] = scan.nextInt();
System.out.println("Sum is " + Sum(arr));
【讨论】:
以上是关于最小唯一数组总和的主要内容,如果未能解决你的问题,请参考以下文章