数组——704,27,977

今天一共学习三道数组习题,分别是LeetCode704.二分查找,LeetCode27.移除元素,LeetCode977.有序数组的平方。

704.二分查找

704.二分查找

梦开始的地方,我感觉每一个讲数据结构和算法的课程,第一个算法题都是这道二分查找。这道题单看题目,本是不难,可以利用遍历来找到是否存在,以及存在时的数组下标,但是遍历较为耗时,LeetCode会显示超时,所以我们需要像一个更高效的方法来完成查找。

在高中的数学课上其实就学过二分法求方程的根的范围,这道题就是利用类似的思想。题目规定数组是有序的,我们从数组的中间元素开始比较,如果目标值比中间元素大,就在后半段重复操作,反之则在前半段重复。

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

首先,在这个方法中,我们先定义查找的范围,刚开始是从数组下标0位置开始到数组最后一个元素结束。根据二分查找的思想,该范围在后续要被不断更新。在这个方法中,一般有一点需要注意。

循环条件

具体来说是循环条件中是否可以取到等号,我们知道,无论范围怎么更新,都要满足左边界小于右边界,但是这里左边界是否可以等于有边界呢?
这里要通过数组的下标取值来思考判断,这里题目没有明确说明,但是我们可以通过题目和示例分析得到,数组范围是一个左右全闭的闭区间。那么左边界=右边界这种情况就可以满足,因为数组范围左右全闭,比如[3,3],那么3就是可以取到的。反之则不行,比如[3,3),左边可以取到3,但右边不能取3,所以3取不到,所以左右不能相等。

27.移除元素

移除元素
第二道题是移除元素,我在自己做这道题时,选择的是使用集合进行存储,最后在存回数组中,较为复杂和冗余,我在自己写的时候,就感觉解得很难受,隐隐约约好像有一种思路,但是又抓不住。讲解中使用了双指针法,以前听过这种方法,但是一般都可以通过冗余的遍历解决,所以一直没有了解过。听完讲解,恍然大悟,原来我隐隐约约能想到类似的思路,原来双指针就是这样的,又一次觉得算法的神奇和有趣。

回归正题,所谓双指针,就是指在遍历对象的过程中,使用两个指针进行遍历,从而达到相应的目的。正常来说我们可以通过两层循环来完成这个题目,第一层循环来遍历数组,第二层循环用来把需要移除的元素的后续元素都向前移动一格,来达到删除目的。而使用双指针,就可以用一层循环来完成两层循环的任务。

	public static int ipRemoveElement(int[] nums,int val){
        
        int k=0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]!=val){
                nums[k]=nums[i];
                k++;
            }
        }
        return k;
    }

双指针的作用是将两个for循环用一次for循环来完成,用原来的nums数组即作为旧数组遍历,也作为新数组存储,i为快指针,遍历旧数组nums,k为慢指针,指向新数组的下标,用来覆盖旧数组的元素,起到存储的作用。
碰到和目标值不同的元素,则存入新数组下标j指向的位置,j向后移动一次,
碰到和目标值相同的元素,则不存入数组,如此必有i>=j,所以称为快慢指针,慢指针覆盖旧数组成为新的数组,一定在i遍历完的范围内,不会影响到旧数组的遍历。

最后,我自己的解法也放在这里,留作参考。

	public static int removeElement(int[] nums,int val){
        ArrayList<Integer> list=new ArrayList<>();
        int k=0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i]!=val){
                list.add(nums[i]);
                k++;
            }
        }
        for (int i = 0; i <k; i++) {
            nums[i]= list.get(i);
        }
        return k;
    }

977.有序数组的平方

数组平方
这道题第一眼看,非常简单,把数组中的每一个元素都平方即可,但是稍微仔细思考,再看一眼示例,就大概能明白难点:负数的平方也是正数,会改变顺序。
那么最简单的方法是,先平方,再排序,简单易懂。

	public static int[] sortedSquares(int[] nums){
        for (int i = 0; i < nums.length; i++) {
            nums[i]=nums[i]*nums[i];
        }
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums.length-1; j++) {
                if (nums[j]>nums[j+1]){
                    int temp=nums[j];
                    nums[j]=nums[j+1];
                    nums[j+1]=temp;
                }
            }
        }
        return nums;
    }

这里的代码时先平方存入新数组,然后我使用冒泡排序对新数组进行排序,也可以使用快排。
但是下方又进阶问题,该问题也可以使用双指针法。双指针法一般有快慢指针和对撞指针,** 27.移除元素** 使用了快慢指针,这道题可以使用对撞指针。对撞指针就是从两个相反方向进行遍历。这道题通过观察和思考,可以发现,平方后越小的值由越靠原数组中间位置的元素得到,越大的元素越靠两边。所以我们可以从数组头尾分别出发进行遍历,注意,这样遍历平方先得到较大的元素,所以可以存入新数组的尾部,向头部移动。
比较两个指针指向位置的元素平方后的大小,先存入较大的,然后指针向自己方向的下一位移动。这里注意,等于和大于小于中的任意一个都可以合并。如果相等,则会将两个元素全部存入,但合并之后,可以理解为,先存入一个元素,剩下一个相等的元素会留到下一轮循环存入。

	public static int[] ipSortedSquares(int[] nums){
        int i=0;
        int j=nums.length-1;
        int[] result=new int[nums.length];
        int k=nums.length-1;
        while(i<=j){
            int a=nums[i]*nums[i];
            int b=nums[j]*nums[j];
            if (a>b){
                result[k]=a;
                k--;
                i++;
            } else {
                result[k]=b;
                k--;
                j--;
            }
        }
        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值