二分查找
最基础的二分查找
public int search(int[] nums, int target) {
//解法二:主要还是想尝试循环不变量法则
//定义区间为[left,right),right此时没有意义
int left = 0;
int right = nums.length;
int middle;
int index = -1;
while(left < right){
//此时的right在每一次的循环都是没有意义的,这点你需要掌握get
middle = (right + left) / 2;
if(nums[middle] == target){
index = middle;
break;
}else if(nums[middle] < target) left = middle + 1;
else right = middle;
//何处体现了right是没有意义的,在二分法中,就相当于说right只想每一次遍历总长度之后的以一个单位。
}
return index;
}
// public int search(int[] nums, int target) {
// //解法一:主要还是想尝试循环不变量法则
// //定义区间为[left,right],right是有意义的
// int left = 0;
// int right = nums.length - 1;
// int middle;
// int index = -1;
// while(left <= right){
// middle = (left + right) / 2;
// if(nums[middle] == target){
// index = middle;
// break;
// }else if(nums[middle] > target) right = middle - 1;
// else left = middle + 1;
// }
// return index;
// }
什么是循环不变量,就是你的定义的区间,或者说你定义的规则每一次循环不发生改变,这个叫做循环不变量。不变的是区间!left <= right or left < right都是表象,关键是区间的定义
35 搜索插入位置
这道题就非常的经典
public int searchInsert(int[] nums, int target) {
int m = 0;
int n = nums.length-1;
while(m <= n){
int middle = (n+m)/2;
if(nums[middle] == target) return middle;
if(nums[middle] < target) m = middle + 1;
if(nums[middle] > target) n = middle - 1;
}
return m;
}
在这里主要考察了当二分查找发找不到的时候应该如何处理。当二分查找找不到的时候.targt>最终值 m + 1 反之 n - 1; you konw it?
34 在排序数组中查找元素的第一个和最后一个位置
这道题的思路就很简单。
用二分法查找到点,向前移动,向后移动,找到范围返回
public int[] searchRange(int[] nums, int target) {
int m = 0;
int n = nums.length - 1;
int index = -1;
while (m<=n){
int middle = (n + m) /2;
if (nums[middle] == target){
index = middle;
break;
}else if (nums[middle] < target) m = middle + 1;
else n = middle - 1;
}
if (index == -1) return new int[]{-1,-1};
int category = 1;
for (int i =index + 1;i<nums.length;i++){
if (nums[i] != nums[index]) break;
category++;
}
for (int i = index - 1;i>=0;i--){
if (nums[i] != nums[index]) break;
category++;
index--;
}
return new int[]{index,index+category-1};
}
27 移除元素
原本我的思路是,遇到 val的数据,就把整个数组往前移动。可这个时候无法处理 val val连续出现的情况,这个时候我无法了解应该怎么做。其实核心思路是再找一个指针去记录。
public int removeElement(int[] nums, int val) {
int left = 0;
for(int i = 0;i<nums.length;i++){
if (nums[i] != val)
nums[left++] = nums[i];
}
return left;
}
283 移动零
public void moveZeroes(int[] nums) {
int left = 0;
for (int i = 0;i < nums.length;i++){
if (nums[i] != 0)
nums[left++] =nums[i];
}
for (int i = left;i<nums.length;i++)
nums[i] = 0;
}
844 比较含退格的字符串
public boolean backspaceCompare(String s, String t) {
char[] fun = fun(s);
char[] fun1 = fun(t);
if (fun.length != fun1.length) return false;
for (int i =0;i<fun.length;i++){
if (fun1[i] != fun[i])
return false;
}
return true;
}
public char[] fun(String s){
char[] temp = new char[s.length()];
int left = 0;
for (int i =0;i<temp.length;i++){
if (s.charAt(i) == '#' && left<= 1)
left = 0;
else if (s.charAt(i) == '#')
left -= 1;
else
temp[left++] = s.charAt(i);
}
char[] res = new char[left];
for (int i = 0;i<left;i++)
res[i] = temp[i];
return res;
}
209 长度最小的子数组
public int minSubArrayLen(int target, int[] nums) {
int len = Integer.MAX_VALUE;
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 && (j-i) < len) {
len = j - i + 1;
break;
}
}
}
if (len == Integer.MAX_VALUE) return 0;
return len;
}
暴力求解,就是简单的两个for循环
另外一个数组的很巧妙的方法就是,滑动窗口的方法
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int sum = 0;
int length = Integer.MAX_VALUE;
for(int i = 0;i<nums.length;i++){
sum += nums[i];
while (sum >= target){
length = Math.min(length,i-left+1);
sum -= nums[left++];
}
}
return length == Integer.MAX_VALUE ? 0:length;
}
当新加入一个数,所带来的的问题一定是判断nums和target的大小问题。如果大了,就一个个减,并且记录长度。很巧妙的方法
59 螺旋矩阵 II
这道题没什么难度,纯粹比的就是一个对于代码的掌控能力
public int[][] generateMatrix(int n) {
int numMax = n * n;
int[][] res = new int[n][n];
boolean[][] visited = new boolean[n][n];
int i = 0;
int j = 0;
int num = 0;
int direction = 1;
while (num < numMax) {
if (direction == 1) {
visited[i][j] = true;
res[i][j++] = num+1;
num++;
if (j>=n || visited[i][j]){
direction = 2;
j--;
i++;
}
}else if (direction == 2){
visited[i][j] = true;
res[i++][j] = num + 1;
num++;
if (i >= n || visited[i][j]){
direction = 3;
i--;
j--;
}
}else if (direction == 3){
visited[i][j] = true;
res[i][j--] = num+1;
num++;
if (j < 0 || visited[i][j]){
direction = 4;
j++;
i--;
}
}else {
visited[i][j] = true;
res[i--][j] = num+1;
num++;
if (i<0 || visited[i][j]){
direction = 1;
i++;
j++;
}
}
}
return res;
}
最后总结一下数组的题目常用的手法
- 二分法
- 旋转循环就是考验对于代码的掌控力
- 双指针
- 滑动窗口