开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第29天,点击查看活动详情
前言
本文主要介绍如何用查找有序表中等于N的最左位置。
看到这个题目也许你会想,这还不简单,直接for循环遍历一下不就可以了?没错,可以用for循环遍历一下就能得到答案,好了本文结束。
开个玩笑,本文介绍一种更高效的方式来实现,用二分查找法。
在解决查找有序表中等于N的最左位置之前,首先来看下如何用二分查找一个有序表中是否存在N。
二分法
二分查找算法的原理如下:
- 给出一个有序表
arr以及要查找的数T。 - 初始化目标查找区间:
L = 0,R = N。 - 若查找区间
[L, R]不存在,则查找失败,否则进行步骤3。 - 取目标查找区间中间位 M =
(L + R) / 2,比较要查找的数T与arr[M]。 - 若
T < arr[M],R = M - 1,查找在左半区间进行,进行步骤2。 - 若
T > arr[M],则L = M + 1, 查找在右半区间进行,进行骤2。 - 若
T = arr[M],则查找成功,返回 M 值。
有序表中是否存在N
给定一个有序数组arr=[1, 2, 3, 5, 7, 8, 9, 12, 13],查找12是否存在该有序数组中。
数组arr长度为9,9 / 2 = 4,进行折半查找,分为两部分[0,4],[5,8]。
首先判断arr[4]与目标数值12的大小,arr[4]为7,12是大于7的,所以目标数据在右边段,左边的就可以直接忽略。
再把右半段进行折半,以此类推,直到最后一个元素或直到目标元素。
下面来看下代码实现。
public static boolean find(int[] arr, int N) {
if (arr == null || arr.length == 0) {
return false;
}
int L = 0;
int R = arr.length - 1;
while (L <= R) {
int M = (L + R) / 2;
if (arr[M] == N) {
return true;
} else if (arr[M] < N) {
L = M + 1;
} else {
R = M - 1;
}
}
return false;
}
大于等于N的最左位置
查找有序表中大于等于N的最左位置的意思为,在一个有序表中查找一个元素,这个元素可能存在多个也可能不存在,如果存在的话,要找出从左边数第一个大于等于目标元素的位置。
例如,给定一个有序数组arr=[1, 1, 2, 2, 2, 3, 3, 5, 7, 8, 9, 12, 13],从这个有序数组中找出大于等于2的最左位置,此时针对arr这个数组,大于等于2的最左位置为1。
思路分析
对有序数组进行二分arr=[1, 1, 2, 2, 2, 3, 3, 5, 7, 8, 9, 12, 13],二分过程如下:
- 对整个有序表进行二分,
L = 0,R = 12。 - 取中间值
M = 6,对应的值arr[6] = 3,3是大于等于2的。 - 用一个临时变量记录
t = 6。 - 继续对左半段进行二分,
L = 0,R = 5。 - 取中间位置
M = 2,arr[2] = 2,2是大于等于2的。 - 此时需要更换临时变量的值
t = 2。 - 继续对左侧进行二分,
L = 0,R = 1。 - 取中间位置
M = 0,arr[0] = 1,此时1小于2,不更新临时变量的值。 - 对右边段进行二分,
L = 1,R = 1。 - 取中间位置
M = 1,arr[1] = 1,此时1小于2,不更新临时变量的值。 - 继续二分,
L = 2,R = 1,此时L > R,不能进行二分了。 - 二分结束,得到最终结果为
2。
代码实现
public static int find(int[] arr, int N) {
if (arr == null || arr.length == 0) {
return -1;
}
int L = 0;
int R = arr.length - 1;
int res = -1;
while (L <= R) {
int M = (L + R) / 2;
if (arr[M] >= N) {
res = M;
R = M - 1;
} else {
L = M + 1;
}
}
return res;
}
拓展
我们知道了如何用二分法查找有序表中大于等于N的最左位置,同理,我们也可以根据上面的思路来实现如何用二分法查找有序表中大于等于N的最右位置,思路是一样的,这里就不再赘述了,有兴趣的小伙伴可以自己尝试下,有问题欢迎留言交流。