文章目录
- 引言
- 复习
- 新作
- 在排序数组中查找元素的第一个和最后一个位置
- 个人实现
- 参考实现
- 搜索旋转排序数组
- 个人实现
- 参考实现
- 总结
引言
复习
新作
在排序数组中查找元素的第一个和最后一个位置
题目链接
注意
- 非递减序列==》元素是递增或者相等,并不是严格递增的
- 找到给定目标值在数组中开始的位置和结束的位置
- 时间复杂度是logn
个人实现
- 这道题就是典型的二分查找的,但是需要知道你背的模板的特点的——“左加大,右减小”
class Solution {
public int[] searchRange(int[] nums, int tar) {
//define result vector
int[] res = {-1,-1};
if(nums.length == 0) return res;
// binary find
int m = nums.length;
int l = 0,r = m - 1;
while(l < r){
int mid = (l + r) >> 1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1;
}
// System.out.println(l);
// judge whether find the value
if(nums[l] != tar) return res;
// the value is the first bigger num
while(l >= 1 && nums[l - 1] == tar) l --;
while(r + 1 < m && nums[r + 1] == tar) r++;
res[0] = l;
res[1] = r;
return res;
}
}
- 写是写完了,这里得去看一下Java中数组的操作,这里整的有点不熟悉,导致很多操作和C++弄混了!
// create the fixed length array
int[] fixedArray = new int[5];
int[] ifxedArray = {1,2,3,4,5};
参考实现
- 这里是使用了两个不同的模板,一个确定左边界,还有一个是确定有边界的。具体两个模板如下
确定左边界
int l = 0,r = m-1;
while(l < r){
int mid = (r + l) >> 1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1;
}
// l is the left edge
确定右边界
int l = 0,r = m - 1;
while(l < r){
int mid = (l + r + 1) >> 1; // 仅仅针对右边界,防止死循环
if(nums[mid] <= tar) l = mid;
else r = mid - 1;
}
最终实现如下
class Solution {
public int[] searchRange(int[] nums, int tar) {
//define result vector
int[] res = {-1,-1};
if(nums.length == 0) return res;
// binary find
int m = nums.length;
int l = 0,r = m - 1;
while(l < r){
int mid = (l + r) >> 1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1;
}
// judge whether find the value
if(nums[l] != tar) return res;
res[0] = l;
// the value is the first bigger num
l = l ;
r = m - 1;
while(l < r){
int mid = (l + r + 1) >> 1;
System.out.println(mid);
if(nums[mid] <= tar) l = mid;
else r = mid - 1;
}
res[1] = r;
return res;
}
}
搜索旋转排序数组
- 题目链接
注意 - 数组严格单调递增
- 数组不为空
- 数组的值不会存在溢出的情况
个人实现
- 这道题是一个二分查找,但是需要转换坐标,将当前坐标转换为未旋转之前的坐标,或者是对其进行分段二分,然后查找目标值。
- 现在有一个问题,无论是哪种方法,都需要知道在哪里进行的反转,如果完整遍历一次对应元素,时间复杂度就是O(n),肯定超时,现在就是想着通过单调递增的方式,尽量快速确定反转的地方。
- 找到反转点
- 元素与元素 之间只有严格的大于或者小于关系,往后遍历就是一定大于,往前遍历一定小于,异常就是说明旋转点,就在这一段
- 找到target就是使用二分查找
class Solution {
public int binFind(int[] nums,int l,int r,int tar){
while(l < r){
int mid = (l + r) >> 1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1;
}
return l;
}
public int search(int[] nums, int tar) {
int m = nums.length ;
// handle the edge situation
if(m == 1) return nums[0] == tar?0:-1;
// find the reverse point
int len = m / 2;
int revP = 0;
while(len > 1){
if(nums[revP] < nums[revP + len - 1]){
revP += len;
}else{
len /= 2;
}
}
// find the target
int res;
System.out.println(revP);
if(tar > nums[0]){
res = binFind(nums,0,revP,tar);
}else{
System.out.println("last");
res = binFind(nums,revP + 1 ,m - 1,tar);
}
System.out.println(res);
// judge the result
if(nums[res] == tar) return res;
else return -1;
}
}
这里只能通过部分样例
- 因为第一个是总是能够算准的,但是第二个不好处理,不应该呀!第二个总是越界,出现各种各样的问题!
- 反转点在第一个元素的位置
- 反转点在最后一个元素的位置
- 仅有两个元素,如何进行判定
但是就是会出现异常!难受!
参考实现
- 这里整体的思路和我的一样,先找划分点,然后二分查找。
这里有一个很巧妙的地方,就是一开始根据条件确定旋转点,类似的二分都可以用同样的思路
- 基本想法和我的是一样的,都是通过等分对应区间长度,然后在进行比较划分,只不过他的套用模板,可靠性更高!
- 具体实现代码如下
class Solution {
public int search(int[] nums, int tar) {
int m = nums.length ;
// handle the edge situation
if(m == 1) return nums[0] == tar?0:-1;
// find the reverse point
int l = 0 ,r = m - 1;
while(l < r){
int mid = (l + r + 1) >> 1;
if(nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
// find the target
if(tar >= nums[0] ){
l= 0;
}else{
l = l + 1;
r = m - 1;
}
while(l < r){
int mid = (l + r) >> 1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1;
}
// judge the result
if(nums[r] == tar) return r;
else return -1;
}
}
对于模板的进一步总结
左加大返回右,右减小返回左
// 方案一,右边界
int l = 0,r = m - 1;
while(l < r){
int mid = (r+l)>>1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1;
}
return r;
// 方案二,左边界
int l = 0,r = m - 1;
while(l < r){
int mid = (r+l)>>1;
if(nums[mid] <= tar) l = mid;
else r = mid - 1;
}
return l;
总结
- 昨天是纯纯摆烂了,主要是最近的状态属实不是很好,学不进去,昨晚上看了一会电视,聊了会天,看了会书,然后十二点就睡了,早上起来挺早的,背了会书,现在开始刷算法,进度还行,调整一下,效果还是很好的!
- 投了字节的另外一个部门,然后居然还给我面试了,这周五,继续加油!