二分查找
思路
题目要求
- 数组为有序数组
- 数组中无重复元素
- 以上则可以考虑使用二分查找
思路一 左闭右闭[ ] 突破界限
- target在区间[left, right]范围内,要考虑以下几点
- left = 0, right = nums.length - 1
- while (left <= right) 要使用 <= ,因为如果target出现在尾端,left == right时,mid才能取到尾端元素
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
- 如果待查找的元素不存在,那么left最后的指向,是比target大的最近的元素位置,或者是超出数组长度的位置。
- 如果待查找的元素不存在,right最后的指向,是比target小的最近的元素位置,或者是在数组首元素左边的位置。
思路二 左闭右开[ )
- target在区间[left, right)范围内,要考虑以下几点
- left = 0, right = nums.length
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
代码
思路一
class Solution {
public int search(int[] nums, int target) {
//[l r]
int l = 0;
int r = nums.length - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] > target){
r = mid - 1;
}else{
l = mid + 1;
}
}
return -1;
}
}
思路二
class Solution {
public int search(int[] nums, int target) {
// [l r)
int l = 0;
int r = nums.length;
while(l < r){
int mid = l + (r - l) / 2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] > target){
r = mid;
}else{
l = mid + 1;
}
}
return -1;
}
}
复杂度
时间复杂度:O(logn) 证明二分查找的时间复杂度
空间复杂度:O(1)
相似题目
35. 搜索插入位置
34. 在排序数组中查找元素的第一个和最后一个位置
69. x 的平方根
367. 有效的完全平方数
移除元素
思想
题目要求
- 空间复杂度为O(1)
- 移除目标元素后,其他的元素应该排在数组的前面
- 最后返回这些其他元素的个数
思路 重构数组
- 第一个指针指向待填充的数组位置,从0开始
- 第二个指针遍历数组,寻找值 != val的元素
- 直到后,将该值填充到第一个指针的位置处,第一个指针向后移动一位
代码
class Solution {
public int removeElement(int[] nums, int val) {
int index = 0;
for(int i = 0; i < nums.length; i++){
if(nums[i] != val){
nums[index++] = nums[i];
}
}
return index;
}
}
复杂度
时间复杂度:O(n)
空间复杂度:O(1)
相似题目
有序数组的平方
思路
- 原数组nums以排好序,从小到大
- 题目要求的是返回每个数字的平方,并按从小到大的顺序返回数组
- 所以平方的最大值有可能是原数组的第一个元素或最后一个元素
- 一个指针指向首元素,一个指向尾元素,进行比较,谁大谁填充到平方数组末尾
代码
class Solution {
public int[] sortedSquares(int[] nums) {
int l = 0;
int r = nums.length - 1;
int[] result = new int[nums.length];
int index = result.length - 1;
while(l < r){
if(nums[r] * nums[r] >= nums[l] * nums[l]){
result[index--] = nums[r] * nums[r];
r--;
}else{
result[index--] = nums[l] * nums[l];
l++;
}
}
result[0] = nums[l] * nums[l];
return result;
}
}
复杂度
时间复杂度:O(n)
空间复杂度:O(n)
相似题目
88. 合并两个有序数组
283. 移动零
203. 移除链表元素
844. 比较含退格的字符串
977. 有序数组的平方
长度最小的子数组
思路
符合条件的子数组要满足以下的几个条件
- 数组总和大于等于
target
- 要求子数组是连续的
然后从符合条件的子数组中找出长度最小的子数组,并返回其数组长度
思路一 暴力求解
- 通过2个
for
循环确定子数组的左右边界 - 第一个
for
用来确定子数组的左边界 - 第二个
for
用来确定子数组的右边界 - 只要满足条件的子数组,就记录他们的数组长度,统计出最小的长度。
注意:
提示中 1 <= nums.length <= 10^5,暴力求解有可能超时。
思路二 滑动窗口
窗口,就是满足连续子数组和大于等于target的一个区间
滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果,即长度最小的子数组。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
代码
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){
int len = j - i + 1;
result = len < result ? len : result;
sum -= nums[i++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}
复杂度
时间复杂度:O(n)
空间复杂度:O(1)
相似题目
904. 水果成篮
76. 最小覆盖子串
718. 最长重复子数组
螺旋矩阵II
思路
思路一 由外到内 逐圈填充
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。按照左闭右开的原则来填充。
那么要填充多少圈呢?
- 观察可以发现,填充一圈之后,待填充的数组
高度和宽度
都- 2
- 直到待填充数组的高度和宽度都为0时,说明数组全部填充完毕
- 例如:n = 2, 即高度、宽度都为2,那么只需1圈就可以全部填充完毕,即2 / 2 = 1
- 当 n 为奇数时,在进行 n/2 (取整) 次填充后,最后只会剩下一个待填充的位置,进行额外的一次填充即可。
思路二 四个守卫,依次抢占领土
- 一共有
上
下
左
右
四个守卫,他们依次循环占领数组的各行或各列,上
下
占领各行,左
右
占领各列 上
守卫只能从左到右,依次占领这一行,遇到右
则停止。停止之后,说明这一行已经被占领,之后从下一行开始。右
守卫只能从上到下,依次占领这一列,遇到下
则停止。停止之后,说明这一列已经被占领,之后从当前占领的左边开始。下
守卫只能从右到左,依次占领这一行,遇到左
则停止。停止之后,说明这一行已经被占领,之后从上一行开始。左
守卫只能从下到上,依次占领这一列,遇到上
则停止。停止之后,说明这一列已经被占领,之后从当前占领的右边开始。
代码
思路一
class Solution {
public int[][] generateMatrix(int n) {
int x = 0;
int y = 0;
int i, j;
int count = 1;
int offset = 1;
// 循环圈数 n 为 偶数 则循环圈数正好是整数
int loop = n / 2;
int[][] a = new int[n][n];
while(loop-- > 0){
//左到右
for(j = y; j < n - offset; j++){
a[x][j] = count++;
}
//上到下
for(i = x; i < n - offset; i++){
a[i][j] = count++;
}
//右到左
for(; j > y; j--){
a[i][j] = count++;
}
//下到上
for(; i > x; i--){
a[i][j] = count++;
}
x++;
y++;
offset++;
}
if(n % 2 != 0){
a[x][y] = count;
}
return a;
}
}
思路二
class Solution {
public int[][] generateMatrix(int a) {
int m = a;//m行
int n = a;//n列
int l = 0, r = a - 1;
int low = 0, high = m - 1;
int[][] ans = new int[m][n];
int count = 1;
while(true){
//从左到右
for(int i = l; i <= r; i++) ans[low][i] = count++;
if(++low > high) break;
//从上到下
for(int i = low; i <= high; i++) ans[i][r] = count++;
if(--r < l) break;
//从右到左
for(int i = r; i >= l; i--) ans[high][i] = count++;
if(--high < low) break;
//从下到上
for(int i = high; i >= low; i--) ans[i][l] = count++;
if(++l > r) break;
}
return ans;
}
}
复杂度
时间复杂度 O(n^2) 模拟遍历二维矩阵的时间
空间复杂度 O(1)