算法打卡

42 阅读4分钟

算法思路

2044. 统计按位或能得到最大值的子集数目思路:首先是求最大值,按位或的最大值就是所有nums里面所有的按位或就是最大值,然后求到最大值之后,使用dfs来回缩求子表中等于这个最大值的子表即可。dfs的使用是这样的一直往下走直到走到数组的最后一个数也就是i等于数组长度的时候判断这个时候子表中的按位或是否跟最大值相等,然后回溯是一个带着本身回溯一个是不带。回溯是会走完所有的子集的情况也就是相当于获取所有的子集的按位或值然后如果跟最大值相等就ans加一。

class Solution {
    public int countMaxOrSubsets(int[] nums) {
        int max = 0;
        //先求出当前这个位置的最大按位或的值
        for (int i = 0; i < nums.length; i++) {
            max = max | nums[i];
        }
        dfs(0, 0, nums, max);
        return ans;
    }

    private int ans = 0;

    private void dfs(int i, int subsetOr, int[] nums, int totalOr) {
        if (i == nums.length) {
            if (subsetOr == totalOr) {
                ans++;
            }
            return;
        }
        dfs(i + 1, subsetOr, nums, totalOr); // 不选 nums[i]
        dfs(i + 1, subsetOr | nums[i], nums, totalOr); // 选 nums[i]
    }
}

977. 有序数组的平方思路:想到了一个很简单的思路,哈哈哈哈直接排序即可。但灵神要复杂度O(n)。

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

正解来了上面是歪门邪道啊哈哈哈,因为题目给出来的是递增顺序排列的,所有只要用两个指针递减的增加最大的哪个值就可也了从右边放置然后一个一个往下放。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int left = 0, right = nums.length - 1;
        int[] ans = new int[nums.length];
        for (int i = nums.length - 1; i >= 0; i--) {
            int x = nums[left] * nums[left];
            int y = nums[right] * nums[right];
            if (x > y) {
                ans[i] = x;
                left++;
            } else {
                ans[i] = y;
                right--;
            }
        }
        return ans;
    }
}

658. 找到 K 个最接近的元素思路:先使用二分查找,找到目标数所在的位置,因为原数组就是已经排序过的,那么在获取到目标值的位置后,使用双指针,找到目标需要的窗口大小,如果左右两边跟目标值的差值是相同的那么扩展左边的窗口,最后把窗口里面的数据转成列表返回。

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        int left = 0, right = arr.length - 1;
        //先找到目标值的位置
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] < x) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        left = right;
        right = left + 1;
        // 扩展窗口找到k个最接近的元素
        while (k > 0) {
            if (left < 0) {
                right++;
            } else if (right >= arr.length) {
                left--;
            } else if (x - arr[left] <= arr[right] - x) {
                left--;
            } else {
                right++;
            }
            k--;
        }
        // 提取子数组(left+1到right-1)
        List<Integer> result = new ArrayList<>();
        for (int i = left + 1; i < right; i++) {
            result.add(arr[i]);
        }
        return result;
    }
}

1471. 数组中的 k 个最强值思路:跟上题差不多的一个思路,中位数的获取上一开始出了点问题,找到中位数的位置然后双指针往这个位置收缩,哪个减中位值更大就给数值加上这个数因为数组是不要求按顺序的只要加入目标答案就可以了。

class Solution {
    public int[] getStrongest(int[] arr, int k) {
        Arrays.sort(arr);
        int[] ans = new int[k];
        int left = 0, right = arr.length - 1;
        int m = arr[((arr.length - 1) / 2)];
        while (k > 0) {
            if (Math.abs(arr[right] - m) >= Math.abs(m - arr[left])) {
                ans[k - 1] = arr[right];
                right--;
            } else {
                ans[k - 1] = arr[left];
                left++;
            }
            k--;
        }
        return ans;
    }
}

167. 两数之和 II - 输入有序数组思路:很简单的一道双指针,没什么思路直接双指针套公式。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] ans = new int[2];
        int left = 0, right = numbers.length - 1;
        while (left <= right) {
            int sum = numbers[left] + numbers[right];
            if (sum == target) {
                ans[0] = left + 1;
                ans[1] = right + 1;
                break;
            } else if (sum > target) {
                right--;
            } else {
                left++;
            }
        }
        return ans;
    }
}

1577. 数的平方等于两数乘积的方法数思路:计算两种不同的三元组的和相加,然后是怎么取这个三元组的个数的呢使用双指针两次循环来求,然后需要注意会有重复出现的统计重复的个数,如果nums2[l] == nums2[r]说明这中间的所有数都是重复的,那么这个区间里面的所有数据都是可以满足的三元组,如果不相等就找前面有多少个相等的然后res加等这个相等的乘积也是满足条件的三元组。

class Solution {
    public int numTriplets(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        return countTriplets(nums1, nums2) + countTriplets(nums2, nums1);
    }

    // 计算类型 1 和 类型 2 的三元组数量
    private int countTriplets(int[] nums1, int[] nums2) {
        int res = 0;
        for (int i = 0; i < nums1.length; i++) {
            long target = (long) nums1[i] * nums1[i];
            int l = 0, r = nums2.length - 1;
            while (l < r) {
                long sum = (long) nums2[l] * nums2[r];
                if (sum < target) {
                    l++;
                } else if (sum > target) {
                    r--;
                } else {
                    // nums2[l] 和 nums2[r] 可能相同,处理重复元素
                    if (nums2[l] == nums2[r]) {
                        int count = r - l + 1;
                        res += (count * (count - 1)) / 2; // 组合数 C(n, 2)
                        break;
                    } else {
                        int lCount = 1, rCount = 1;
                        while (l + 1 < r && nums2[l] == nums2[l + 1]) {
                            lCount++;
                            l++;
                        }
                        while (r - 1 > l && nums2[r] == nums2[r - 1]) {
                            rCount++;
                            r--;
                        }
                        res += lCount * rCount;
                        l++;
                        r--;
                    }
                }
            }
        }
        return res;
    }
}