排序数组中最右边最小元素的索引

时间:2017-04-06 03:38:48

标签: java arrays

我有一个很大的排序数组,我想快速找到最正确最小元素的索引。 O(logn)时间

例如

INDEX: 0 1 2 3 4 5 6 7 8 9 10 11 12
VALUE: 5 5 5 5 5 5 5 5 6 8  9  9 10 ......... N

The answer is 7

我的观点是二元搜索,但我有疑问

1 个答案:

答案 0 :(得分:3)

由于输入已排序,二进制搜索将在 O(log n)中找到解决方案。

对于认为自己 O(n)的人来说,因为他们认为必须进行线性搜索才能找到边界,所以你不会考虑它。< / p>

要查找边界,在找到目标值的索引时,不要停止二进制搜索。您继续执行二进制搜索,直到查看相邻索引。

为了说明(我将编码作为练习留给感兴趣的各方):

INDEX: 0 1 2 3 4 5 6 7 8 9 10 11 12
VALUE: 5 5 5 5 5 5 5 5 6 8  9  9 10

在索引0处找到最小值,因此5。现在搜索5直到找到边界,即直到找到左边有值且右边有更高值的相邻索引:

INDEX: 0 1 2 3 4 5 6 7 8 9 10 11 12
VALUE: 5 5 5 5 5 5 5 5 6 8  9  9 10
       |-----------|--------------|  Found 5, so search right
                   |-----|--------|  Found 8, so search left
                   |-|---|           Found 5, so search right
                     |-|-|           Found 6, so search left
                     |-|             Adjacent, so search complete

在索引7和8之间找到边界,因此最小值的最后一个索引是: 7

这可以推广到查找任何目标数的第一个/最后一个索引,或查找小于目标的最后一个索引,或者第一个索引的数字大于目标,无论目标数是否实际存在于输入中。

<强>更新

因为我喜欢NavigableSetNavigableMap的关系搜索操作,所以我认为实现Arrays.binarySearch(int[] a, int key)的{​​{1}}等效方法可能很有趣,{{1} } {,ceiling()floor(),以及higher()的{​​{1}}和lower()变体。

为了只实现主二进制搜索逻辑一次,我决定使用函数接口/ lambda表达式来处理比较逻辑。克隆代码或使用first()可能会表现得更好,但是,我只是 有趣

因此,以下是last()的6种二元搜索方法,以及主搜索逻辑的get()方法:

boolean

由于有些人似乎很难接受 O(log n)的复杂性,我在主搜索逻辑中添加了统计信息。

这是测试代码,对9个值(n = 9)进行36次测试。对于统计数据,不计算空输入的搜索。

int[]

输出,搜索值5

private

可以看出,执行的平均搜索/** * Returns the least index in this array with value strictly equal to the given key, * or {@code -1} if there is no such key. */ public static int binaryFirst(int[] a, int key) { int idx = binaryCeiling(a, key); return (idx < 0 || a[idx] != key ? -1 : idx); } /** * Returns the greatest index in this array with value strictly equal to the given key, * or {@code -1} if there is no such key. */ public static int binaryLast(int[] a, int key) { int idx = binaryFloor(a, key); return (idx < 0 || a[idx] != key ? -1 : idx); } /** * Returns the greatest index in this array with value strictly less than the given key, * or {@code -1} if there is no such key. */ public static int binaryLower(int[] a, int key) { return binarySearch(a, x -> Integer.compare(x, key) < 0); } /** * Returns the greatest index in this array with value less than or equal to the given key, * or {@code -1} if there is no such key. */ public static int binaryFloor(int[] a, int key) { return binarySearch(a, x -> Integer.compare(x, key) <= 0); } /** * Returns the least index in this array with value greater than or equal to the given key, * or {@code -1} if there is no such key. */ public static int binaryCeiling(int[] a, int key) { int idx = binaryLower(a, key) + 1; return (idx == a.length ? -1 : idx); } /** * Returns the least index in this array with value strictly greater than the given key, * or {@code -1} if there is no such key. */ public static int binaryHigher(int[] a, int key) { int idx = binaryFloor(a, key) + 1; return (idx == a.length ? -1 : idx); } private static int minComp = Integer.MAX_VALUE; // For stats private static int maxComp = Integer.MIN_VALUE; // For stats private static int countComp = 0; // For stats private static int countSearch = 0; // For stats private static int binarySearch(int[] a, IntPredicate searchRight) { if (a.length == 0) return -1; int comp = 0; // For stats int first = 0, last = a.length - 1; while (first + 1 < last) { int mid = (first + last) / 2; comp++; // For stats if (searchRight.test(a[mid])) first = mid; else last = mid; } int result; if (first == last || first == 0) { comp++; // For stats result = (searchRight.test(a[first]) ? first : first - 1); } else if (last == a.length - 1) { comp++; // For stats result = (searchRight.test(a[last]) ? last : last - 1); } else { result = first; } minComp = Math.min(minComp, comp); // For stats maxComp = Math.max(maxComp, comp); // For stats countComp += comp; // For stats countSearch++; // For stats return result; } 比较(public static void main(String[] args) { System.out.println(" = = < <= >= >"); System.out.println("First Last Lower Floor Ceiling Higher Input"); test(); test(1,1,1,1,1,9,9,9,9,9); test(1,1,1,5,5,5,9,9,9,9); test(1,1,1,1,1,1,1,1,1,1); test(5,5,5,5,5,5,5,5,5,5); test(9,9,9,9,9,9,9,9,9,9); test(0,1,2,3,4,5,6,7,8,9); System.out.printf("%nStats: min=%d, max=%d, avg=%s%n", minComp, maxComp, countComp / (double) countSearch); } private static void test(int... a) { System.out.printf("%3d%7d%7d%7d%7d%7d %s%n", binaryFirst(a, 5), binaryLast(a, 5), binaryLower(a, 5), binaryFloor(a, 5), binaryCeiling(a, 5), binaryHigher(a, 5), Arrays.toString(a)); } ),最差情况为 = = < <= >= > First Last Lower Floor Ceiling Higher Input -1 -1 -1 -1 -1 -1 [] -1 -1 4 4 5 5 [1, 1, 1, 1, 1, 9, 9, 9, 9, 9] 3 5 2 5 3 6 [1, 1, 1, 5, 5, 5, 9, 9, 9, 9] -1 -1 9 9 -1 -1 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 0 9 -1 9 0 -1 [5, 5, 5, 5, 5, 5, 5, 5, 5, 5] -1 -1 -1 -1 0 0 [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] 5 5 4 5 5 6 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Stats: min=3, max=5, avg=3.75 比较以执行一次搜索。

我还做了4个10000个值的不同数组,共24次搜索:

3.75

同样,log2(9) = 3.169925的平均值与搜索10000个值(5)进行比较。

我认为这充分证明了我对搜索的 O(log n)复杂性的断言。

要完全解决问题:

Stats: min=14, max=15, avg=14.375

输出

14.375