1.局部最小值
给定一个数组,相邻的数不相等, 返回一个局部最小值的索引。
func getLessIndex(arr []int) int {
n := len(arr)
if n == 0 {
return -1 // no exist
}
if n == 1 || arr[0] < arr[1] {
return 0
}
if arr[n - 1] < arr[n - 2] {
return n - 1
}
// 说明在1...n-2之间是这样趋势:\.../
l, r := 1, n - 2
var mid int
for l <= r {
mid = l + ((r - l) >> 1)
if arr[mid] > arr[mid + 1] {
l = mid + 1
} else if arr[mid] > arr[mid - 1] {
r = mid - 1
} else {
return mid
}
}
return -1
}
2.寻找两个有序数组的中位数
LeetCode4. 寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组nums1
和nums2
。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设nums1
和nums2
不会同时为空。
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length; // 短
int m = nums2.length; // 长
if(n > m) {
return findMedianSortedArrays(nums2, nums1);
}
int len = n + m;
// i : nums1前半部分的size
// j : nums2前半部分的size
// 保持 : i + j == len / 2
int i, j;
int l = 0;
int r = n;
// 找到合适的i
while(l <= r) {
i = l + ((r - l) >> 1);
j = len/2 - i;
if(i != 0 && nums1[i - 1] > nums2[j]) {
// 说明nums1[i]太大了,需要向左边移动
r = i - 1;
} else if(i < n && nums2[j - 1] > nums1[i]) {
// 说明nums1[i]太小了,需要向右边移动
l = i + 1;
} else {
// 找到答案
// 如果是奇数的话返回 min(num1[i], nums2[j])
double odd;
if(i == n) {
odd = nums2[j];
} else if(j == m) {
odd = nums1[i];
} else {
odd = Math.min(nums1[i], nums2[j]);
}
if((len & 1) == 1) {
return odd;
}
double even;
if(i == 0) {
even = nums2[j - 1];
} else if(j == 0) {
even = nums1[i - 1];
} else {
even = Math.max(nums1[i - 1], nums2[j - 1]);
}
return (odd + even) / 2.0;
}
}
return 0.0;
}
}
以下几题, 切记首选右侧比较
3.寻找旋转排序数组中的最小值
153.寻找旋转排序数组中的最小值
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]
可能变为[4,5,6,7,0,1,2]
)。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。
class Solution {
public int findMin(int[] nums) {
int l = 0;
int r = nums.length - 1;
int mid;
while(l <= r) {
mid = l + ((r - l) >> 1);
if(nums[mid] < nums[r]) {
r = mid; // mid 是有可能是答案,所以不能r = mid - 1
} else { // nums[mid] > nums[r], 说明右边存在谷
l = mid + 1;
}
}
// 最后答案一定出现在mid位置,但是mid赋给了r,所以r位置就是答案
return nums[r];
}
}
4.寻找旋转排序数组中的最小值 II
154. 寻找旋转排序数组中的最小值 II
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]
可能变为[4,5,6,7,0,1,2]
)。
请找出其中最小的元素。
注意数组中可能存在重复的元素
class Solution {
public int findMin(int[] nums) {
int l = 0;
int r = nums.length - 1;
int mid;
while(l <= r) {
mid = l + ((r - l) >> 1);
if(nums[mid] < nums[r]) { //说明右边有序, mid可能是答案,所以不可以r = mid + 1
r = mid;
} else if(nums[mid] > nums[r]) { // 说明右边存在山谷, 但是此处的mid不可能是答案
l = mid + 1;
} else { // nums[mid] == nums[r], 之间的数据状况无法判断,可能是平地,也可能是山谷,也可能是山峰, 所以只能一个一个试
r--;
}
}
// 这里为什么返回的是l, 而不是r呢?
// 因为在最后一次进入循环也就是"l == r"的时候,
// 会进入"r--"分支, 所以应该返回 "l",
// 或者循环条件写成 l < r, 那么最后的情况就是l == r,
// 此时就是答案,返回l或者r都行,就不用纠结了
return nums[l];
}
}
5.旋转数组
LeetCode33. 搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]
可能变为[4,5,6,7,0,1,2]
)。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1
。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
class Solution {
public int search(int[] nums, int target) {
int l = 0;
int r = nums.length - 1;
int mid;
// 左:[l, mid], 右 : [mid, r]
// 一定要先判断右,再判断左,否则[3,1], target == 1会出现问题
while(l <= r) {
mid = l + ((r - l) >> 1);
if(nums[mid] == target) {
return mid;
} else if(nums[mid] < nums[r]) { // 说明右半部分有序
if(target > nums[mid] && target <= nums[r]) {
l = mid + 1;
} else {
r = mid - 1;
}
} else { // nums[l] < nums[mid], 说明左半部分有序
if(target >= nums[l] && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
}
}
return -1;
}
}
6.旋转数组2
81. 搜索旋转排序数组 II
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,0,1,2,2,5,6]
可能变为[2,5,6,0,0,1,2]
)。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回true
,否则返回false
。
class Solution {
public boolean search(int[] nums, int target) {
int l = 0;
int r = nums.length - 1;
int mid;
while(l <= r) {
mid = l + ((r - l) >> 1);
if(target == nums[mid]) {
return true;
} else if(nums[mid] < nums[r]) { // 右边有序
if(target > nums[mid] && target <= nums[r]) {
l = mid + 1;
} else {
r = mid - 1;
}
} else if(nums[mid] > nums[r]) { // 左边有序
if(target >= nums[l] && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else { // nums[mid] == nums[r], 说明[mid, r]数据状况无法判断,可能相等,可能存在峰和谷
r--;
}
}
return false;
}
}