学习目标:
掌握二分查找算法以及变种Leftmost、Rightmost查找,刷Leetcode相关练习题。
- 掌握基本二分查找算法
- 掌握Leftmost、Rightmost查找算法
- 练习Leetcode相关练习题
基本二分查找算法:
前提:有序数组
算法思想:每次比较数组中间位置元素与待查target元素,若中间位置元素与target相等,则返回索引表示找到,当中间位置元素值大于target,表示待查元素在target左面,此时查找target左边数组,当中间位置元素小于target,表示待查元素在target右边,此时查找target右边元素。
public static int binarySearchBasic(int[] a, int target) {
int i = 0, j = a.length - 1; // 设置指针和初值
while (i <= j) { // 表示target与ij范围内包含ij比较
int m = (i + j) >>> 1;
if (target < a[m]) { // 目标在左边
j = m - 1;
} else if (a[m] < target) { // 目标在右边
i = m + 1;
} else { // 找到了
return m;
}
}
return -1;
}
Leftmost二分查找算法:
前提:有序数组
算法思想:在二分查找算法思想上考虑了重复元素的影响,返回的是最左侧的重复元素。
public static int binarySearchLeftmost(int[] a, int target) {
int i = 0, j = a.length - 1;
int candidate = -1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m - 1;
} else if (a[m] < target) {
i = m + 1;
} else {//此时表示查找到了但考虑重复元素影响
candidate = m; // 记录候选位置
j = m - 1; // 继续向左
}
}
return candidate;
}
改进:当查找到时返回最左侧索引,当查找不到时放回待插入位置索引
public static int binarySearchLeftmost2(int[] a, int target) {
int i = 0, j = a.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target <= a[m]) {
j = m - 1;
} else {
i = m + 1;
}
}
return i;
}
Rightmost二分查找算法:
前提:有序数组
算法思想:较Leftmost返回重复元素的最右边元素
public static int binarySearchRightmost(int[] a, int target) {
int i = 0, j = a.length - 1;
int candidate = -1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m - 1;
} else if (a[m] < target) {
i = m + 1;
} else {
candidate = m; // 记录候选位置
i = m + 1; // 继续向右
}
}
return candidate;
}
改进:当查找到时返回最右侧索引,当查找不到时放回待插入位置索引
public static int binarySearchRightmost2(int[] a, int target) {
int i = 0, j = a.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m - 1;
} else {
i = m + 1;
}
}
return i - 1;
}
Leetcode相关练习题
解题思路:Leftmost改进版,找到时返回重复元素最左侧索引,找不到时放回待插入位置
class Solution {
public int searchInsert(int[] a, int target) {
int i = 0, j = a.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target <= a[m]) {
j = m - 1;
} else {
i = m + 1;
}
}
return i;
}
}
思路:一次leftmost找左位置,一次rightmost找右位置,这里不需要插入,只需要找到,所以在leftmost中要有candidate记录当前找到的中间位置
class Solution {
public int[] searchRange(int[] nums, int target) {
int x = left(nums, target);
if(x == -1){
return new int[]{-1,-1};
}else{
return new int[]{x, right(nums, target)};
}
}
public int left(int[] a, int target){
int i = 0, j = a.length-1;
int candidate = -1;
while(i <= j){
int m = (i + j) >>> 1;
if(target < a[m]){
j = m-1;
}else if(target > a[m]){
i = m +1;
}else{
candidate = m;
j = m-1;
}
}
return candidate;
}
public int right(int[] a, int target){
int i = 0, j = a.length-1;
int candidate = -1;
while(i <= j){
int m = (i + j) >>> 1;
if(target < a[m]){
j = m-1;
}else if(target > a[m]){
i = m +1;
}else{
candidate = m;
i = m+1;
}
}
return candidate;
}
}