每日一练:第一天(2024.12.18)

102 阅读5分钟

第一题:找单独的数

一、问题描述:

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。

要求:

1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。

2. 尽量减少额外空间的使用,以体现你的算法优化能力。

测试样例

样例1:

输入:cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
输出:4
解释:拿到数字 4 的同学是唯一一个没有配对的。

样例2:

输入:cards = [0, 1, 0, 1, 2]
输出:2
解释:数字 2 只出现一次,是独特的卡片。

样例3:

输入:cards = [7, 3, 3, 7, 10]
输出:10
解释:10 是班级中唯一一个不重复的数字卡片。

约束条件

  • 1 ≤ cards.length ≤ 1001
  • 0 ≤ cards[i] ≤ 1000
  • 班级人数为奇数
  • 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次

二、题解

(1)问题理解

你需要在一个整数数组中找到唯一一个出现一次的数字。数组中的其他数字都恰好出现两次。

(2)数据结构选择

由于题目要求时间复杂度为 O(n),并且尽量减少额外空间的使用,我们可以考虑使用位运算来解决这个问题。

(3)算法步骤

  1. 异或运算:异或运算有一个重要的性质:a ^ a = 0 和 a ^ 0 = a。这意味着,如果我们对数组中的所有元素进行异或运算,所有出现两次的数字会相互抵消,最终剩下的就是那个只出现一次的数字。

(4)具体步骤

  1. 初始化一个变量 result 为 0。
  2. 遍历数组中的每一个元素,将 result 与当前元素进行异或运算。
  3. 遍历结束后,result 就是那个只出现一次的数字。

(5)总结

通过使用异或运算,我们可以在 O(n) 的时间复杂度内解决这个问题,并且不需要额外的空间。

三、答案

public class Main {
public static int solution(int[] cards) {
// Edit your code here
/异或运算有一个重要的性质:a ^ a = 0 和 a ^ 0 = a。
这意味着,如果我们对数组中的所有元素进行异或运算,所有出现两次的数字会相互抵消
/
int result = 0;
for(int card:cards){
result ^= card;
}
return result;
}

    public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}) );
System.out.println(solution(new int[]{0, 1, 0, 1, 2}) );
}
}

 

 

 

 

 

 

 

 

 

四、其它方法(没有约束条件)

方法一:使用哈希表,时间复杂度为 O(n),但需要额外的空间

我们可以使用哈希表来记录每个数字出现的次数,然后找到只出现一次的数字。步骤:

1.创建一个哈希表(HashMap)来记录每个数字出现的次数。

2.遍历数组,将每个数字及其出现次数记录在哈希表中。   3.遍历哈希表,找到出现次数为 1 的数字。

import java.util.HashMap;

public class Main {

    public static int solution(int[] cards) {

        HashMap<Integer, Integer> countMap = new HashMap<>();

       

// 记录每个数字出现的次数

        for (int card : cards) {

            countMap.put(card, countMap.getOrDefault(card, 0) + 1);

        }

// 找到出现次数为 1 的数字

        for (int card : countMap.keySet()) {

            if (countMap.get(card) == 1) {

                return card;

            }

        }

        return -1;    // 理论上不会执行到这里

    }

 

    public static void main(String[] args) {

        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}));

        System.out.println(solution(new int[]{0, 1, 0, 1, 2}));

    }

}

方法二:排序后查找,时间复杂度为 O(n log n),但不需要额外的空间。

我们可以先对数组进行排序,然后查找只出现一次的数字。

步骤:

1.对数组进行排序。

2.遍历排序后的数组,找到只出现一次的数字。

import java.util.Arrays;

public class Main {

    public static int solution(int[] cards) {

        Arrays.sort(cards);

       

        for (int i = 0; i < cards.length; i += 2) {

            if (i == cards.length - 1 || cards[i] != cards[i + 1]) {

                return cards[i];

            }

        }

       

        return -1; // 理论上不会执行到这里

    }

 

    public static void main(String[] args) {

        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}));

        System.out.println(solution(new int[]{0, 1, 0, 1, 2}));

    }

}

 

 

 

 

方法三:使用两个 for 循环

我们可以通过两次遍历数组来找到只出现一次的数字:第一次遍历数组,记录每个数字的出现次数。    第二次遍历数组,找到只出现一次的数字。

public class Main {

    public static int solution(int[] cards) {

// 第一次遍历:记录每个数字的出现次数

        int[] count = new int[1001]; // 根据题目约束条件,最大值为1000

        for (int card : cards) {

            count[card]++;

        }

 // 第二次遍历:找到只出现一次的数字

        for (int card : cards) {

            if (count[card] == 1) {

                return card;

            }

        }

        return -1; // 理论上不会执行到这里

    }

 

    public static void main(String[] args) {

        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}));

        System.out.println(solution(new int[]{0, 1, 0, 1, 2}));

    }

}

(1)第一次遍历:

我们使用一个数组 count 来记录每个数字的出现次数。数组的大小为 1001,因为题目中数字的范围是 0 到 1000。

遍历数组 cards,将每个数字的出现次数记录在 count 数组中。

(1)第二次遍历:

再次遍历数组 cards,检查 count 数组中每个数字的出现次数。

如果某个数字的出现次数为 1,则返回该数字。