[LeetCode题解分享]最富有用户的资产总量+在排序数组中查找元素的第一个和最后一个位置+分割数组为连续子序列+x的平方根

Leetcode

[1672]. 最富有客户的资产总量

题目描述:

给你一个 m x n 的整数网格 accounts ,
其中 accounts[i][j] 是第 i 位客户在第 j 家银行托管的资产数量。
返回最富有客户所拥有的 资产总量 。
客户的 资产总量 就是他们在各家银行托管的资产数量之和。最富有客户就是 资产总量 最大的客户。

思路:

给一个数组接收每一行的元素之和,然后找出这个数组中的最大值,
这个最大值即最富有客户的资产总量。
能改进的地方就在于排序算法,所以我们选用最快的快排法。
这里强烈推荐Arrays工具类中的sort函数,底层采用的就是快排法,也很方便

代码实现:

class Solution {
    public int maximumWealth(int[][] accounts) {
        int[] nums = new int[accounts.length];
        for (int i = 0; i < accounts.length; i++) {
            for (int j = 0; j < accounts[i].length; j++) {
                nums[i] += accounts[i][j];
            }
        }
        Arrays.sort(nums);
        return nums[nums.length - 1];
    }
}

[34]. 在排序数组中查找元素的第一个和最后一个位置

题目描述:

给定一个按照升序排列的整数数组 nums,和一个目标值 target。
找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

思路1:

设置两个指针,左指针i,右指针j,分别从两头开始寻找target出现的位置。
如果是左指针找到了target,从左指针开始到j,寻找第一个不为target的元素;
如果是右指针找打了target,从右指针开始到i,寻找第一个不为target的元素

代码:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int len = nums.length;
        int[] ints = new int[]{-1,-1};
        if(len==0){
            return ints;
        }
        if(len==1&&nums[len-1]==target){
            return new int[]{0,0};
        }
        int i=0,j=len-1;
        boolean flag = true;
        while(i<=j&&flag){
            if(nums[i]!=target&&nums[j]!=target){
                //没有找到
                i++;
                j--;
            } else if(nums[i]==target){
                //左指针找到了target
                ints[0] = i;
                for (int k = i; k <= j; k++) {
                    if(nums[k]==target){
                        ints[1] = k;
                    }else{
                        ints[1] = k-1;
                        flag = false;
                        break;
                    }
                }
                flag = false;
            }else{
                //右指针找到了target
                ints[1] = j;
                for (int k = j; k >= i; k--) {
                    if(nums[k]==target){
                        ints[0] = k;
                    }else{
                        ints[0] = k+1;
                        flag = false;
                        break;
                    }
                }
                flag = false;
            }
        }
        return ints;
    }
}

思路2:

利用上题目给定的递增有序数组这个条件,先二分查找到target的索引值,
然后分别从左右开始试探,更改res数组的索引

代码:

class Solution {
    public static int[] searchRange(int[] nums, int target) {
        int[] res={-1,-1};
        //先二分查找,查找到该元素的索引,然后向左和向右试探,更改res索引
        int index = -1;
        int left = 0,right = nums.length-1,middle;
        while(left<=right){
            middle = (left+right)/2;
            if(nums[middle]>target){
                right = middle-1;
            }else if(nums[middle]<target){
                left = middle+1;
            }else{
                index = middle;
                break;
            }
        }
        if(index==-1){
            return res;
        }else{
            left = right = index;
            while(left>0){
                if(nums[left-1] == target){
                    left--;
                }else{
                    break;
                }
            }
            while(right<nums.length-1){
                if(nums[right+1] == target){
                    right++;
                }else{
                    break;
                }
            }
        }
        res[0] = left;
        res[1] = right;
        return res;
    }
}

[659. 分割数组为连续子序列]

题目描述:

给你一个按升序排序的整数数组 num(可能包含重复数字),
请你将它们分割成一个或多个子序列,其中每个子序列都由连续整数组成且长度至少为 3 。
如果可以完成上述分割,则返回 true ;否则,返回 false 。

思路:

这道题一开始想了很久没有想出来,也不是原创思路,借鉴了评论区dalao的思路。这类题被称为贪心算法。贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。也就是说,不从整体最优上加以考虑,做出的只是在某种意义上的局部最优解 。具体思路可以参考我的代码的注释

代码实现

public class Solution {
    /**
    假设数组一为[3,4,6,5]、数组二为[9,1,2,5,8,3]、k = 5;
    组合情况有0 + 5、1 + 4、2 + 3、3 + 2、4 + 1五种情况,就是从此五种情况取出组合最大的一种;
    Math.max(0, k - n)表示若数组二的元素个数 >= k,则数组一的元素个数可以从0开始取,否则在数组二的大小基础上补.
    */
    public int[] maxNumber(int[] nums1, int[] nums2, int k) {
        int m = nums1.length, n = nums2.length;
        int[] res = new int[k];
        for (int i = Math.max(0, k - n); i <= k && i <= m; i++) {
            int[] arr = merge(maxArr(nums1, i), maxArr(nums2, k - i), k);
            if (gt(arr, 0, res, 0)) res = arr;
        }
        return res;
    }

    /**
    假设选择了2 + 3的情况,分别从两个数组取出相应元素个数的最大组合,对数组一来说就是[6,5],对数组二来说是[9,8,3];
    n - i : 当前数组中,当前下标到结尾还有多少个元素;
    j : 当前数组中i之前有多少个数加入到最大组合中;
    n - i + j > k <=> n - i - 1 + j >= k : 当前下标的元素大于最大组合的末尾元素,就需要弹出,弹出后的元素减少,故j--,
    n - i(数组剩余元素) - 1(去掉最大组合末尾元素) + j(最大组合中剩余元素)时刻保持 >= k;
    if j < k : 先将最大组合填满再进行比较替换操作
     */
    private int[] maxArr(int[] nums, int k) {
        int n = nums.length;
        int[] res = new int[k];
        for (int i = 0, j = 0; i < n; i++) {
            while (n - i + j > k && j > 0 && nums[i] > res[j-1]) j--;
            if (j < k) res[j++] = nums[i];
        }
        return res;
    }

    /**
    假设数组一最大组合为[6,5],数组二最大组合为[9,8,3],进行双指针排序,排序后为[9,8,6,5,3]
     */
    private int[] merge(int[] nums1, int[] nums2, int k) {
        int[] res = new int[k];
        for (int i = 0, j = 0, r = 0; r < k; r++)
            res[r] = gt(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
        return res;
    }

    /**
    比较两数组相应位置大小,相等就一直跳过,直到不相等就比较.
     */
    private boolean gt(int[] nums1, int i, int[] nums2, int j) {
        while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {
            i++;
            j++;
        }
        return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);
    }
}

[69. x 的平方根]

题目描述:

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

思路:

由于x的平方根的整数部分res是满足k^2<=x的最大整数k值,因此我们可以对k进行二分查找,我们把上界设置为x,下界设置为0,每一步中,我们只需要比较mid与x的大小关系,但是这里为了防止数据溢出,我们做一步调整,把mid*mid<=x改成x/mid>=mid,这样可以防止数据的溢出

代码实现:

class Solution {
    public int mySqrt(int x) {
       if(x == 1) {
            return 1;
        }
        int min = 0;
        int max = x;
        while(max-min>1) {
            int mid = (max+min)/2;
            if(x/mid<mid){
                max = mid;
            } else {
                min = mid;
            }
        }
        return min;
    }
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页