题目一(数组中只出现一次的两个数字)描述:
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
示例:
输入:[1,4,1,6]
返回值:[4,6]
说明:返回的结果中较小的数排在前面
方法一:哈希表
已经总结过所有与元素次数相关的问题都可以用哈希表解决。
import java.util.*;
public class Solution {
public int[] FindNumsAppearOnce (int[] array) {
HashMap<Integer, Integer> map = new HashMap<>();
int[] result = new int[2];
int index = 0;
for (int i = 0; i < array.length; i++) {
int count = map.getOrDefault(array[i], 0);
map.put(array[i], count + 1);
}
for (Integer i : map.keySet()) {
if (map.get(i) == 1) {
result[index] = i;
index++;
}
}
if (arr[0] > arr[1]) {
swap(arr); // 将较小的数放在前面
}
return result;
}
private void swap(int[] arr) {
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
}
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
n
)
O(n)
O(n)。
方法二:位运算
假设数组中两个数字(a, b)只出现一次,其余数字都出现两次,说明a和b必然不相等,它们的二进制也就不相同,则a和b异或的结果不等于0。先将数组中的所有整数进行异或,结果为result
,由于其它元素都出现两次,异或满足交换律,所以最终的结果就等于a和b异或的结果,即result = a ^ b
,且result不等于0。result二进制从低位到高位,在第一个为1的位置,a和b二进制在此位置不相等(设a在此位置为0,b在此位置为1,这样异或的结果在此位置才为1)。找到result二进制从低位到高位第一个为1的位置,result = result & (-result)
,此时假设result结果为0000 1000
,说明a的二进制在第3位是0,b的二进制在第3位是1,据此将原数组中的元素划分为两组。一组:元素第3位是0,则该元素 i & result 结果为0,arr[0] ^= i
,a被分到该组,arr[0]异或的最终结果就是a;另一组:元素的第3位是1,则该元素 i & result 结果不为0,arr[1] ^= i
,b被分到该组,arr[1]异或的最终结果就是b。返回arr之前,先判断arr[0]和arr[1]的大小,如果arr[0] > arr[1],则交换arr[0]和arr[1],使得较小的数在前面。
import java.util.*;
public class Solution {
public int[] FindNumsAppearOnce (int[] array) {
int[] arr = new int[2];
int result = 0;
for (int i: array) {
result ^= i;
}
result = result & (-result); // 从低位到高位开始,第一个二进制位是1的位置
for (int i: array) {
if ((i & result) == 0) {
arr[0] ^= i;
} else {
arr[1] ^= i;
}
}
if (arr[0] > arr[1]) {
swap(arr);
}
return arr;
}
private void swap(int[] arr) {
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
}
时间复杂度:遍历数组每一个元素,进行异或, O ( n ) O(n) O(n);空间复杂度: O ( 1 ) O(1) O(1)。
题目二(数组中只出现一次的唯一数字)描述 :
在一个数组 nums
中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例:
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
方法一:哈希表
采用哈希表的方法非常简单,不再叙述过程。
class Solution {
public int singleNumber(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for(int num: nums) {
int count = map.getOrDefault(num, 0);
map.put(num, count + 1);
}
for(Integer num: map.keySet()) {
if(map.get(num) == 1) {
return num;
}
}
return -1;
}
}
时间复杂度:
O
(
n
)
O(n)
O(n),空间复杂度:
O
(
n
)
O(n)
O(n)。
方法二:位运算
由于其余重复的数字出现三次,如果使用异或位运算,结果还是这个重复的数字,所以本题无法使用异或位运算。对于重复的数字,将其二进制中对应的位置求和,则二进制每个位置的总和能被3整除。如果再加上这个只出现一次的整数,二进制中对应的每一位能被3整除,则说明这个只出现一次的数的二进制对应位置为0;如果加上这个只出现一次的整数,二进制中对应的每一位不能被3整除,则说明这个只出现一次的数的二进制对应位置为1。所以总的思路是先求数组中所有整数的二进制对应位的总和,再通过二进制形式求得这个只出现一次的整数。
class Solution {
public int singleNumber(int[] nums) {
int[] bitSum = new int[32];
for (int i = 0; i < nums.length; i++) {
int flag = 1;
for (int j = 31; j >= 0; j--) {
if ((nums[i] & flag) != 0) {
bitSum[j] += 1;
}
flag = flag << 1;
}
}
// 所有整数的每位二进制求和结束
int result = 0;
for (int i = 0; i < 32; i++) {
result = result << 1;
result += bitSum[i] % 3;
}
return result;
}
}
时间复杂度:虽然有双层for循环,但是内层for循环的时间复杂度为 O ( 1 ) O(1) O(1),所以总的时间复杂度为 O ( n ) O(n) O(n);算法中需要开辟32个元素空间,仍然是常量阶,所以空间复杂度为 O ( 1 ) O(1) O(1)。
结束语:如果本篇博客对您有帮助,请点赞、收藏或关注,您的鼓励是博主进步的动力,感谢支持,共同进步。