34、排序数组中查找元素的第一和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[]{-1, -1};
int left = 0;
int right = nums.length - 1;
// 寻找左边界
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] < target){
left = mid + 1;
}else if(nums[mid] > target){
right = mid - 1;
}else{
if(mid == 0 || nums[mid-1] != nums[mid]){
res[0] = mid;
break;
}else{
right = mid - 1;
}
}
}
int left1 = 0;
int right1 = nums.length - 1;
// 寻找右边界
while(left1 <= right1){
int mid1 = left1 + (right1 - left1) / 2;
if(nums[mid1] < target){
left1 = mid1 + 1;
}else if(nums[mid1] > target){
right1 = mid1 - 1;
}else{
if(mid1 == nums.length - 1 || nums[mid1] != nums[mid1+1]){
res[1] = mid1;
break;
}else{
left1 = mid1 + 1;
}
}
}
return res;
}
}
注意:| 和 ||的区别,前者还要判断后一个条件;后者若第一个条件是true,则不会判断第二个条件。
35、搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] < target){
if(mid == nums.length-1){
return nums.length;
}else if(nums[mid+1] > target){
return mid+1;
}else{
left = mid + 1;
}
}else{
if(mid == 0){
return 0;
}else if(nums[mid-1] < target){
return mid;
}else{
right = mid - 1;
}
}
}
return -1;
}
}
注意:
1、先写出二分搜索的框架,再在框架中考虑特殊情况(搜索不到/到达边界)
2、最后一定要return -1???
另:
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
// 定义target在左闭右闭的区间,[low, high]
int low = 0;
int high = n - 1;
while (low <= high) { // 当low==high,区间[low, high]依然有效
int mid = low + (high - low) / 2; // 防止溢出
if (nums[mid] > target) {
high = mid - 1; // target 在左区间,所以[low, mid - 1]
} else if (nums[mid] < target) {
low = mid + 1; // target 在右区间,所以[mid + 1, high]
} else {
// 1. 目标值等于数组中某一个元素 return mid;
return mid;
}
}
// 2.目标值在数组所有元素之前 3.目标值插入数组中 4.目标值在数组所有元素之后 return right + 1;
return high + 1;
}
}
//第二种二分法:左闭右开
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) { //左闭右开 [left, right)
int middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle; // target 在左区间,在[left, middle)中
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在 [middle+1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值的情况,直接返回下标
}
}
// 目标值在数组所有元素之前 [0,0)
// 目标值插入数组中的位置 [left, right) ,return right 即可
// 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
return right;
}
这两种方法绝了,写出了精髓
977、有序数组的平方
给你一个按非递减顺序排序的整数数组 nums
,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
class Solution {
public int[] sortedSquares(int[] nums) {
int lenth = nums.length;
int[] res = new int[lenth];
int cnt = nums.length-1;
// 两个指针分别向两边搜索较大的那个值
int left = 0;
int right = nums.length-1;
while(left <= right){
if(Math.abs(nums[left]) <= Math.abs(nums[right])){
res[cnt] = nums[right] * nums[right];
cnt--;
right--;
}else{
res[cnt] = nums[left] * nums[left];
cnt--;
left++;
}
}
return res;
}
}
其实就是简单的双指针的思想。 不过可以把++和--放到赋值运算中去,使代码更简洁。
res[cnt--] = nums[right] * nums[right--];
209、长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target。
找出该数组中满足其和 ≥ target 的长度最小的连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0; //滑动窗口起始位置
int sum = 0;
int result = Integer.MAX_VALUE;
for(int j = 0; j < nums.length; j++){
sum += nums[j];
while(sum >= target){
result = Math.min(result, j - i + 1);
sum -= nums[i++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}
注意滑动窗口的思想,确定窗口终止位置,改变起始位置