*二分查找
题目链接
https://leetcode.cn/problems/binary-search/
左闭右闭区间实现
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
/** * 左闭右闭写法 * * @param nums * @param target * @return */ public static int search1(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int left = 0; int right = nums.length - 1; // 要是没有等号的话,在{5}里面找5就会出问题了 while (left <= right) { int center = (left + right) / 2; if (target == nums[center]) { return center; } else if (target > nums[center]) { left = center + 1; } else { right = center - 1; } } return -1; }
左闭右开区间实现
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
/** * 左闭右开写法 * * @param nums * @param target * @return */ public static int search2(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int left = 0; // 区别点 int right = nums.length; // 区别点 while (left < right) { int center = (left + right) / 2; if (target == nums[center]) { return center; } else if (target > nums[center]) { left = center + 1; } else { // 区别点 // 虽然已经知道center位置的元素不是要找的元素,但是因为是左闭右开,因此right还是指向center // 后面无论怎么循环,都不会再使用到center这个为止,因为center = (left + right) / 2永远小于right right = center; } } return -1; }
*移除元素
题目链接
https://leetcode.cn/problems/remove-element/
暴力求解
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
public static int removeElement(int[] nums, int val) { int sameNum = 0; int i = 0; while (i < nums.length - sameNum) { if (val == nums[i]) { // 将后面的元素前移过来 for (int j = i; j < nums.length - 1 - sameNum; j++) { nums[j] = nums[j + 1]; } sameNum++; } if (val == nums[i]) { // 前移过来的数值和val一样,将i-- i--; } i++; } return nums.length - sameNum; }
双指针
- 时间复杂度:O(n)
- 空间复杂度:O(1)
【思想】
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作,将快指针的值赋给慢指针
slow:新数组存储值的索引,因为每次存储完新数组的值,都会将slow++,因此最后直接返回slow即可,需要返回slow+1
fast:用来去前面探索那些不是val的值,然后将这个值赋给slow对应的位置
/** * 双指针 * * @param nums * @param val * @return */ public static int removeElement1(int[] nums, int val) { int slow = 0; for (int fast = 0; fast < nums.length; fast++) { if (nums[fast] != val) { // 如果快指针找的数值不是val,就需要将其存储到新数组中 // 同时slow++,以便存储下一个新数组的值 nums[slow++] = nums[fast]; } } return slow; }
相向双指针法
- 时间复杂度:O(n)
- 空间复杂度:O(1)
上面的实现方法并没有改变元素的相对位置,相向双指针方法改变了元素相对位置,但是确保移动最少元素
/** * 想向双指针 * * @param nums * @param val * @return */ public static int removeElement(int[] nums, int val) { int slow = 0, fast = nums.length - 1; while (slow <= fast) { // slow赋值为第一个等于val的索引 while (slow <= fast) { if (nums[slow] != val) { slow++; } else { break; } } // fast赋值为最后一个等于val的索引 while (fast >= slow) { if (nums[fast] == val) { fast--; } else { break; } } if (slow < fast) { nums[slow++] = nums[fast--]; } } // slow最后一定指向的是 新数组末尾元素的下一个元素 return slow; }
*有序数组的平方
题目链接
https://leetcode.cn/problems/squares-of-a-sorted-array/
暴力求解
- 先对数组的每个元素求平方
- 对数组升序排序
时间复杂度:O(n)+排序算法的时间复杂度
双指针
【思路】
数组本身就是非递减顺序的,只不过负数在平方之后可能会变大,因此只需要用两端来比较就能知道那个数字的平方更大
- 时间复杂度:O(n)
- 空间复杂度:O(n)
public static int[] sortedSquares(int[] nums) { int left = 0, right = nums.length - 1; // 用于存储排序之后的结果 int[] result = new int[nums.length]; for (int i = nums.length - 1; i >= 0; i--) { int leftValue = nums[left]; int rightValue = nums[right]; if (leftValue * leftValue >= rightValue * rightValue) { result[i] = leftValue * leftValue; left++; } else { result[i] = rightValue * rightValue; right--; } System.out.println(Arrays.toString(result)); } return result; }
*长度最小的子数组
题目链接
https://leetcode.cn/problems/minimum-size-subarray-sum/
暴力求解
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
注意,子数组是原数组中连续的几个元素的集合
public int minSubArrayLen(int target, int[] nums) { // 最小子数组长度 int minLen = nums.length + 1; for (int i = 0; i < nums.length; i++) { int sum = 0; for (int j = i; j < nums.length; j++) { sum += nums[j]; if (sum >= target) { // 已经>=target,可以暂停了 if ((j - i + 1) < minLen) { minLen = j - i + 1; break; } } } } return minLen == nums.length + 1 ? 0 : minLen; }
部分案例已经超出时间限制
滑动窗口
- 时间复杂度:O(n):不能以为for里放一个while就是O(n^2), 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)
- 空间复杂度:O(1)
/** * 滑动窗口 * * @param target * @param nums * @return */ public int minSubArrayLen1(int target, int[] nums) { // 最小子数组长度 int minLen = nums.length + 1; int i = 0; int sum = 0; for (int j = 0; j < nums.length; j++) { // j++,窗口终止位置后移一位,sum添加相应的元素 sum += nums[j]; while (sum >= target) { // 窗口内的元素总和已经超过target,尝试将窗口的起始位置后移,即i++ if ((j - i + 1) < minLen) { minLen = j - i + 1; } // sum移除窗口起始位置的元素值 sum -= nums[i++]; } } return minLen == nums.length + 1 ? 0 : minLen; }
螺旋矩阵
【思想】
这道题主要是考代码能力,注意每次循环都是左开右闭就行
【我写的程序】
public static int[][] generateMatrix1(int n) { int[][] result = new int[n][n]; int curNum = 1; int target = n * n; int initN = n; // 圈数 int cirCleNum = 0; while (curNum <= target) { if (curNum == target) { result[cirCleNum][cirCleNum] = curNum; System.out.println("---------------------填中心---------------------"); for (int i = 0; i < result.length; i++) { System.out.println(Arrays.toString(result[i])); } break; } // 填上边 for (int i = 0; i < n - 1; i++) { result[cirCleNum][i + cirCleNum] = curNum++; } System.out.println("---------------------填上边---------------------"); for (int i = 0; i < result.length; i++) { System.out.println(Arrays.toString(result[i])); } // 填右边 for (int i = 0; i < n - 1; i++) { result[i + cirCleNum][initN - 1 - cirCleNum] = curNum++; } System.out.println("---------------------填右边---------------------"); for (int i = 0; i < result.length; i++) { System.out.println(Arrays.toString(result[i])); } // 填下边 for (int i = n - 1; i > 0; i--) { result[initN - 1 - cirCleNum][i + cirCleNum] = curNum++; } System.out.println("---------------------填下边---------------------"); for (int i = 0; i < result.length; i++) { System.out.println(Arrays.toString(result[i])); } // 填左边 for (int i = n - 1; i > 0; i--) { result[i + cirCleNum][cirCleNum] = curNum++; } System.out.println("---------------------填左边---------------------"); for (int i = 0; i < result.length; i++) { System.out.println(Arrays.toString(result[i])); } System.out.println("======================================================================"); cirCleNum += 1; n -= 2; } return result; }
【别人的代码】
public int[][] generateMatrix(int n) { int loop = 0; // 控制循环次数 int[][] res = new int[n][n]; int start = 0; // 每次循环的开始点(start, start) int count = 1; // 定义填充数字 int i, j; while (loop++ < n / 2) { // 判断边界后,loop从1开始 // 模拟上侧从左到右 for (j = start; j < n - loop; j++) { res[start][j] = count++; } // 模拟右侧从上到下 for (i = start; i < n - loop; i++) { res[i][j] = count++; } // 模拟下侧从右到左 for (; j >= loop; j--) { res[i][j] = count++; } // 模拟左侧从下到上 for (; i >= loop; i--) { res[i][j] = count++; } start++; } if (n % 2 == 1) { res[start][start] = count; } return res; }