最近我遇到了这个采访问题:
在数组中给出n个实数。如果数组中的数字超过n / 10次,则数组中的数字称为十进制显性。给出O(n)时间算法以确定给定数组是否具有十进制显性。
现在我可以想出几种方法来解决这个问题,以及对这个问题的任何概括(即找出在数组中出现K次的任何数字)
可以在第1遍中创建哈希表,然后计算第2遍中的出现次数,即O(n),但也使用O(n)空间,
有一种方式using 9 buckets
但是,我们有什么方法可以在一个恒定的空间里做到这一点?有什么建议??
[编辑]我没有检查过9桶解决方案,读者可能想通过下面的n.m.评论
答案 0 :(得分:8)
我已经概述了一种基于Boyer-Moore多数投票算法here的方法。
你记得(最多)(m-1)
元素,这些元素最近比其他元素和相关数量更常见。当看到记住的元素时,其计数会增加。当看到一个未记忆的元素时,如果有一个空闲的插槽,你会记住它并将其计数设置为1,否则你从记忆元素的所有计数中减去1。忘记了计数为0的记忆元素。任何比例高于1/m
的元素都是记忆元素之一。如果您不知道有m-1
个元素出现超过n/m
次的先验,则需要第二遍来计算记住元素的出现次数。
编辑:在访问指定的页面后,我发现它是完全相同的解决方案,但它没有考虑到记住的非支配者。
一旦完成,任何计数大于1的计数变量在列表中都有超过10个自身实例。
不正确,但最后会记住所有小数统治者的计数>= 1
。
我没有通过那里的代码,所以它可能包含错误,但算法是正确的,除了缺少检查通行证。
如果我们有第二遍,状态演变
,则正确处理拟议的反例0 1 2 3 4 5 6 7 8
1 1 1 1 1 1 1 1 1 // coming 9
0 0 0 0 0 0 0 0 0 // all forgotten, no slots occupied, coming 10
10 - - - - - - - -
1 - - - - - - - - // coming 0
10 0 - - - - - - -
1 1 - - - - - - -
最后,有两个插槽被占用,10和0都被记住,计数为1. 10不是十进制显性,但是0是,并且它是唯一的,因为将由第二次检查通行证。
答案 1 :(得分:1)
public static int[] KthDominants(int[] arr, int kth)
{
var data = new Dominants(arr.Length, kth);
for (int i = 0; i < arr.Length; i++)
{
data.Push(arr[i]);
}
return data.ToArray();
}
Dominants类将kth-1计数器存储在Dictionary中。它使用System.Linq来处理集合。
public class Dominants
{
int _size;
int _kth;
int _grade;
Dictionary<int, int> _counters;
public Dominants(int size, int kth)
{
_size = size;
_kth = kth;
_counters = new Dictionary<int, int>();
}
public void Push(int key)
{
if (_counters.ContainsKey(key))
_counters[key]++;
else
{
// The trick is that the dominant element remains same
// if you delete any k distinct items from the array.
if (_counters.Count < _kth)
_counters.Add(key, 1);
else
{
foreach (var i in _counters.Keys.ToArray())
{
if (_counters[i] > 1)
_counters[i]--;
else
_counters.Remove(i);
}
_grade++;
}
}
}
public int[] ToArray()
{
var cnt = _size / _kth - (_grade + 1);
return _counters
.Where(i => i.Value > cnt)
.Select(i => i.Key)
.ToArray();
}
}
答案 2 :(得分:1)
您可以使用 3-way-partition 快速选择来查找数组的第(n / 10)个最大元素,然后检查它是否为十进制 - 优势;如果没有,则在右子阵列中重现。
这可以在线性预期时间内就地完成,因此不是严格恒定的空间。
答案 3 :(得分:1)
使用3向qSort Java实现打印
public class DecimalDominants {
public static void sort(Comparable[] arr) {
sort(arr, 0, arr.length - 1);
}
private static void sort(Comparable[] arr, int lo, int hi) {
if (lo >= hi) return;
int lt = lo, gt = hi;
Comparable v = arr[lo];
int i = lo;
while (i <= gt) {
int cmp = arr[i].compareTo(v);
if (cmp < 0) swap(arr, lt++, i++);
else if (cmp > 0) swap(arr, i, gt--);
else i++;
}
int times = gt - lt + 1;
if (times > arr.length / 10) System.out.printf("%s repeats %d times\n", v, times);
sort(arr, lo, lt - 1);
sort(arr, gt + 1, hi);
}
private static void swap(Comparable[] arr, int i, int j) {
Comparable swap = arr[i];
arr[i] = arr[j];
arr[j] = swap;
}
public static void main(String[] args) {
Integer[] arr = {1, 2, 1, 2, 1, 2, 1, 4, 3, 2, 4, 2, 3, 5, 6, 7, 8, 9, 3, 2, 4, 5, 2, 3, 6, 7, 8, 3, 2, 5};
System.out.println("print numbers which repeat more then " + arr.length / 10);
sort(arr);
}
}
答案 4 :(得分:0)
执行循环并将值存储/添加到实数数组int []中。
执行另一个循环,如果数组列表值大于1,则将数组列表值除以10。
答案 5 :(得分:0)
具有线性时间复杂度和恒定空间复杂度的三向排序。 最糟糕的情况是,该递归调用将进行10次
def three_way_partition(arr, start, end, dominants):
if (end - start + 1) >= int(len(arr)/10):
lt = start
gt = end
i = lt
while True:
if arr[i] < arr[lt]:
arr[lt], arr[i] = arr[i], arr[lt]
i += 1
lt += 1
elif arr[i] > arr[lt]:
arr[gt], arr[i] = arr[i], arr[gt]
gt -= 1
else:
i += 1
if i > gt:
break
if gt - lt + 1 > int(len(arr)/10):
dominants.append(arr[lt])
three_way_partition(arr, start, lt-1, dominants)
three_way_partition(arr, gt + 1, end, dominants)
def decimal_dominants(arr):
dominants = []
three_way_partition(arr, 0, len(arr)-1, dominants)
return dominants