算法入门:编程者的必修课

题目
选自力扣

删除有序数组中的重复项 II

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

示例 1:

输入:nums = [1,1,1,2,2,3] 输出:5, nums = [1,1,2,2,3] 解释:函数应返回新长度 length =5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3。 不需要考虑数组中超出新长度后面的元素

示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3] 输出:7, nums = [0,0,1,1,2,3,3] 解释:函数应返回新长度
length = 7, 并且原数组的前七个元素被修改为 0, 0, 1, 1, 2, 3, 3。不需要考虑数组中超出新长度后面的元素。

提示:

1 < = n u m s . l e n g t h < = 3 ∗ 1 0 4 1 <= nums.length <= 3 * 10^4 1<=nums.length<=3104
− 1 0 4 < = n u m s [ i ] < = 1 0 4 -10^4 <= nums[i] <= 10^4 104<=nums[i]<=104
nums 已按升序排列

根据题目要求,我们需要修改给定的有序数组 nums,使得每个元素最多只出现两次,并返回修改后数组的有效长度。我们可以使用双指针法来解决这个问题。

下面是实现这个功能的 Java 代码:

class Solution {
    public int removeDuplicates(int[] nums) {
        int n = nums.length;
        if (n <= 2) {
            return n;
        }
        
        int slow = 2; // slow 指针用来追踪下一个可以放置新元素的位置
        int fast = 2; // fast 指针用来遍历数组
        
        while (fast < n) {
            // 如果当前元素与 slow - 2 位置的元素不同,说明它不是重复元素
            if (nums[slow - 2] != nums[fast]) {
                nums[slow] = nums[fast];
                ++slow;
            }
            ++fast;
        }
        
        return slow;
    }
}

代码解析

  1. 初始化:

    • 如果数组长度小于等于 2,则直接返回长度。
    • 初始化两个指针 slowfast,都从索引 2 开始,因为我们允许每个元素出现两次。
  2. 循环:

    • 使用 while 循环遍历数组。
    • 如果 fast 指针所指向的元素与 slow - 2 所指向的元素不同,则说明 fast 指针所指向的元素不是重复元素,可以将其移动到 slow 指针所指向的位置,并将 slow 指针向前移动一位。
    • 不管 fast 指针所指向的元素是否被移动,都需要将 fast 指针向前移动一位,以便继续检查下一个元素。
  3. 返回结果:

    • 返回 slow 指针的位置,即修改后数组的有效长度。

这个方法满足题目要求的原地修改数组和 O(1) 的额外空间复杂度。


多数元素

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:nums = [3,2,3] 输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2] 输出:2

提示:
n = = n u m s . l e n g t h n == nums.length n==nums.length
1 < = n < = 5 ∗ 1 0 4 1 <= n <= 5 * 10^4 1<=n<=5104
− 1 0 9 < = n u m s [ i ] < = 1 0 9 -10^9 <= nums[i] <= 10^9 109<=nums[i]<=109

这个问题可以通过 Boyer-Moore 投票算法来解决,这是一种线性时间和常数空间复杂度的算法。该算法的基本思想是在遍历数组的过程中动态维护一个候选多数元素和一个计数器。每当遇到一个和当前候选多数元素相同的元素时,计数器加一;遇到不同的元素时,计数器减一。如果计数器变为零,则选择当前元素作为新的候选多数元素。

下面是使用 Boyer-Moore 投票算法解决这个问题的 Java 代码实现:

import java.util.Arrays;

public class MajorityElementFinder {

    public static int findMajorityElement(int[] nums) {
        int candidate = 0;
        int count = 0;

        // 第一步:找出候选的多数元素
        for (int num : nums) {
            if (count == 0) {
                candidate = num;
            }
            count += (num == candidate) ? 1 : -1;
        }

        // 第二步:验证候选元素是否真的是多数元素
        count = 0;
        for (int num : nums) {
            if (num == candidate) {
                count++;
            }
        }

        // 检查计数是否大于 n / 2
        if (count > nums.length / 2) {
            return candidate;
        } else {
            throw new IllegalArgumentException("No majority element found");
        }
    }

    public static void main(String[] args) {
        int[] nums1 = {3, 2, 3};
        int[] nums2 = {2, 2, 1, 1, 1, 2, 2};

        System.out.println("Majority element in [3, 2, 3]: " + findMajorityElement(nums1));
        System.out.println("Majority element in [2, 2, 1, 1, 1, 2, 2]: " + findMajorityElement(nums2));
    }
}

在这段代码中,我们首先遍历数组找到候选的多数元素,然后再次遍历数组来验证这个候选元素是否真的出现次数超过了数组长度的一半。如果验证通过,我们就找到了多数元素;如果没有通过验证,理论上不应该发生这种情况,因为题目已经保证了多数元素的存在。

这种方法的时间复杂度是 O(n),因为我们只遍历数组两次;空间复杂度是 O(1),因为我们只使用了固定数量的额外变量。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布说在见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值