第一题:找单独的数
一、问题描述:
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小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)算法步骤
- 异或运算:异或运算有一个重要的性质:a ^ a = 0 和 a ^ 0 = a。这意味着,如果我们对数组中的所有元素进行异或运算,所有出现两次的数字会相互抵消,最终剩下的就是那个只出现一次的数字。
(4)具体步骤
- 初始化一个变量 result 为 0。
- 遍历数组中的每一个元素,将 result 与当前元素进行异或运算。
- 遍历结束后,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,则返回该数字。