一、二分查找
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0) {
return -1;
}
int l = 0, r = nums.length - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(nums[mid] == target){
return mid;
} else if(nums[mid] < target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return -1;
}
第一天
第一题:在排序数组中查找元素的第一个和最后一个位置
34.在排序数组中查找元素的第一个和最后一个位置
思路
因为序列已经排序,采用二分查找无疑就是很方便的方法。
一种方式采用二分找到所要求的元素,然后分别向前向后遍历,找第一个出现位置和最后一次出现位置,但这样在特殊情况下比如所有元素均为所求元素时时
间复杂度仍为O(n)。
另一种采用二分进行两次逼近,一次向左逼近,一次向右逼近,找到第一次和最后一次位置,这样保证无论什么情况均为O(logn)
代码
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0, right = nums.length - 1;
int[] count = {-1,-1};
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] == target){
left = mid;
right = mid;
while(left >= 0 && nums[left] == target){
left--;
}
while(right < nums.length && nums[right] == target){
right++;
}
count[0] = left+1;
count[1] = right-1;
break;
}else if(nums[mid] > target){
right = mid - 1;
}else{
left = mid + 1;
}
}
return count;
}
}
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0, right = nums.length - 1;
int[] count = {-1,-1};
if(left > right){
return count;
}
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] >= target){
right = mid;
}else{
left = mid + 1;
}
}
if(nums[left] != target){
return count;
}else{
count[0] = left;
}
right = nums.length;
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] <= target){
left = mid+1;
}else{
right = mid;
}
}
count[1] = left - 1;
return count;
}
}
第二题:搜索旋转排序数组
33.搜索旋转排序数组
思路
因为排序数组进行了旋转也就是移位,使得二分中简单的判断已经不符合要求,需要加入新的判断条件来判断目标元素在中间下标之前还是之后。
当中间下标元素值小于目标元素时,如果目标元素也小于或等于右指针下标元素,则说明目标若存在,则必在中间下标与右指针下标之间,移动左指针,反之
如果目标元素大于右指针下标元素,则说明目标若存在,则必在中间下标与左指针下标之间,移动右指针。
当中间下标元素值大于目标元素时分析同理,与左指针下标元素比较,如果目标元素也大于或等于左指针下标元素,移动右指针,反之移动左指针
代码
class Solution {
public int search(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
while(low <= high){
int mid = low + (high - low) / 2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] < nums[high]){
if(nums[mid] < target && target <= nums[high]){
low = mid + 1;
}else{
high = mid - 1;
}
}else{
if(nums[low] <= target && nums[mid] > target){
high = mid - 1;
}else{
low = mid + 1;
}
}
}
return -1;
}
}
第三题:搜索二维矩阵
74.搜索二维矩阵
思路
标准的二分搜索题,数组变成了二维,但按题意按行先的话元素依然是标准的升序的,现在用左右指针表示行先的元素的位置,则中间位置的行数下标为中间
位置除以列数的商,列数下标为中间位置除以列数的余数。
代码
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length == 0 || matrix[0].length == 0){
return false;
}
int m = matrix.length,n = matrix[0].length;
int low = 0,high = m * n - 1;
while(low <= high){
int mid = low + (high - low) / 2;
int i = mid / n;
int j = mid % n;
if(matrix[i][j] == target){
return true;
}else if(matrix[i][j] < target){
low = mid + 1;
}else{
high = mid - 1;
}
}
return false;
}
}
第二天
第四题:寻找旋转排序数组中的最小值
153.寻找旋转排序数组中的最小值
思路
排序数组旋转之后的数组无非就是两段升序的数组,从开始到中间某一个数为升序,然后接着就是最小数到最后的一个升序,找最小值就是找第二个升序数组
的开始小标,因元素互不相同可知第二个升序数组的最大值是小于第一个升序数组最小值的。所以采用二分法时,若中间位置的元素大于或等于0下标元素,
则说明中间元素在第一个升序数组中,则移动左指针,否则移动右指针。
因为旋转次数可能为数组长度的整数倍,这时数组整体为升序,找第二个升序数组的开始下标会找到数组末尾,这恰恰是最大的元素,因此,在完成二分查找
后,还应与数组第一个元素进行比较,如果第一个元素更小应返回第一个元素。
代码
class Solution {
public int findMin(int[] nums) {
int l = 0, r = nums.length - 1;
while(l < r){
int mid = l + (r - l) / 2;
if(nums[mid] >= nums[0]){
l = mid + 1;
}else{
r = mid;
}
}
return nums[l] < nums[0]?nums[l]:nums[0];
}
}
第五题:寻找峰值
162.寻找峰值
思路
本题可以理解为找函数的极大值点,由于nums[-1] = nums[n] = -∞的存在,可知必然存在极大值点。
当采用二分算法时,如果中间位置大于下一个元素,则可知在中间位置到下一个位置是下降的,因为nums[-1] = -∞,则可知在左指针位置到中间位置必有先
上升后下降,即必有大极点,则移动右指针。如果中间位置小于下一个元素,则可知在中间位置到下一个位置是上升的,因为nums[n] = -∞,则可知在中间
位置到右指针必是先上升后下降,即必有极大点,则移动左指针。
代码
class Solution {
public int findPeakElement(int[] nums) {
int l = 0, r = nums.length - 1;
while(l < r){
int mid = l + (r - l) / 2;
if(nums[mid] > nums[mid + 1]){
r = mid;
}else{
l = mid + 1;
}
}
return l;
}
}
|