二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想。每次通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0
二分查找的时间复杂度 O ( l o g n ) O(logn) O(logn)
非递归实现:
public int bsearch(int[] nums, int value) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] > value) {
high = mid - 1;
} else if (nums[mid] < value) {
low = mid + 1;
} else {
return mid;
}
}
return -1;
}
递归实现:
public int bsearch(int[] nums, int value) {
return bsearchInternally(nums, 0, nums.length - 1, value);
}
private int bsearchInternally(int[] nums, int low, int high, int value) {
if(low>high) return -1;
int mid = low + ((high - low) >> 1);
if (nums[mid] > value) {
return bsearchInternally(nums, low, mid - 1, value);
} else if (nums[mid] < value) {
return bsearchInternally(nums, mid + 1, high, value);
} else {
return mid;
}
}
首先,二分查找依赖的是数组,主要原因是二分查找算法需要按照下标随机访问元素。数组按照下标随机访问数据的时间复杂度是 O ( 1 ) O(1) O(1),而如果使用链表,链表随机访问的时间复杂度是 O ( n ) O(n) O(n)。所以,如果数据使用链表存储,二分查找的时间复杂度就会变得很高
其次,二分查找针对的是有序数据
public int bsearch(int[] nums, int value) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] > value) {
high = mid - 1;
} else if (nums[mid] < value) {
low = mid + 1;
} else {
if ((mid == 0) || nums[mid - 1] != value) return mid;
else high = mid - 1;
}
}
return -1;
}
public int bsearch(int[] nums, int value) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] > value) {
high = mid - 1;
} else if (nums[mid] < value) {
low = mid + 1;
} else {
if ((mid == 0) || nums[mid + 1] != value) return mid;
else low = mid + 1;
}
}
return -1;
}
public int bsearch(int[] nums, int value) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] >= value) {
if (mid == 0 || nums[mid - 1] < value) return mid;
else high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
public int bsearch(int[] nums, int value) {
int low = 0;
int high = nums.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (nums[mid] <= value) {
if (mid == 0 || nums[mid + 1] > value) return mid;
else low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
实现int sqrt(int x)
函数
计算并返回x的平方根,其中x是非负整数
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,由于返回类型是整数,小数部分将被舍去
题解:
public int mySqrt(int x) {
long low = 0;
long high = x / 2 + 1;
while (low < high) {
//这里一定取右中位数,如果取左中位数,代码会进入死循环
long mid = low + ((high - low + 1) >> 1);
long result = mid * mid;
if (result > x) {
high = mid - 1;
} else {
low = mid;
}
}
return (int) low;
}
给定一个正整数num,编写一个函数,如果num是一个完全平方数,则返回True,否则返回False
说明:不要使用任何内置的库函数,如sqrt
示例 1:
输入:16
输出:True
示例 2:
输入:14
输出:False
题解:
public boolean isPerfectSquare(int num) {
long low = 0;
long high = num / 2 + 1;
while (low < high) {
long mid = low + ((high - low + 1) >> 1);
long result = mid * mid;
if (result > num) {
high = mid - 1;
} else {
low = mid;
}
}
return low * low == num ? true : false;
}
假设按照升序排序的数组在预先未知的某个点上进行了旋转
(例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])
请找出其中最小的元素
你可以假设数组中不存在重复元素
示例 1:
输入: [3,4,5,1,2]
输出: 1
示例 2:
输入: [4,5,6,7,0,1,2]
输出: 0
题解:
public int findMin(int[] nums) {
int low = 0;
int high = nums.length - 1;
//nums.length==1时nums[high]==nums[0]
if (nums[high] >= nums[0]) return nums[0];
while (high >= low) {
int mid = low + ((high - low) >> 1);
//先判断nums[mid + 1] < nums[mid]再判断nums[mid - 1] > nums[mid],否则会出现数组索引越界,案例为[2,1]
if (nums[mid + 1] < nums[mid]) {
return nums[mid + 1];
}
if (nums[mid - 1] > nums[mid]) {
return nums[mid];
}
if (nums[mid] > nums[0]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
假设按照升序排序的数组在预先未知的某个点上进行了旋转
(例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1
你可以假设数组中不存在重复的元素
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
题解:
public int search(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
while (high >= low) {
int mid = low + ((high - low) >> 1);
if (nums[mid] == target) return mid;
if (nums[low] <= nums[mid]) {
if (target >= nums[low] && target < nums[mid]) high = mid - 1;
else low = mid + 1;
} else {
if (target > nums[mid] && target <= nums[high]) low = mid + 1;
else high = mid - 1;
}
}
return -1;
}
编写一个高效的算法来判断m x n
矩阵中,是否存在一个目标值。该矩阵具有如下特性:
示例 1:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true
示例 2:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false
题解:
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
if (m == 0) return false;
int n = matrix[0].length;
int left = 0, right = m * n - 1;
int pivotIdx, pivotElement;
while (left <= right) {
pivotIdx = (left + right) / 2;
pivotElement = matrix[pivotIdx / n][pivotIdx % n];
if (target == pivotElement) return true;
else {
if (target < pivotElement) right = pivotIdx - 1;
else left = pivotIdx + 1;
}
}
return false;
}
常用数据结构的时间、空间复杂度:
https://www.bigocheatsheet.com/