如何改进:给定两个整数,返回它们共享的位数

时间:2016-07-28 08:49:28

标签: java algorithm int hashtable

我在接受采访时收到了这个问题,问题是

  

给定两个整数,返回它们共享的位数。

例如129和431将返回1 - 因为它们都共享数字1,但没有其他数字。 95和780将返回0,因为没有整数重叠。

我的想法只是遍历数字,将它们存储在哈希表中并检查.containsKey.

我的Java解决方案:

public int commonDigits(int x, int y) {
     int count = 0;
     HashTable<Integer, String> ht = new HashTable<Integer, String>();

     while (x != 0) { 
         ht.put(x % 10, "x");
         x /= 10;
     }

     while (y != 0) {
         if ((ht.containsKey(y % 10)) {
             count++;
         }
         y /= 10;
     }

    return count;
}

但这会占用O(n)空间和O(n + m)时间,无论如何我可以改进这个吗?

6 个答案:

答案 0 :(得分:8)

为什么不只是使用一些简单的小提琴?

public int commonDigits(int x, int y) {
  int dX = 0;
  while (x != 0) {
    dX |= 1 << (x % 10);
    x /= 10;
  }
  int count = 0;
  while (y != 0) {
    int mask = 1 << (y % 10);
    if ((dX & mask) != 0) {
      count ++;
      dX &= ~mask;
    }
    y /= 10;
  }
  return count;
}

这只是为x中的每个数字设置了dX中的相应位。在第二个循环中,对于x中的每个数字,代码检查它是否在dX中有一个条目。如果是这样,它会被计数并重置该位以避免重复计算(请注意,在代码中缺少这一点,请考虑123和141)。显然不使用任何额外的存储(如果重要的话,dX和count可能只是字节)。

请注意,您的解决方案中不需要HashTable - 您可以只使用HasSet或BitSet ..

您的代码已转换为使用BitSet并修复了重复计算问题:

public int commonDigits(int x, int y) {
  int count = 0;
  BitSet ht = new BitSet();

  while (x != 0) { 
     ht.set(x % 10, true);
     x /= 10;
  }
  while (y != 0) {
     if ((ht.get(y % 10)) {
         count++;
         ht.set(y % 10, false);
     }
     y /= 10;
  }
  return count;
}

两个片段的工作方式完全相同,后者只是为BitSet(和嵌入式数组)实例带来了更多的开销。

本文说明了在一般情况下BitSet优于布尔数组的原因:http://chrononsystems.com/blog/hidden-evils-of-javas-byte-array-byte

修改

如果实际需要多次计算相同的数字(从问题中的示例中不清楚),请使用int数组来存储计数:

public int commonDigits(int x, int y) {
  int count = 0;
  int[] digits = new int[10];

  while (x != 0) { 
     digits[x % 10]++;
     x /= 10;
  }
  while (y != 0) {
     if (digits[x % 10] > 0) {
         count++;
         digits[x % 10]--;
     }
     y /= 10;
  }
  return count;
}

答案 1 :(得分:6)

这是具有最小存储空间的解决方案(10个字节的数组而不是哈希表):

public int commonDigits(int x, int y) {
 int count = 0;
 byte[] digits=new byte[10];

 while (x != 0) { 
     digits[x%10] ++;
     x /= 10;
 }

 while (y != 0) {
     if (digits[y % 10] > 0) {
         count++;
         digits[y % 10] --;
     }
     y /= 10;
 }

return count;
}

此解决方案在运行时间O(n+m)中是最佳的,其中nx中的位数,my中的位数。您只需枚举x的数字,然后列举y的数字。

答案 2 :(得分:4)

由于只有10个可能的数字,为什么不存储整数数组呢?索引从0到9,每个数字一个。遍历每个数字并递增相应的数组元素。这个空间的复杂性取决于你定义的“常量” - 这总是占用10个单位的空间(在C / C ++中将是10个字节,不确定JVM是如何做到的)

通过信息保护,你遍历两个数字的每个数字(它们是独立的,所以你不能只推断另一个数字),所以你的时间复杂度将保持在O(m + n)。另外我认为O(n)你的意思是O(log n),因为这是数字的任何表示中的位数。

答案 3 :(得分:2)

最多可以共享10位数字。这意味着您不需要像Hashtable这样复杂的数据结构,数组或位掩码就足够了!

您只需迭代第一个数字,并且您点击的每个数字都标记为“true”(在您的位掩码或数组中)。如果您已经找到每个数字一次(使用位掩码简单且便宜),您甚至可以将其短路并提前返回。然后翻看第二个号码。每次敲击也标记为真的数字时,请增加计数器。如果位掩码包含每个数字,则通过返回数字的长度(最高指数)进行短路,否则返回计数器。

这不会降低O复杂度,但它会减少内存占用并为大数量增加一些短路。

例如,如果第一个数字是1234567890,那么它将始终与第二个数字共享多少位数,因为第二个数字具有小数位数。

答案 4 :(得分:2)

哈希表太过分了,只需使用十个标志的数组(无论是将它们打包成一个整数,还是按位运算,或者保持自变量都取决于你)。

扫描第一个整数并为每个数字提高相关标志。

扫描第二个整数并重置每个数字的相关标志。

返回真正的重置次数(从1到0)。

更新:要处理重复项,需要用计数器替换标志。

如果两个数字都有M个数字,则必须全部查看它们,以便时间复杂度肯定为Ω(M)。

空间复杂性的情况不太清楚。这里给出的所有解都是O(N),其中N是可能的位数(十进制基数为10)。但是O(1)空间是可能的:依次取第一个数字的每个数字,检查它是否是第一个数字中的第一个数字(以避免重复计数),然后检查它是否存在于第二个数字中。这是一个O(M²)时间过程,但仅限O(1) - 空间。

更新:处理重复项,每次处理数字时,计算第一个数字中相同的前任数量;当在第二个数字中寻找匹配时,也匹配前辈的数量。

所以有人可能想知道O(M)时间,O(1)空间解决方案是否可行。

我的解决方案是O(M + N) - 时间,O(N) - 空间。时间复杂度中的+ N仅用于初始化所有N个标志。如果您接受不计算初始化,您可以编写算法,以便清除它设置的所有标志(以便可以再次播放算法),这会产生O(M) - 时间,O( N)空间解决方案。

有一个简单的O(M Log M)时间,O(M)空间解决方案,通过对两个数字串进行排序并在类似合并的步骤中计算相同的数字。如果M与N相比非常小,则可以使用。

答案 5 :(得分:1)

您的解决方案并未考虑重复项。

# 11, 211 -> 2

您使用哈希是正确的,它比数组更快。要在python中执行此操作,因为键入更快...

# Returns an array containing the shared digits
def getShared(num1, num2):
    shared = []
    digits1 = getDigits(num1)
    digits2 = getDigits(num2)
    for digit in digits1:
        if digit in digits2:
            while digits2[digit] > 1 and digits1[digit] > 1:
                digits2[digit] = digits2[digit] - 1
                digits1[digit] = digits1[digit] - 1
                shared += [digit]
            del digits2[digit]
            del digits1[digit]
    return shared

# Given an integer, returns a dictionary with the keys as the digits,
# and the value as the number of times the digit appears
def getDigits(num)
    dict = {}
    while num > 0:
        newDigit = num % 10
        if newDigit not in dict:
            dict[newDigit] = 1
        else:
            dict[newDigit] = dict[newDigit] + 1
    return dict