一串数字中有两个只出现一次的数字其余都是成对相同,求这两个数

本文介绍了一种通过异或操作解决查找数组中唯一出现的两个数字的方法,详细步骤包括求异或结果、确定异或结果的首个1位、将数组分为两组并再次异或求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当然如果这个问题是只有一个不同的数,其他数字成对相同,那么就是把所有数字异或就得出这个数了

这次是有两个只出现一次的数字,其他数字都成对相同

1)先把所有数都异或得到数t

2)算出t的二进制第一个1的位置flag

3)将所有数根据二进制flag位置是否为1分成两组b1[],b2[] (此时每组数字的个数一定是奇数)

4)将b1组异或得到ans1,将b2组异或得到ans2

#include<iostream>
#include<stdio.h>
using namespace std;
#define isset(a,n) a&(1<<(n))
#define N 1000
int main(){
    int t,c,i,b[N],b1[N],b2[N],flag,cou1,cou2,ans1,ans2;
    while(cin>>c,c){
        if(c%2!=0){
            cout<<"c必须为偶数"<<endl;
            continue;
        }
        cin>>b[0];
        t=b[0];
        for(i=1;i<c;i++){
            cin>>b[i];
            t^=b[i];
        }
        flag=0;
        for(i=0;i<32;i++)
            if(isset(t,i)){
                flag=i;
                break;
            }
        cou1=cou2=0;
        for(int i=0;i<c;i++){
            if(isset(b[i],flag))
                b1[cou1++]=b[i];
            else
                b2[cou2++]=b[i];
        }
        ans1=b1[0];
        ans2=b2[0];
        for(i=1;i<cou1;i++)
            ans1^=b1[i];
        for(i=1;i<cou2;i++)
            ans2^=b2[i];
        cout<<ans1<<" "<<ans2<<endl;

    }

}


 

<think>我们需要生成7位有效数字的整数,即7位整数(范围1000000到9999999)。要每次生成的数字重复。 高效方法:使用一个Set来记录已经生成的数字,当生成新数字时检查是否已存在。为了避免在生成大量数字时效率下降(因为随机碰撞概率增加),我们可以考虑使用序列号(但这样可能够随机)或者使用洗牌算法(shuffle)来预先生成所有可能的数字。然而,7位整数一共有9000000个(9e6),如果我们要生成的数量远小于这个数,使用Set是可行的;如果接近或等于这个数,我们可以预先生成所有数字并打乱顺序,然后依次取出。 考虑到问题要每次调用生成一个重复的数字,我们可以设计种模式: 1. 当需要生成的数字数量远小于9000000时,使用Set记录已生成的数字,每次随机生成直到重复。 2. 当需要生成的数字数量接近9000000时,预先生成一个包含所有可能数字的列表,然后打乱顺序,按顺序返回。 但是,问题没有明确需要生成多少个,所以我们可以设计一个类,在初始化时决定使用哪种方式。过,由于9000000个数字占用的内存大约是9000000*4字节(整数)约36MB,在内存允许的情况下,我们可以预先生成所有数字并打乱。这样在后续调用中就可以高效地依次返回。 这里我们采用第二种方式:预生成所有可能的7位整数,然后打乱顺序,并提供一个next()方法依次返回。 实现步骤: 1. 创建一个包含1000000到9999999的整数数组。 2. 使用Fisher-Yates洗牌算法(或Collections.shuffle)打乱数组顺序。 3. 维护一个指针,每次调用next()方法时返回指针位置的数字,然后指针后移。 注意:当所有数字都生成完后,再次调用应该抛出异常或返回特殊值。 代码实现: ```java import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; public class UniqueSevenDigitGenerator { // 所有可能的7位整数(1000000到9999999) private static final int MIN = 1000000; private static final int MAX = 9999999; private final int[] numbers; private int index; public UniqueSevenDigitGenerator() { // 生成所有整数 numbers = IntStream.rangeClosed(MIN, MAX).toArray(); // 打乱数组 shuffleArray(numbers); index = 0; } // Fisher-Yates洗牌算法 private void shuffleArray(int[] array) { Random rnd = new Random(); for (int i = array.length - 1; i > 0; i--) { int j = rnd.nextInt(i + 1); // 交换 int temp = array[i]; array[i] = array[j]; array[j] = temp; } } public int next() { if (index >= numbers.length) { throw new IllegalStateException("All numbers have been generated."); } return numbers[index++]; } // 测试 public static void main(String[] args) { UniqueSevenDigitGenerator generator = new UniqueSevenDigitGenerator(); for (int i = 0; i < 10; i++) { System.out.println(generator.next()); } } } ``` 但是,如果我们需要生成的数字数量多(比如只生成1000个),那么预先生成900万个数字并打乱就显得浪费。因此,我们可以改进为动态生成,使用Set来去重,并设置一个最大尝试次数。 改进版本(动态生成,适用于数量远小于900万的情况): ```java import java.util.HashSet; import java.util.Random; import java.util.Set; public class UniqueSevenDigitGenerator { private final Set<Integer> generated = new HashSet<>(); private final Random random = new Random(); private static final int MAX_ATTEMPTS = 1000; public int next() { int attempts = 0; while (attempts < MAX_ATTEMPTS) { int num = 1000000 + random.nextInt(9000000); if (!generated.contains(num)) { generated.add(num); return num; } attempts++; } throw new RuntimeException("Could not generate a unique number after " + MAX_ATTEMPTS + " attempts."); } public static void main(String[] args) { UniqueSevenDigitGenerator generator = new UniqueSevenDigitGenerator(); for (int i = 0; i < 10; i++) { System.out.println(generator.next()); } } } ``` 如何选择? 我们可以让用户指定生成数量,然后根据数量选择策略: - 如果生成数量超过总数的一半(450万),则预生成所有并打乱。 - 否则,使用动态生成(Set去重)。 但为了简单,我们这里只提供一种策略。考虑到问题要高效,且没有指定数量,我们假设用户会生成所有数字(即数量远小于900万),使用动态生成方法。 但是,如果用户需要生成接近900万个数字,动态生成会越来越慢(因为随机碰撞概率增加)。因此,我们可以提供一个构造参数,让用户指定预期数量,然后选择策略。 下面我们实现一个智能选择策略的生成器: ```java import java.util.*; public class UniqueSevenDigitGenerator { private static final int TOTAL_NUMBERS = 9000000; // 9e6 private final Queue<Integer> preGeneratedNumbers; // 用于预生成的情况 private final Set<Integer> generatedSet; // 用于动态生成的情况 private final Random random = new Random(); private final int maxNumbers; private int generatedCount = 0; private final boolean usePreGenerate; public UniqueSevenDigitGenerator(int maxNumbers) { this.maxNumbers = maxNumbers; // 如果maxNumbers超过总数的一半,则预生成所有并打乱,然后取前maxNumbers个(但这样可能浪费,所以预生成所有,然后按需取) // 但预生成所有需要36MB内存,如果用户只需要100个,我们没必要预生成所有。所以折中:如果maxNumbers超过100万,则预生成所有;否则动态生成。 usePreGenerate = maxNumbers > 1000000; if (usePreGenerate) { // 预生成所有数字 List<Integer> allNumbers = new ArrayList<>(TOTAL_NUMBERS); for (int i = 0; i < TOTAL_NUMBERS; i++) { allNumbers.add(1000000 + i); } Collections.shuffle(allNumbers); preGeneratedNumbers = new LinkedList<>(allNumbers.subList(0, maxNumbers)); generatedSet = null; } else { preGeneratedNumbers = null; generatedSet = new HashSet<>(maxNumbers); } } public int next() { if (generatedCount >= maxNumbers) { throw new IllegalStateException("Reached max numbers limit: " + maxNumbers); } if (usePreGenerate) { generatedCount++; return preGeneratedNumbers.poll(); } else { int attempts = 0; while (attempts < 10000) { int num = 1000000 + random.nextInt(TOTAL_NUMBERS); if (!generatedSet.contains(num)) { generatedSet.add(num); generatedCount++; return num; } attempts++; } throw new RuntimeException("Could not generate unique number after 10000 attempts."); } } public static void main(String[] args) { // 生成10个唯一数字 UniqueSevenDigitGenerator smallGenerator = new UniqueSevenDigitGenerator(10); for (int i = 0; i < 10; i++) { System.out.println(smallGenerator.next()); } // 生成1000000个唯一数字(使用预生成策略) UniqueSevenDigitGenerator largeGenerator = new UniqueSevenDigitGenerator(1000000); for (int i = 0; i < 10; i++) { // 只打印前10个 System.out.println(largeGenerator.next()); } } } ``` 这个实现根据用户指定的最大生成数量选择策略: - 如果maxNumbers大于100万,则预生成所有900万个数字,打乱顺序,然后取前maxNumbers个(注意:这里我们预生成所有,但只保留maxNumbers个,这样可能浪费,因为预生成所有需要时间。另一种做法是只生成maxNumbers个,但这样需要随机选取maxNumbers个重复数字,可以使用采样的方法,但实现复杂。为了简单,我们预先生成所有,然后取子集)。 - 否则,使用Set动态生成。 注意:预生成策略在构造时可能会消耗较多时间和内存(生成900万个整数并打乱),但后续的next()操作是O(1)的。动态生成策略在构造时很快,但next()操作在生成数量接近maxNumbers时可能会变慢(因为碰撞概率增加)。 根据需选择合适策略。 但是,如果用户需要生成超过100万个,但远小于900万(比如200万),我们也可以只生成200万个随机数,而生成所有900万个。我们可以使用一种高效的采样算法(如Reservoir sampling)来生成重复的随机数。但这样实现复杂。 因此,我们折中使用:当maxNumbers大于100万时,我们预先生成整个900万的列表并打乱,然后取前maxNumbers个。这样虽然初始化慢,但后续快。 如果内存足,我们可以考虑使用位图(bitmap)来动态生成,但这样每次生成需要遍历位图,效率高。 综上所述,我们采用上述策略。 注意:我们假设用户会要生成超过900万个数字。 测试:在main方法中,我们分别测试生成10个和100万个数字的情况。 输出:每次调用next()返回一个唯一的7位整数。 如果用户要生成超过900万个数字,我们可以在构造器中检查maxNumbers,如果超过TOTAL_NUMBERS,则抛出异常。 修改构造器: ```java public UniqueSevenDigitGenerator(int maxNumbers) { if (maxNumbers > TOTAL_NUMBERS) { throw new IllegalArgumentException("maxNumbers cannot exceed " + TOTAL_NUMBERS); } // ... 其余变 } ``` 完整代码: ```java import java.util.*; public class UniqueSevenDigitGenerator { private static final int MIN = 1000000; private static final int MAX = 9999999; private static final int TOTAL_NUMBERS = MAX - MIN + 1; // 9000000 private final Queue<Integer> preGeneratedNumbers; // 用于预生成的情况 private final Set<Integer> generatedSet; // 用于动态生成的情况 private final Random random = new Random(); private final int maxNumbers; private int generatedCount = 0; private final boolean usePreGenerate; public UniqueSevenDigitGenerator(int maxNumbers) { if (maxNumbers > TOTAL_NUMBERS) { throw new IllegalArgumentException("Cannot generate more than " + TOTAL_NUMBERS + " unique numbers."); } this.maxNumbers = maxNumbers; // 决定策略:如果maxNumbers大于100万,则使用预生成所有然后取子集;否则动态生成 usePreGenerate = maxNumbers > 1_000_000; if (usePreGenerate) { // 预生成所有数字(1000000到9999999) List<Integer> allNumbers = new ArrayList<>(TOTAL_NUMBERS); for (int i = 0; i < TOTAL_NUMBERS; i++) { allNumbers.add(MIN + i); } // 打乱 Collections.shuffle(allNumbers); // 取前maxNumbers个 preGeneratedNumbers = new LinkedList<>(allNumbers.subList(0, maxNumbers)); generatedSet = null; } else { preGeneratedNumbers = null; generatedSet = new HashSet<>(maxNumbers); } } public int next() { if (generatedCount >= maxNumbers) { throw new IllegalStateException("Reached max numbers limit: " + maxNumbers); } if (usePreGenerate) { generatedCount++; return preGeneratedNumbers.poll(); } else { int attempts = 0; while (attempts < 10000) { int num = MIN + random.nextInt(TOTAL_NUMBERS); if (!generatedSet.contains(num)) { generatedSet.add(num); generatedCount++; return num; } attempts++; } throw new RuntimeException("Could not generate unique number after 10000 attempts."); } } public static void main(String[] args) { // 示例:生成10个唯一数字 UniqueSevenDigitGenerator smallGen = new UniqueSevenDigitGenerator(10); for (int i = 0; i < 10; i++) { System.out.println(smallGen.next()); } // 示例:生成1000000个唯一数字(使用预生成策略) UniqueSevenDigitGenerator largeGen = new UniqueSevenDigitGenerator(1000000); for (int i = 0; i < 10; i++) { // 打印前10个 System.out.println(largeGen.next()); } } } ``` 这个实现可以高效地生成指定数量的唯一7位整数。 注意:在预生成策略中,构造器会花费较多时间和内存(生成900万个整数,打乱,然后取子集)。如果maxNumbers比较小(比如100万),我们其实可以只生成100万个随机重复数字,而生成所有900万。但为了代码简单,我们采用当前策略。如果对初始化性能要很高,可以进一步优化。 另一种优化预生成策略:生成所有900万,而是生成一个0到900万-1的随机排列(使用Fisher-Yates算法生成部分排列),然后取前maxNumbers个,每个数加上MIN。这样我们只需要一个长度为maxNumbers的数组,但生成部分排列的算法稍微复杂。这里为了简单,我们使用预先生成整个列表。 如果内存足,我们可以使用文件存储预生成的数字,但这样效率低。所以,如果内存足,建议使用动态生成(适用于maxNumbers<=100万)。 因此,这个实现在内存和速度之间做了权衡。 最后,我们提供相关的问题。 §§相关问题§§: 1. 如何生成重复的随机整数序列? 2. 在Java中,如何高效地打乱一个非常大的列表(例如超过内存大小)? 3. 如何生成一个范围内的随机重复整数,且需要存储所有生成的数字(即使用最小内存)? 4. 在生成唯一随机数时,如何避免在接近上限时性能下降?</think># Java中高效生成唯一7位有效数字整数的封装方法 在Java中高效生成唯一7位有效数字整数(即1000000到9999999之间的整数)并确保每次生成的值都重复,可以使用以下优化方法: ## 高效实现方案 ```java import java.util.BitSet; import java.util.Random; public class UniqueSevenDigitGenerator { // 7位有效数字的范围:1000000 ~ 9999999 private static final int MIN = 1000000; private static final int MAX = 9999999; private static final int RANGE = MAX - MIN + 1; // 9000000个可能值 // 使用BitSet高效跟踪已生成的数字(位图技术) private final BitSet generated = new BitSet(RANGE); private final Random random = new Random(); // 计数器跟踪已生成数量 private int count = 0; /** * 生成一个唯一的7位有效数字 * @return 7位有效数字整数 * @throws IllegalStateException 当所有可能值都已生成时抛出 */ public int generateUnique7Digit() { if (count >= RANGE) { throw new IllegalStateException("所有可能的7位数字已生成完毕"); } // 生成随机偏移量 int offset = random.nextInt(RANGE); int attempts = 0; // 使用线性探测解决冲突(高效查找未使用的位置) while (generated.get(offset)) { offset = (offset + 1) % RANGE; attempts++; // 防止无限循环(理论上会发生,但安全起见) if (attempts > RANGE) { throw new IllegalStateException("无法找到未使用的数字"); } } // 标记该数字为已生成 generated.set(offset); count++; // 计算实际数字值 return MIN + offset; } /** * 重置生成器状态(清除所有已生成记录) */ public void reset() { generated.clear(); count = 0; } // 测试方法 public static void main(String[] args) { UniqueSevenDigitGenerator generator = new UniqueSevenDigitGenerator(); // 生成并打印10个唯一数字 for (int i = 0; i < 10; i++) { int number = generator.generateUnique7Digit(); System.out.println("生成的唯一7位数字 #" + (i+1) + ": " + number); } // 验证唯一性(生成10000个数字) System.out.println("\n生成10000个唯一数字测试..."); generator.reset(); for (int i = 0; i < 10000; i++) { generator.generateUnique7Digit(); } System.out.println("成功生成10000个唯一数字"); } } ``` ## 代码解释 ### 1. 核心数据结构:BitSet - 使用`BitSet`替代`HashSet`或`List`来跟踪已生成的数字 - 每个位代表一个可能的数字(1000000到9999999) - 内存优化:仅需约1.1MB内存(9000000位 ≈ 1.07MB) - 时间复杂度:O(1)的查找和设置操作 ### 2. 高效生成算法 - **随机起点**:从随机位置开始搜索 - **线性探测**:顺序查找下一个可用位置 - **冲突解决**:当遇到已生成的数字时,继续查找下一个位置 - **终止条件**:当所有可能数字都已生成时抛出异常 ### 3. 性能优化点 - **无对象创建**:避免使用Integer对象,减少GC压力 - **位操作高效**:BitSet的get/set操作接近O(1)时间复杂度 - **内存紧凑**:相比HashSet(每个Integer约16-24字节),BitSet每个数字仅需1位 - **负载均衡**:随机起点确保均匀分布 ### 4. 边界处理 - 当所有9000000个可能值都已生成时抛出异常 - 设置最大尝试次数防止无限循环 - 提供`reset()`方法重置生成器状态 ## 性能分析 | 方法 | 内存占用 | 生成速度 | 适合场景 | |------|----------|----------|----------| | BitSet+线性探测 | ~1.1MB | O(1)平均 | 最佳方案,适合任意数量生成 | | HashSet | 100MB+ | O(1)平均 | 少量生成时可用 | | 预生成所有数字 | ~34MB | O(1) | 需要全部生成时可用 | ## 测试输出示例 ``` 生成的唯一7位数字 #1: 4827361 生成的唯一7位数字 #2: 1578924 生成的唯一7位数字 #3: 9356712 生成的唯一7位数字 #4: 6241893 生成的唯一7位数字 #5: 3095726 生成的唯一7位数字 #6: 8710453 生成的唯一7位数字 #7: 4562138 生成的唯一7位数字 #8: 1928374 生成的唯一7位数字 #9: 7689012 生成的唯一7位数字 #10: 3456789 生成10000个唯一数字测试... 成功生成10000个唯一数字 ``` ##
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码随想录

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

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

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

打赏作者

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

抵扣说明:

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

余额充值