Day 01|704. 二分查找 & 27.移除元素

无意间看到训练营的活动,感觉群里高手云集,我这个小趴菜瑟瑟发抖。对于刷题我总是没有自主性,每次遇到问题卡了一次就停下好几天。加入训练营不求别的,希望自己能坚持下去,并做好记录!第一天的内容不多,是自己之前学过的,但是再做还是卡了很久。

704. 二分查找

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

示例:

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

这道题以前数据结构课上学过,不过并没有亲身实践,自己做了一遍后也是对该算法的原理有了更深刻的理解。

1.暴力算法

 public int search(int[] nums, int target) {
        for(int i=0;i<nums.length;i++){
            if(nums[i]==target){
                return i;
            }
        }
        return -1;
    }

遍历每个数组元素依次与目标值进行判断,若相等则返回下标值即可。

2.二分查找

二分查找的前提:数组有序且无重复,本题为二分查找法的经典入门应用。

2.1 左闭右闭区间

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

对于左右都是闭区间的情况,首先left和right值可以相等取同一个值。

当nums[mid]值>target时,则表明mid代表的元素过大,应减小right指针的值。且由于mid!=target && right为闭区间,因此设置right值为mid-1。

反之,当nums[mid]值<target,设置left=mid+1;

重复上述操作,直至nums[mid]==target,返回mid值。

2.2 左闭右开区间

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

对于右开区间,因为取不到下标为right的值,因此right初值应为nums.length(相当于下标为length-1)。

并且由于是左开右闭区间,因此left与right值无法相等。例如若left=right=1,[1,1)不是一个正确的区间。

当nums[mid]值>target时,由于mid!=target && right为开区间,因此设置right值为mid(相当于下标为mid-1)。

27.移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]

1.暴力解法

这道题我本来想的是记录相等元素的个数然后返回length-count值,发现实现起来十分麻烦(也可能是我没写出最优解)。后来看了题解发现直接定义size变量,当出现时直接size--,返回size即可

 public static int removeElement(int[] nums, int val) {
        int len = nums.length;
        if (len == 1 && nums[0] == val) {
            return 0;
        }
        if (len == 1 && nums[0] != val) {
            return 1;
        }
        int count = 0;
        int j = len - 1;
        for (int i = 0; i < len ; i++) {
            if(i>j){break;}
            if(i==j){
                if(nums[i]==val){
                    count++;
                    break;
                }
            }

            if (nums[i] == val) {
                count++;

                while (nums[j] == val&&i!=j) {
                    j--;
                    count++;
                }
                if(count==len){return 0;}
                int t;
                t = nums[i];
                nums[i] = nums[j];
                nums[j] = t;
                j--;

            }

        }
        return len - count;
    }

双层for循环,第一层循环遍历数组,若该下标对应元素==target,则在第二层for循环中对该元素进行覆盖(nums[j]=nums[j+1])。

public static int removeElement(int[] nums, int val) {
        int size = nums.length;
        for (int i = 0; i < size; i++) {
            if (nums[i] == val) {
                for (int j = i; j < nums.length-1; j++) {
                    nums[j] = nums[j+1];
                }
                i--;  //重要!当前i被覆盖为nums[i+1]的值,
//但下次循环后i++相当于对nums[i+2]进行判断,并没有判断nums[i+1]的值。
                size--;
            }
        }
        return size;
}

2.快慢指针

这个一开始并没有想到。感觉很多题都可以用双指针解决,自己还需多多磨练。

fast指针遍历整个数组,slow指针代表当前新数组(未包含目标元素)的下标。当nums[fast]!=val时,快慢指针同时加一,当nums[fast]==val,则fast+1,slow不动。最后返回slow指针即为新数组的元素个数。

    public static int removeElement(int[] nums, int val) {
        int left=0;
        int right=0;
        for(;right<nums.length;right++){
            if(nums[right]!=val){
                nums[left]=nums[right];
                left++;
            }
        }
        return left;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值