我收到了一个面试问题,我的算法只通过了示例测试用例,并没有通过所有测试用例。
问题:给定一个排序的整数数组,返回数组的总和,以便通过向重复元素添加一些数字使每个元素是唯一的,以便唯一元素的总和最小。
即,如果数组中的所有元素都是唯一的,则返回总和。 如果某些元素是重复的,那么递增它们以确保所有元素都是唯一的,这样这些唯一元素的总和就会最小。
一些例子:
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
还有哪些其他测试用例和可以通过所有案例的算法?
有些人抱怨问题不明确。我想告诉你这个问题。如果仅允许正数或正数和负数,则没有关于添加数字的明确描述。给出了三个输入和输出示例,以及其他一些您不允许看到的输入和输出情况,编写一个程序来传递所有其他看不见的输入/输出情况。那是个问题。
答案 0 :(得分:5)
在具有更多重复值的情况下,您的算法将失败,例如
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;
}
*正如评论中所指出的,无需对已排序的数组进行排序。
答案 1 :(得分:1)
我确实喜欢这样,没有排序。
// 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)个测试用例。
答案 2 :(得分:0)
您可以向任何输入添加负值,然后最小值只是第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
答案 3 :(得分:0)
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);
}
答案 4 :(得分:0)
您可以尝试以下代码。
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:即使我在采访中得到了这个问题,上面的代码也通过了所有的测试用例。
答案 5 :(得分:0)
public static 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;
}
答案 6 :(得分:0)
在java中使用集合有很大帮助, 这里我使用HashMap,因为它存储每个唯一键的值
我在hashmap中的键是数组元素,值是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));
}
}
答案 7 :(得分:0)
根据您对隐藏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/
答案 8 :(得分:0)
工作解决方案(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;
}
答案 9 :(得分:0)
在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
答案 10 :(得分:0)
虽然此解决方案基于Java,但是思考过程可以应用在任何地方。
您的解决方案几乎是正确的和经过优化的。使用多个for循环会减慢很多速度,因此应尽可能避免!由于您的数组已经预先排序,因此您有足够的1 for循环。
您认为最后一个测试用例错误的假设似乎并不正确,因为增量意味着您只能执行+1(实际上,大多数问题都将这种分配限制为增量。)
您错过的是整数的最大范围。
如果它们传递Integer.MAX_VALUE,则您的总和将溢出并且为负数。因此,您的sum变量需要为更大的类型。 double或BigInteger应该可以工作(BigInteger最好)。
此外,当它们两次通过MAX_VALUE时,您的curr + 1也会溢出,变为负数。因此,您希望curr和prev也成为更大的类型。为此应该做多长时间。
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);
}
答案 11 :(得分:0)
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
public class MinimumUniqueSumProblem
{
public static int getMinUniqueSum(List<Integer> list)
{
Set<Integer> set = new HashSet<Integer>();
for (Integer val : list)
{
if(!set.add(val))
set.add(val+1);
}
return getSum(set);
}
public static int getSum(Set<Integer> set)
{
int sum = 0;
for (Integer val : set)
sum = val + sum;
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));
}
}
答案 12 :(得分:0)
在 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;
}
慢版本大约需要10s来处理10^5个数字的数组。
虽然优化需要大约0.5s来处理10^7个数字的数组。
虽然缓慢的解决方案显然是正确的 - 我们可以用它来测试优化的解决方案:
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 :(得分:0)
在 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 真 0 [2, 3, 4, 5]
[2,3,4,5]
minsum = 14
countdups 真 0 [1, 2, 2]
[1,2,3]
minsum = 6
countdups 真 0 [2, 2, 4, 5]
[2,3,4,5]
minsum = 14
countdups 真 0 [1,2,2,3,7]
[1,2,3,4,7]
minsum = 17
countdups 真 0 [1,1,1,2,3,10]
[1,2,3,4,5,10]
minsum = 25
countdups 真 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 :(得分:0)
// 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;
}