最小唯一数组和

时间:2016-07-14 21:26:25

标签: arrays algorithm

我收到了一个面试问题,我的算法只通过了示例测试用例,并没有通过所有测试用例。

问题:给定一个排序的整数数组,返回数组的总和,以便通过向重复元素添加一些数字使每个元素是唯一的,以便唯一元素的总和最小。

即,如果数组中的所有元素都是唯一的,则返回总和。 如果某些元素是重复的,那么递增它们以确保所有元素都是唯一的,这样这些唯一元素的总和就会最小。

一些例子:

  • 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 

还有哪些其他测试用例和可以通过所有案例的算法?

有些人抱怨问题不明确。我想告诉你这个问题。如果仅允许正数或正数和负数,则没有关于添加数字的明确描述。给出了三个输入和输出示例,以及其他一些您不允许看到的输入和输出情况,编写一个程序来传递所有其他看不见的输入/输出情况。那是个问题。

15 个答案:

答案 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;
}