找了很多的刷题网站,也看过很多的课,包括之前的牛客,左神的课,老韩的课,都没能坚持下来。
算法这东西,正反馈确实少,忘的确实快。
之前牛客每日一题也没坚持下来,确实是惰性太严重了。
从今天开始,正式一天三道题,刷不完不睡觉。
PS:连这做不到就别想着进大厂了。
代码随想录个人觉得还是挺不错的一个网站,从容易的题开始做也不容易被劝退。
一. 二分查找
题目链接:力扣
思路
这是很基础的一个题了,大伙儿多少应该都会写。当然也不排除有像我一样的算法小白,还是简单地说一下。
这类题都有两个先决条件,必须满足。
1. 有序
2. 数组中无重复元素
有同学会问了,那要不满足呢?
不满足就不能用二分去做,再想想别的招儿。
思路是很清晰的:取中值,比大小。
比中值大就说明在小的区间里,比中值小就说明在大的区间里。
不断地重复该过程,直到元素拥有原子性,不可再次拆分。
实话说,很简单。但大家可能就经常性地像我一样,一看就会,一做就废。因为它虽然思路简单,但循环边界的判断却是一个难点。
这里按照不同的题型,分为两种情况。
1. 左闭右闭 [ left, right ]
2. 左闭右开 [ left, right )
先说左闭右闭的情况。
- while(left <= right)的判断条件应该为 <= ,因为这种情况下 left == right是有意义的。
- if(nums[mid] > target) { right = mid - 1} ,因为当前 nums[mid] 的值一定不是 target,那么接下来要寻找的左区间的结束下标就是 mid - 1。
上代码。
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int rigth = nums.length-1;
while(left <= right){
//计算中值,这样计算的中值好处就是不会出现负坐标的情况
int mid = left + (right - left) / 2;
if(nums[mid] == target){
return mid;
} else if(nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
再说左闭右开的情况。
- while(left < right)的判断条件应该为 < ,因为在 [ left,right ) 的这种情况下 left == right变得没有意义。
- if(nums[mid] > target) {right = mid},此处right应直接赋值为 mid ,因为当前nums[mid]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[mid]。
上代码。
class Solution {
public int search(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){
right = mid;
} else {
left = mid+1;
}
}
return -1;
}
}
二.搜索插入位置
题目链接:力扣
这道题其实就是上面二分查找的应用,我们学会一道题之后,不但要学会解它本身,还要学会把它用起来,玩起来,解决类型题。
思路:
如果该题目暴力解决的话需要O(n) 的时间复杂度,但是如果二分的话则可以降低到 O(logn) 的时间复杂度。
整体思路和普通的二分查找几乎没有区别,先设定左侧下标 left 和右侧下标 right,再计算中间下标 mid
每次根据 nums[mid] 和 target 之间的大小进行判断,相等则直接返回下标,nums[mid] < target 则 left 右移,nums[mid] > target 则 right 左移
查找结束如果没有相等值则返回 left,该值为插入位置。
还是和普通二分一样,分两种情况做。
1.左闭右闭
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){
right = mid - 1;
} else {
left = mid + 1;
}
}
//若找不到,返回它将会被顺序插入的位置(下标)
return left;
}
}
2.左闭右开
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){
right = mid - 1;
} else {
left = mid + 1;
}
}
//若找不到,返回它将会被顺序插入的位置(下标)
return left;
}
}
三.在排序数组中查找元素的第一个和最后一个位置
题目链接:力扣
思路:
1. 先使用二分法,判断数组中是否存在target
2. 分别通过滑动左、右边界的方式去求target的范围(注意不能越界)
class Solution {
public int[] searchRange(int[] nums, int target) {
int index = binarySearch(nums, target);
if(index == -1){
return new int[]{-1, -1};
}
//target存在,滑动边界去寻找区间
int left = index;
int right = index;
//向左滑动,寻找左边界
while(left - 1 >= 0 && nums[left - 1] == nums[index]){
//防止数组越界,逻辑短路 条件顺序不能更改
left--;
}
//向右滑动,寻找右边界
while(right + 1 < nums.length && nums[right + 1] == nums[index]){
right++;
}
return new int[]{left, right};
}
public int binarySearch(int[] nums, int target){
int left = 0;
int right = nums.length-1;
//先使用二分查找确认target存不存在
while(left <= right){
int mid = left + (right-left) / 2;
if(nums[mid] == target){
return mid;
} else if(nums[mid] > target){
right = mid - 1;
} else {
left = mid + 1;
}
}
//target不存在
return -1;
}
}
第一天顺利结束,我一定,一定要坚持下去。