寒假算法打卡第一天

学习目标:

第一章数组

  • 一704.二分查找
  • 一24.移除元素

学习内容:

704.二分查找
(1)左闭右闭
思路:二分查找也称折半查找,根据题意数组是升序排列的,因此可以通过优先查找中间值与目标数据进行比较,来判断目标数据具体在哪个更加精确的范围内,最终查到数据。由于左右部都是闭区间,因此在进行while循环一次次进行精确范围判断时可以存在left=right的情况。但也要注意:当middle(中间数据)比目标数据小的时候,left应变为middle+1,因为左部是闭区间,但是middle已经判断过不是目标数据了因此无需再次进行比较;当middle(中间数据)比目标数据大的时候,right应变为middle-1,因为右部是闭区间,但是middle已经判断过不是目标数据了因此无需再次进行比较。

 public static int select_01(int[] nums,int target){
        //左闭右闭
        int left = 0;
        int right = nums.length - 1;

        while(left <= right)//左右区间都是闭的因此相等有意义,例如数组num[1,1]
        {
            int middle = (left + right) / 2;
            if (nums[middle] < target){
                left = middle + 1;//满足上述if的判断条件就说明nums[middle]已经不是目标的值了并且还小于目标值,因此目标值target应该在更大的区间里面,所以要将left的值变为middle+1以便于下次进入数值更大的区间进行循环查询
                //例如:由[1,3,5,7,9,11],middle = 5/2 = 2,且num[2] = 5,变为[7,9,11]
            }
            else if(nums[middle] > target){
                right = middle - 1;//满足上述if的判断条件就说明nums[middle]已经不是目标的值了并且还大于目标值,因此目标值target应该在更小的区间里面,所以要将right的值变为middle-1以便于下次进入数值更小的区间进行循环查询
                //例如:由[1,3,5,7,9,11],middle = 5/2 = 2,且num[2] = 5,因此变为[1,3]
            }
            else{
                return middle;
            }
        }

        return -1;
    }

(2)左闭右开
思路:由于右边是开区间,因此需要注意在进行while循环时不能带有等号,因为这会导致数组逻辑错误,并且需要注意的是,当middle(中间数据)比目标数据小的时候,left应变为middle+1,因为左部仍是闭区间,但是middle已经判断过不是目标数据了因此无需再次进行比较;而当middle比目标数据大的时候,right可以直接等于middle,因为右部是开区间,本身就不回包括在内,这就是跟左闭右闭最大的不同之处。

 public static int select_02(int[] nums,int target){
        //左闭右开
        int left = 0;
        int right = nums.length;

        while(left < right){//若数组为[1,1),左部1表示包含1,右部1表示不包含1,则发生冲突,因此不能写left = right,不会存在这种情况
            int middle = (left+right) / 2;
            if(nums[middle] < target){
                left = middle + 1;//由于是左闭,因此不能包含middle,因此left = middle + 1
            }
            else if(nums[middle] > target){
                right =middle;//由于是右开,因此就算right = middle也不会包含middle,因此right = middle
            }
            else{
                return middle;
            }
        }

        return -1;
    }

24.移除元素
(1)暴力解法
思路:每找到一个需要删除的数据,就将其后面的数据整体向前移动一个下标,用以覆盖原始数据从而达到删除效果,并且还好将数据的长度直接减一,简洁且暴力的操作能完成对目标元素的删除。

 public static int delete_01(int[] nums,int val){
        //暴力法
        int length = nums.length;
        for (int i = 0; i < length; i++) {//遍历数组
            if(nums[i] == val){//找到需要移除的数组
                for (int j = i + 1 ; j < length; j++) {
                    nums[j - 1] = nums[j];//将该数字后面的所有数据都向前移动一个下标用以覆盖要删除的数字
                }
                i--;//因为i后面的数值都向前亿了一位,因此i也要向前移一位才能判断本该在下一次判断数字是否该删除,不然会跳过一位数导致没有进行判断删除
                length--;//数组长度减一
            }
        }
        return length;
    }

(2)双指针法(快慢指针法)
思路:虽然不是真的造了一个新的数组出来存放数据但思路差不多,通过设计一个fast和slow指针下标,fast用来搜索不需要被删除的元素,每搜索到一个则slow自增一以增加数组长度,而slow用来表示不需要被删除元素在“新“ 的数组中的具体下标,而这个”新“的数组还是原来的数组,只不过是通过覆盖原来数组中对应下标的数据来实现的,并且数组长度也变成了slow。总的来说数组存储数据的具体位置不变,但内部存储的数据改变了,通过只存储不用被移除的元素从而变相达到删除元素的目的。

  public static int delete_02(int[] nums,int val){
        //快慢指针法
        int length = nums.length;
        int slow = 0;//慢指针用来表示不用删除数据保留在数组中的下标
        for (int fast = 0; fast < length; fast++) {//快指针用来检索出不用删除的数据有哪些
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }

(3)相向双指针法
思路:设置两个指针下标left和right,left表示从0开始的数组下标,用来存放不用被移除的元素,跟上述的双指针法一致;right表示原数组中最后一个元素。具体逻辑是:将原数组最左边的元素与目标元素进行比较,若相同则将right下标对应的数据赋值给当前下标以完成覆盖操作,也就是变相的删除,同时将right自减1移向前一个下标获取前一个数据;若不同则left自增1,表示下一个元素进行对比,一直到循环结束。由于每次都是最右部元素来覆盖数据,而检索又是从左往右的,因此正好是相向而行的两个指针。需要注意的是,该算法可能导致最终得到的数组中的元素的相对位置与原数组是不同的。

 public static int delete_03(int[] nums,int val){
        int left = 0;//确定要保留数字在新数组中的下标
        int right = nums.length - 1;//数组最后一个数据下标
        while(left <= right){
            if(nums[left] == val){
                nums[left] = nums[right];//可能导致数据之间相对顺序发生改变,例如[1,3,5,7,9]要删除5时,该算法得出的结果应该是[1,3,9,7],而不是[1,3,7,9]
                right--;
            }else {
                // 这里兼容了right指针指向的值与val相等的情况
                left++;
            }
        }
        return left;
    }

学习时间:

早上两个小时,下午一个半小时。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值