1 二分法
注意:二分法的区间主要是左闭右闭和左闭右开
二分法使用前提
-
有序数组
-
数组中没有重复元素
题目
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1
思路:这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
1.1 易错点
对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
1.1.1左闭右闭[ ]
left = 0//left是数组的左边索引 right = numsize - 1 //right是数组的右边索引,因为数组的下标是从0开始的并且是闭区间 while(left <= right){ //例如:[1,1]是一个合法区间 middle = (left + right)/2 if(nums[middle] > target){ right = middle - 1;//nums[middle]已经大于target,所以right不包含middle else if(nums[middle] < target){ left = middle + 1 }else{ return middle; } } } return -1;
1.1.2 左闭右开[ )
left = 0//left是数组的左边索引 right = numsize //right是数组的右边索引,因为数组的下标是从0开始的并且是开区间 //例如{1,2,3,4,5} 索引是0~4 则区间有[0,4]或者[0,5) while(left < right){ //例如:[1,1)不是一个合法区间 middle = (left + right)/2 if(nums[middle] > target){ right = middle;//nums[middle]已经大于target,所以right不包含middle,因为左闭右开区间,所以区间本来就不包含middle else if(nums[middle] < target){ left = middle + 1 }else{ return middle; } } } return -1;
1.2 代码实现
-
自己写的代码
package com.itheima; public class Text { public static void main(String[] args) { int[] arr={1,2,3,4,5}; System.out.println(getNum(arr,6)); } public static int getNum(int[] nums, int target){ for (int i = 0; i < nums.length; i++) { int num = nums[i]; if(num == target){ return i; } } return -1; } }
没有用到二分法,直接遍历了整个数组
-
题解
class Solution { public int search(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) {//左闭右闭 int mid = (right - left) / 2 + left; int num = nums[mid]; if (num == target) { return mid; } else if (num > target) { right = mid - 1; } else { left = mid + 1; } } return -1; } }
class Solution { public int search(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left < right) {//左闭右开 int mid = (right - left) / 2 + left; int num = nums[mid]; if (num == target) { return mid; } else if (num > target) { right = mid; } else { left = mid + 1; } } return -1; } }
2. 移除元素
题目需求
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
数组基础知识
-
要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
解法
暴力解法
双循环:时间复杂度是n^2
public class Text { public static void main(String[] args) { int[] arr={1,2,3,4,5}; System.out.println(removeNum(arr,3)); } public static int removeNum(int[] nums, int value){ int length = nums.length; for (int i = 0; i < nums.length; i++) { if(nums[i] == value){ for (int i1 = i + 1; i1 < nums.length; i1++) { nums[i1-1] = nums[i1];//把后一位的位置赋值给前一位 } i--;//覆盖之后指标向前移动一位 length--;//覆盖之后数组长度也减少1 } } return length; } }
双指针思想
-
思路
-
定义快慢指针
-
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
-
慢指针:指向更新 新数组下标的位置
-
-
-
注意这些实现方法并没有改变元素的相对位置!
-
时间复杂度:O(n)
-
空间复杂度:O(1)
-
-
代码示例
public static int removeNum(int[] nums, int value){ int slowIndex = 0;//定义慢指针表示新数组的索引 for(int fastIndex = 0; fastIndex < nums.length; fastIndex++){ if(nums[fastIndex] != value){ nums[slowIndex] = nums[fastIndex];//如果快指针对应的元素值与value不相等,就拿出来赋值给慢指针对应索引的位置 slowIndex++; } } return slowIndex;//慢指针的大小就是新数组的长度 }