算法训练day1:二分查找、移除元素(双指针)

二分

二分查找是很经典的查找算法,用于在有序数列里面快速查找元素,时间复杂度为logn

时间复杂度解释

每次查找会将所有范围缩小一半,当查找k次之后如果搜索范围为1就得出结果

即n/2^k=1  

可以推出n=2^k,即k=log2 n

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1


示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。


 

二分查找注意事项:区间的定义

这里普遍是两种区间,第一种左闭右闭,第二种左闭右开

1、左闭右闭

即left、right所在的索引我们认为是参与target判断的,所以首先定义left和right的时候分别为0和nums.length-1,并且查找的终止条件是left==right,并且由于left和right下标的数组都参与了target判断,我们缩小区间的时候left=mid+1,right=mid-1,当跳出循环,就代表没有target存在。

代码如下:

class Solution {
    public int search(int[] nums, int target) {
        int left=0;int right=nums.length-1;
        while(left<=right){
            int mid=left-(left-right)/2;
            if(nums[mid]>target){
                right=mid-1;
            }
            else if(nums[mid]<target){
                left=mid+1;
            }
            else return mid;
        }
        return -1;
    }
}

2、左闭右开 

同理,左闭右开意味着left所在索引元素参与判断,right所在元素不参与判断,那么定义left=0,right=nums.length,并且定义循环终止条件为left<right即可,因为left==right是没有意义的。此外,当我们缩小区间的时候,因为left是闭,所以left=mid+1,right是开,所以right=mid,那么当最后循环终止,也就是left==right的时候,也同样没有找到元素,返回-1

代码如下:

class Solution {
    public int search(int[] nums, int target) {
        int left=0;int right=nums.length;
        while(left<right){
            int mid=left-(left-right)/2;
            if(nums[mid]>target){
                right=mid;
            }
            else if(nums[mid]<target){
                left=mid+1;
            }
            else return mid;
        }
        return -1;
    }
}

3、左开

那肯定有人疑惑为什么没有左开右闭或者左开右开

其实硬要写也是可以的,就是比较麻烦

说一下本菜鸟的思路

以左开右闭为例吧

首先:定义left=-1,right=nums.length-1,容易理解left==right是没有意义的,因为left不参与判断,如果写left<right,那么会有一种情况是left=right-1,此时mid必然等于left,因为mid=(left*2+1)/2,若target大于mid对应元素,那么会陷入死循环,left一直等于mid就出不来了。

所以这边,我想到的方法就是循环条件写成right-left>1,写成这个之后就有问题了,那就是最后left=right-1的情况被忽略了,就需要在循环外部手动再判断right对应的元素是否等于target。此外还有一种情况,就是target比所有元素都小,那么right最后会跑到-1去,会有数组越界的风险。

这是我随便写的代码,也顺利通过了,仅供参考

class Solution {
    public int search(int[] nums, int target) {
        int left=-1;int right=nums.length-1;
        while(right-left>1){
            int mid=left-(left-right)/2;
            if(nums[mid]>target){
                right=mid-1;
            }
            else if(nums[mid]<target){
                left=mid;
            }
            else return mid;
        }
        if(right<0) return -1;
        return nums[right]==target?right:-1;
    }
}

再贴一个左开右开

class Solution {
    public int search(int[] nums, int target) {
        int left=-1;int right=nums.length;
        while(right-left>1){
            int mid=left-(left-right)/2;
            if(nums[mid]>target){
                right=mid;
            }
            else if(nums[mid]<target){
                left=mid;
            }
            else return mid;
        }
        if(right-1<0) return -1;
        return nums[right-1]==target?right:-1;
    }
}

可以看到其实挺麻烦的,而且稍微不注意就要写错,很不好。

双指针

27. 移除元素

提示

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

  • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
  • 返回 k

这题没什么好说的,算是双指针,一个表示插入下标,一个用来遍历就行了

class Solution {
    public int removeElement(int[] nums, int val) {
        int p=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=val){
                nums[p++]=nums[i];
            }
        }
        return p;
    }
}

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

简单写法,主要就是遍历一遍平方掉,然后Arrays.sort直接排序,时间复杂度为nlogn(快速排序)

class Solution {
    public int[] sortedSquares(int[] nums) {
        for(int i=0;i<nums.length;i++){
            nums[i]*=nums[i];
        }
        Arrays.sort(nums);
        return nums;
    }
}

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

选择用双指针的方法来实现On的时间复杂度,其实有点像是三指针了

主要思路就是先平方,然后left right双指针从两边向中间遍历,i记录插入位置。

因为原本的数组本来就是非递减的,所以最大值只会出现在两端,所以直接从两端找最大值插入到i的位置。left==right的时候就插入完成了

代码如下:

class Solution {
    public int[] sortedSquares(int[] nums) {

        int n=nums.length;
        for(int i=0;i<n;i++) nums[i]*=nums[i];

        int left=0;
        int right=n-1;
        int [] ans=new int[n];

        int i=n-1;
        while(left<=right){
            if(nums[left]<nums[right]) ans[i--]=nums[right--];
            else ans[i--]=nums[left++];
        }

        return ans;
    }
}

新人第一篇博客,也是算法学习的开篇,其实之前粗浅刷过一次,这次系统的记录一下。如果有朋友愿意一起的话,可以关注我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值