BM52-数组中只出现一次的两个数字

文章介绍了两种方法解决找出数组中仅出现一次的两个数字的问题。第一种方法使用哈希表统计每个数字的出现频率,然后筛选出频率为1的数字。第二种方法利用异或运算的特性,将数组分为两部分,使得两个目标数字分别在不同部分,再进行异或运算分离这两个数字。两种方法的时间复杂度均为O(n),空间复杂度分别为O(n)和O(1)。

题目

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

数据范围:数组长度 2≤n≤1000,数组中每个数的大小 0<val≤1000000。
要求:空间复杂度 O(1),时间复杂度 O(n)。

提示:输出时按非降序排列。

示例1

输入:[1,4,1,6]

返回值:[4,6]

说明:返回的结果中较小的数排在前面。

示例2

输入:[1,2,3,3,2,9]

返回值:[1,9]


思路1:哈希表

既然有两个数字只出现了一次,就统计每个数字的出现次数,利用哈希表的快速根据key值访问其频率值。

具体做法:

  • step 1:遍历数组,用哈希表统计每个数字出现的频率。
  • step 2:然后再遍历一次数组,对比哈希表,找到出现频率为1的两个数字。
  • step 3:最后整理次序输出。

代码1

import java.util.*;

public class Solution {
    public int[] FindNumsAppearOnce (int[] array) {
        HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>();
        ArrayList<Integer> res = new ArrayList<Integer>();

        //遍历数组
        for (int i = 0; i < array.length; i++) {
            //统计每个数出现的频率
            if (!mp.containsKey(array[i])) {
                mp.put(array[i], 1);
            } else {
                mp.put(array[i], mp.get(array[i]) + 1);
            }
        }
            
        //再次遍历数组
        for (int i = 0; i < array.length; i++) {
            //找到频率为1的两个数
            if (mp.get(array[i]) == 1) {
                res.add(array[i]);
            }
        }

        //整理次序
        if (res.get(0) < res.get(1)) {
            return new int[] {res.get(0), res.get(1)};
        } else {
            return new int[] {res.get(1), res.get(0)};
        }
    }
}
  • 时间复杂度:O(n),其中n为数组长度,两次单独的遍历数组每个元素。
  • 空间复杂度:O(n),哈希表的长度应该为(n−2)/2。

思路2:异或运算

异或运算满足交换率,且相同的数字作异或会被抵消掉,比如:a⊕b⊕c⊕b⊕c=a,且任何数字与0异或还是原数字,放到这个题目里面所有数字异或运算就会得到a⊕b,也即得到了两个只出现一次的数字的异或和。

但我们是要将其分开得到结果的,可以考虑将数组分成两部分,一部分为a⊕d⊕c⊕d⊕c=a,另一部分为b⊕x⊕y⊕x⊕y=b的样式,怎么划分才能让a与b完全分开,而另外的也能刚好成对在一个组呢?这是需要考虑的问题。

a⊕b的结果中如果二进制第一位是1,则说明a与b的第一位二进制不相同,否则则是相同的,从结果二进制的最高位开始遍历,总能找到二进制位为1的情况。

因为两个数字不相同,我们就以这一位是否为1来划分上述的两个数组,相同的数字自然会被划分到另一边,而a与b也会刚好被分开。

具体做法:

  • step 1:遍历整个数组,将每个元素逐个异或运算,得到a⊕b。
  • step 2:可以考虑位运算的性质,找到二进制中第一个不相同的位,将所有数组划分成两组。
  • step 3:遍历数组对分开的数组单独作异或连算。
  • step 4:最后整理次序输出。

代码2

import java.util.*;

public class Solution {
    public int[] FindNumsAppearOnce (int[] array) {
        int res1 = 0;
        int res2 = 0;
        int temp = 0;

        //遍历数组得到a^b
        for (int i = 0; i < array.length; i++) {
            temp ^= array[i];
        }
        int k = 1;

        //找到两个数不相同的第一位
        while ((k & temp) == 0) {
            k <<= 1;
        }
        for (int i = 0; i < array.length; i++) {
            //遍历数组,对每个数分类
            if ((k & array[i]) == 0) {
                res1 ^= array[i];
            } else {
                res2 ^= array[i];
            }
        }

        //整理次序
        if (res1 < res2) {
            return new int[] {res1, res2};
        } else {
            return new int[] {res2, res1};
        }
    }
}

<think> 首先,用户的问题是基于之前的引用和对话。回顾用户提供的引用: - 引用[1] 讨论了在有序数组中找到第K小元素的优化算法,利用中位数的概念。 - 引用[2] 涉及滑动窗口算法中,使用一个need字典来跟踪元素的需求量。 - 引用[3] 是关于滑雪问题的动态规划,求解最长递减路径。 用户当前的问题:给定长度为n的数组a和长度为m的数组b,可以通过减少b数组中的数字或向b中添加新数字,使得a数组的每个元素都能在b数组中找到唯一对应的元素(即一一对应),目标是求最少添加数字的次数。 关键点: - 我们可以减少b中的数字:意思是我们可以改变b中元素的值为更小的值,但能增加它们的值。 - 或者添加新数字到b中。 - 最终,a中的每个元素必须匹配b中的一个元素,且匹配是唯一的(一一对应)。 - 我们需要最小化添加新数字的次数。减少b中的值增加添加次数,但添加新数字会。 用户提到“减小b数组数字或添加新数字”,所以操作有两种:减少b中现有元素的值,或添加新元素到b。 目标是使a的每个元素在b中有一个对应的元素,匹配条件是值相等?或者只是匹配?用户说“a数组的每个数字都能在b数组里找到对应数字”,并且“b数组元素只能和a数组一个元素对应”,所以是双射:每个a元素匹配一个b元素,反之每个b元素匹配一个a元素?,用户说“b数组元素只能和a数组一个元素对应”,但是所有b元素必须被使用;只要求a的每个元素有一个b元素对应,且b元素只能被一个a元素占用。所以,可能b中有多余元素未被使用。 核心是:我们需要一个映射f: a -> b,使得f是单射(每个a元素映射到唯一的b元素),但我们还需要匹配值相等?用户没有指定匹配条件是否基于值。 用户说:“a数组的每个数字都能在b数组里找到对应数字”,意思是值相等?等等,再读:“使a数组的每个数字都能在b数组里找到对应数字”,并且“对应”可能意味着值相等,因为数字是具体的值。 此外,通过减小b的数字或添加新数字,我们是在修改b。 所以,匹配时,我们关心值是否相等?用户没有明说,但上下文是数字数组,所以应该需要值匹配。 假设:匹配要求值相等。也就是说,对于每个a[i],必须存在某个b[j] 使得 b[j] == a[i],并且每个b元素最多匹配一个a元素。 但我们可以在匹配前修改b:减少b中现有元素的值,或添加新元素。 减少值:这意味着我们可以将某个b[k]减少到任意更小的值?或者减少到特定值?用户说“减小b数组数字”,但没有指定减少到多少。可能可以减小到任意值,只要小于原值。 类似地,添加新数字:我们可以添加任意整数?或者特定值?用户没有指定,但既然a中是数字,可能我们可以添加任意数字。 目标是匹配a的所有元素,最小化添加新元素的次数。减少现有元素的值增加添加次数,所以我们应尽可能通过减少b的值来匹配,而是添加新元素。 问题:减少b的值可能使它匹配多个a元素?,因为当我们减少一个b元素的值时,它只能匹配一个a元素,因为匹配是一一对应的。 例如,假设a有元素x,b有一个元素y > x。我们可以减少y到x,然后匹配。 或者,如果b没有元素可以减小到x,我们需要添加一个新元素x。 但b可能有多个元素可以减小来匹配同一个a元素,但由于匹配是一一对应,我们应该只用一个来匹配。 策略:为了最小化添加,我们应最大化通过减少现有b元素来匹配a元素的数量。 这类似于一个匹配问题。 定义: 设a = [a1, a2, ..., an],b = [b1, b2, ..., bm] 我们可以: - 减少一些b_i的值到某个更小值,记为新值b'_i <= b_i(假设可以减小到任意值) - 添加一些新元素到b中,记新元素值为任意整数。 最终,得到一个修改后的b'数组,大小为m + k,其中k是添加的元素数。 然后,我们需要存在一个单射f: a -> b',使得对于每个a元素,f(a)在b'中,并且值相等?,用户说“找到对应数字”,可能值相等。 用户原文:“使a数组的每个数字都能在b数组里找到对应数字” – 我认为“对应数字”意味着值相等的数字。 所以,在修改后的b'中,对于每个a[i],必须存在某个元素在b'中等于a[i]。 并且每个b'元素最多被一个a元素使用(一一对应)。 由于我们添加了新元素,b'中有元素来自原b(可能被减少)和新添加的元素。 但我们关心的是最小化添加的次数,即k。 减少现有元素的值计数为添加。 现在,问题是如何选择减少哪些b元素和减少到多少值来匹配a元素。 由于我们可以减少到任意值,为了匹配一个a元素x,我们可以: - 如果b中有某个元素y >= x,我们可以减少y到x,然后匹配。但y可能原本大于x,减少后可以匹配x。 - 或者,如果没有这样的y,我们添加一个新元素x。 但注意,当我们减少y到x时,如果x在a中只出现一次,那很好,但如果a中有重复元素呢? 用户没有指定a或b中是否有重复元素。数组可能有重复。 a可能有重复,b也可能有重复。 但匹配时,由于是一一对应,每个a元素需要一个匹配,所以如果有重复值,我们需要多个匹配项。 例如,如果a有两个元素都等于5,那么我们需要b'中有两个元素等于5。 所以,我们需要考虑频率。 问题本质上是:我们需要确保修改后b'中每个值v的频率至少等于a中v的频率?一定,因为匹配是基于值相等,但我们可以减少b的元素到同的值。 定义频率。 让我定义:对于每个值,我们需要在b'中有足够的元素匹配a的需求。 但b'由原b修改而来:一些元素被减少了值,一些新元素添加。 当我们减少一个b元素,我们可以设置为任意值小于等于原值。 所以,为了匹配a,我们需要对于每个值v在a中出现的次数c_a(v),在b'中,值v的元素数量必须至少c_a(v)。 因为每个匹配要求值相等。 在b'中,元素来源: - 原b元素,可能被减少到某个值 <= 原值 - 新添加的元素,设为任意值。 对于值v,b'中值为v的元素可以来自: - 原b中某个元素y,被减少到v,其中y >= v(因为减少到更小值) - 或者新添加的元素设为v。 每个原b元素可以被减少到任意值 <= 其原值,或者保持变(如果减少)。 但如果减少,它就保持原值。 现在,目标是最小化添加新元素的数量k。 等价于最大化通过减少现有b元素来满足的a元素匹配数。 对于每个原b元素,它可以通过减少到某个值,来匹配a中的一个元素,前提是减少后的值在a中存在。 但由于我们可以减少到任意值,匹配策略是:我们选择将一些b元素减少到a中出现的某个值,从而满足a的一个需求。 具体地,对于一个b元素y,它可以被减少来匹配任意a元素x,只要x <= y,因为我们可以减少y到x。 然后,匹配(x,y)成立,我们使用y来满足x。 但每个y只能匹配一个x(因为一一对应),每个x只能被一个y匹配。 所以,问题可以建模为一个二分图匹配。 定义二分图:左侧是a的元素,右侧是b的元素。 有一条边从a[i] 到 b[j] 如果 a[i] <= b[j],因为我们可以减少b[j]到a[i]来匹配。 然后,我们需要找到一个最大匹配,其中覆盖尽可能多的a元素。 因为每个匹配示我们使用一个b元素(通过可能减少)来满足一个a元素。 如果某个a元素没有在左侧匹配,那么我们需要添加一个新元素来匹配它。 在二分图中,边存在当a[i] <= b[j],示b[j]可以被减少到a[i]。 最大匹配的大小示我们最多可以满足多少个a元素而添加新元素。 然后,未匹配的a元素数量就是我们需要添加新元素的次数。 设匹配大小为M,则未匹配a元素数为 n - M,因此最少添加次数为 n - M。 因为每个未匹配的a元素都需要一个新添加的元素来匹配它(设为该值)。 在匹配中,M是满足的a元素数,因此添加次数为 n - M。 但我们还需要考虑b元素是否被使用。 在二分图匹配中,每个匹配边一个b[j]被用来匹配一个a[i],通过可能减少值。 对于未使用的b元素,我们需要做任何事;它们可以被忽略或减少但未用于匹配。 在最终b'中,未使用的b元素可以被保留或移除?用户没有说,但由于匹配只要求覆盖a,b'中多余元素无关紧要,只要a被覆盖。 所以,在最小化添加时,我们只关心覆盖a。 因此,问题归结为:在a和b数组上,找到最大匹配,其中a[i]可以匹配b[j]如果a[i] <= b[j],意思是b[j]可以被减少到a[i]。 然后,最大匹配大小M,最少添加次数为 n - M。 这正确吗? 考虑数组可能有重复元素的情况。 在二分图中,a和b的元素是实例,是值。所以如果有重复值,a[i]和a[k]如果值相同,但i≠k,是两个同的元素。 同样b[j]和b[l]如果值相同,是两个元素。 边:如果a[i] <= b[j],则从a[i]到b[j]有边。 然后最大匹配给出最大可覆盖a元素数。 未覆盖的a元素需要添加新元素。 是的。 但匹配的条件是a[i] <= b[j],因为b[j]只能减少,能增加,所以只有当a[i] <= b[j]时,b[j]才能被减少到a[i]。 如果a[i] > b[j],我们能增加b[j],只能减少或添加新元素,所以能匹配。 因此,边只当 a[i] <= b[j]。 现在,用户提到“减小b数组数字或向b数组中增加新数字”,但我们没有使用减少值的操作,除非在匹配中;在匹配中,当我们匹配a[i]和b[j]时,我们隐含了减少b[j]到a[i]。 对于未匹配的b[j],我们需要改变它们,但既然它们被使用,我们可以忽略。 添加新元素用于未匹配的a[i]。 在最少添加次数计算中,只取决于n和最大匹配大小。 但我们需要实际计算最大匹配。 由于数组可能无序,我们需要找到最大匹配。 最大匹配在二分图中可以通过标准算法找到。 但我们可以更高效。 首先,排序两个数组。 排序a和b。 然后,使用贪心策略找最大匹配。 由于边基于比较,排序后贪心可能工作。 例如,排序b升序。 排序a升序。 然后,尝试为每个a[i]找一个b[j] 使得 a[i] <= b[j],并且j尽可能小,等等。 类似贪心匹配。 由于匹配只要求a[i] <= b[j],我们可以找到一个最大基数匹配。 在排序后,贪心应该找到最大匹配。 算法: 1. 将a和b排序。 2. 初始化两个指针,i=0 对于a,j=0 对于b。 3. 遍历a和b: - 如果 a[i] <= b[j],则匹配a[i]和b[j],i和j都递增。 - 否则,如果 a[i] > b[j],则匹配,j递增尝试下一个b元素。 但等等,对。 由于b[j]可能用于匹配更大的a,但在这个设置中,因为a和b都排序了,如果a[i] > b[j],那么由于b排序,b[j]太小,能匹配当前或未来的a元素,因为a[i]是当前最小的,如果a[i] > b[j],那么b[j]能匹配任何a元素,因为所有a[k] >= a[i] > b[j],而b[j]只能减少,能增加,所以b[j]无法匹配任何a元素。因此,我们可以跳过b[j]。 类似地,如果 a[i] <= b[j],我们可以匹配,然后两者都递增。 在指针移动中: 排序a和b。 i=0, j=0 匹配数 = 0 当 i < n 且 j < m: 如果 a[i] <= b[j]: 匹配 a[i] 和 b[j],匹配数加一,i 加一,j 加一 否则: // a[i] > b[j] j 加一 // 跳过这个 b[j],因为它太小,无法匹配 结束后,如果 j == m 但 i < n,那么剩下的 a[i] 没有被匹配,需要添加。 在循环中,当 a[i] > b[j] 时,我们增加 j,跳过 b[j]。 由于 b 是递增排列,如果 b[j] < a[i],并且 a[i] 是当前最小的 a 元素,那么 b[j] 无法匹配任何 a 元素,所以丢弃 b[j] 是正确的。 当 a[i] <= b[j] 时,我们匹配并同时增加 i 和 j。 在循环之后,匹配的数量就是 M。 那么未被匹配的 a 元素数量是 n - M。 每个未被匹配的 a 元素都需要在 b 中新添加一个元素。 减少的次数被计入;只有添加的次数是 n - M。 在匹配的情况下,我们可能减少了 b[j],但减少操作是允许的,并且耗费添加的次数。 所以,最少添加次数是 n - M。 现在,在贪心算法中,我们是否总能找到最大匹配? 在排序后使用双指针。 假设 a 和 b 都是升序排列。 如果对于某个 i,j,有 a[i] <= b[j],那么匹配它们是最优的,因为如果跳过 b[j],可能用于后续的 a,但后续的 a 更大,所以 b[j] 也可以用于它们,但既然 a[i] 是当前最小的,而 b[j] 可以覆盖它,匹配它们应该没问题。 如果匹配,b[j] 可能用于某个 a[k] > a[i],但由于 a[k] >= a[i],而 b[j] >= a[i](因为 a[i] <= b[j]),所以 b[j] 也可以覆盖 a[k],但如果我们跳过,可能会浪费。 但在算法中,当 a[i] <= b[j],我们匹配,所以没问题。 如果 a[i] > b[j],我们跳过 b[j],而既然 b[j] < a[i] <= a 中所有后续元素,所以 b[j] 无用,跳过。 所以,贪心算法应正确找到最大匹配数。 例子: 设 a = [1,3,5], b = [2,4] 排序:a=[1,3,5], b=[2,4] i=0,j=0:a[0]=1 <= b[0]=2?是,匹配,i=1,j=1 i=1,j=1:a[1]=3 <= b[1]=4?是,匹配,i=2,j=2 i=2,但j=2已结束,所以所有a匹配,M=3,但n=3,m=2,匹配了2个?等等对。 a有3个元素,b只有2个。 第一次迭代后:匹配a[0]和b[0],i=1,j=1 第二次迭代:a[1]=3,b[1]=4,3<=4,匹配,i=2,j=2 现在i=2,但j=2超出范围,所以a[2]=5未匹配。 M=2,未被匹配的a元素数量:1,所以需要添加1次。 最少添加次数:1。 我们可以把b[0]减少到1?然后匹配a[0],把b[1]减少到3?但b[1]=4减少到3匹配a[1]=3,但a[2]=5未匹配,所以需要添加5。 或者我们可以添加一个元素5。 如果我们减少,但添加更多元素,但最少添加是1。 另一个例子:a=[1,1,2],b=[1,3] 排序后:a=[1,1,2],b=[1,3] i=0,j=0:a[0]=1 <= b[0]=1?是,匹配,i=1,j=1 i=1,j=1:a[1]=1 <= b[1]=3?是,匹配(减少b[1]到1),i=2,j=2?j=2结束。 但a[1]=1被匹配,但值:当匹配a[1]和b[1]时,我们把b[1]减少到1,所以b'有两个1,匹配两个a[1]。 但a[2]=2未匹配,需要添加2。 M=2,n=3,添加1次。 我们可以匹配a[0]和b[0](都是1),然后a[1]需要另一个1,但b[1]=3,可以减到1,或者添加一个新元素?减少更好,新增添加。 然后a[2]=2未匹配,添加。 最少添加:1。 如果b=[3,3],a=[1,2] 排序:a=[1,2],b=[3,3] i=0,j=0:a[0]=1 <= b[0]=3,是,匹配,i=1,j=1 i=1,j=1:a[1]=2 <= b[1]=3,是,匹配,i=2结束,全部匹配,M=2,添加0。 通过将两个b元素减少到1和2。 现在,如果a=[3,4],b=[1,2] 排序:a=[3,4],b=[1,2] i=0,j=0:a[0]=3 > b[0]=1,跳过b[0],j=1(现在b[1]=2) a[0]=3 > b[1]=2,跳过b[1],j=2结束,没有匹配,需要添加两个元素。 正确。 在算法中,当a[i] > b[j]时,我们增加j。 在代码中。 现在,计算最少添加次数。 在用户的问题中,数组可能未排序,但我们可以排序。 在实现中,我们需要处理多个测试用例之类的,但用户没有具体说明。 现在,关于引用:用户提供了引用,但在这个问题中,引用似乎无关。 引用[1]是关于在有序数组中找到第k个元素的,可能相关。 引用[2]是关于滑动窗口的,也直接相关。 引用[3]是关于滑雪的DP,也相关。 但用户说“上述引用仅供参考,并是用户真正的需求”,所以可能可以忽略。 但用户提到“请尽可能多的参考用户上一次的问题和你的回答”,但在这个对话中,这是第一次互动,所以可能没有历史。 在系统提示中,有之前的例子,但可能适用。 所以,对于这个问题,我认为解决方案是排序两个数组,然后使用双指针技术找到最大匹配数M,然后最少添加次数是n - M。 在双指针方法中,就像是在计算通过减少b元素可以覆盖多少个a元素。 然后添加剩下的。 现在,在用户的问题中,它写着“可减小b数组数字或添加新数字”,我们使用了减少操作。 而“最少添加数字的次数”就是添加的次数。 在匹配过程中,当我们匹配时,可能减少了b元素,但由于减少是允许的,所以没问题。 此外,对于未匹配的b元素,我们做任何操作,忽略。 所以,是的。 复杂度:排序O(n log n + m log m),双指针O(n + m)。 现在,来回答用户。 用户要求用中文回答。 结构清晰。 首先解释方法。 然后,可能提供伪代码或描述。 由于是算法问题,需要代码,但要解释。 用户可能期望类似参考的格式。 在参考中,有代码块。 但这里可能需要代码。 最后,生成相关问题。 首先,给出答案。 最少添加次数的计算方式。 定义变量。 设 a 的长度为 n,b 的长度为 m。 排序后,使用两个指针遍历。 添加次数 = n - M,其中 M 是匹配数。 在算法中,M 是满足 a[i] <= b[j] 的配对数量,其中每个元素只使用一次。 在指针方法中,当 a[i] <= b[j] 时,我们配对并同时增加两者。 否则,只增加 j。 当 j 超出范围时,停止。 M 是配对的次数。 然后添加次数 = n - M。 因为每个未配对的 a 元素都需要添加。 在代码中,我们可以写。 但题目中,可能没有具体说明编程。 现在,来写答案。 另外,在引用中,有数学达式,但这里可能需要。 用户提到所有行内数学达式使用 $...$,所以可能对于数组索引之类的内容。 但在文本中。 开始写。 </think> ### 问题分析 给定长度为 $n$ 的数组 $a$ 和长度为 $m$ 的数组 $b$,可以通过以下操作修改 $b$ 数组- 减少 $b$ 中现有元素的值(可以减小到任意更小的整数)。 - 添加新元素到 $b$ 中(值可以是任意整数)。 目标是使 $a$ 的每个元素在修改后的 $b$ 数组中都能找到一个唯一对应的元素(即一一对应),且对应元素的值必须相等。同时,需要最小化添加新元素的次数(减少现有元素的值增加添加次数)。 ### 解决思路 问题的核心是最大化通过减少 $b$ 中现有元素的值来覆盖 $a$ 中元素的数量。因为每个减少操作可以避免一次添加操作,从而最小化添加次数。具体步骤如下: 1. **排序数组**:将 $a$ 和 $b$ 分别按升序排序。排序后,我们可以高效地找到最多可以匹配的元素对。 2. **贪心匹配**:使用双指针遍历排序后的 $a$ 和 $b$。对于每个 $a$ 元素,尝试在 $b$ 中找到一个可匹配的元素(即 $b$ 元素值大于等于 $a$ 元素值)。如果找到,则通过减少 $b$ 元素的值来匹配;否则,该 $a$ 元素需要添加新元素。 3. **计算最少添加次数**:设匹配成功的元素对数量为 $M$,则最少添加次数为 $n - M$(即未被覆盖的 $a$ 元素数量)。 ### 算法描述 1. 排序数组 $a$ 和 $b$:时间复杂度 $O(n \log n + m \log m)$。 2. 双指针遍历找最大匹配: - 初始化指针 $i = 0$(用于 $a$)和 $j = 0$(用于 $b$),匹配计数 $M = 0$。 - 当 $i < n$ 和 $j < m$ 时: - 如果 $a[i] \leq b[j]$,则 $b[j]$ 可以通过减少到 $a[i]$ 来匹配 $a[i]$。匹配成功,$M$ 增加 1,$i$ 和 $j$ 均递增。 - 否则($a[i] > b[j]$),则 $b[j]$ 无法匹配任何 $a$ 元素(因为 $b[j]$ 只能减少,能增加,且后续 $a$ 元素更大),递增 $j$。 - 如果 $j$ 到达 $b$ 末尾但 $i$ 未结束,则剩余的 $a[i..n-1]$ 元素均需要添加。 3. 最少添加次数为 $n - M$。 ### 示例说明 - **示例 1**:$a = [1, 3, 5]$, $b = [2, 4]$ - 排序后:$a = [1, 3, 5]$, $b = [2, 4]$ - 遍历: - $i=0,j=0$: $1 \leq 2$,匹配($b[0]$ 减少到 1),$M=1$, $i=1,j=1$ - $i=1,j=1$: $3 \leq 4$,匹配($b[1]$ 减少到 3),$M=2$, $i=2,j=2$ - $i=2,j=2$: $j$ 结束,$a[2]=5$ 未匹配,添加新元素 5。 - 最少添加次数:$n - M = 3 - 2 = 1$。 - **示例 2**:$a = [1, 1, 2]$, $b = [1, 3]$ - 排序后:$a = [1, 1, 2]$, $b = [1, 3]$ - 遍历: - $i=0,j=0$: $1 \leq 1$,匹配($b[0]$ 变),$M=1$, $i=1,j=1$ - $i=1,j=1$: $1 \leq 3$,匹配($b[1]$ 减少到 1),$M=2$, $i=2,j=2$ - $i=2,j=2$: $j$ 结束,$a[2]=2$ 未匹配,添加新元素 2- 最少添加次数:$n - M = 3 - 2 = 1$。 - **示例 3**:$a = [3, 4]$, $b = [1, 2]$ - 排序后:$a = [3, 4]$, $b = [1, 2]$ - 遍历: - $i=0,j=0$: $3 > 1$, 递增 $j$ ($j=1$) - $i=0,j=1$: $3 > 2$, 递增 $j$ ($j=2$) - $j$ 结束,无匹配,添加两个新元素(3 和 4)。 - 最少添加次数:$n - M = 2 - 0 = 2$。 ### 伪代码实现 ```pseudocode function minAdditions(a, b): n = length(a) m = length(b) sort(a) // 升序排序 a sort(b) // 升序排序 b i = 0, j = 0 M = 0 // 匹配计数 while i < n and j < m: if a[i] <= b[j]: M += 1 // 匹配成功:减少 b[j] 到 a[i] 覆盖 a[i] i += 1 j += 1 else: j += 1 // 跳过 b[j],无法匹配任何 a 元素 return n - M // 最少添加次数 ``` ### 复杂度分析 - **时间复杂度**:排序 $a$ 和 $b$ 的时间为 $O(n \log n + m \log m)$,双指针遍历为 $O(n + m)$,整体为 $O(n \log n + m \log m)$。 - **空间复杂度**:$O(1)$(原地排序或使用额外空间排序)。 ### 注意事项 -算法假设数组元素为整数,且操作中减少值或添加值会引入其他约束(如值范围)。 - 如果 $m = 0$($b$ 为空),则所有 $a$ 元素都需要添加,添加次数为 $n$。 - 匹配过程中,$b$ 中未使用的元素可以忽略,影响结果。 ### 相关问题 1. 如果允许增加 $b$ 数组中元素的值,会对最少添加次数产生什么影响? 2. 在上述算法中,如何扩展以输出具体的修改操作(哪些元素减少、哪些值添加)? 3. 如果 $a$ 和 $b$ 数组包含重复元素,且要求匹配时值严格相等(能减少后匹配),该如何求解? 4. 在分布式环境下,如何高效处理大规模数组 $a$ 和 $b$ 的匹配问题?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值