文章目录
相关标签
- 位运算
- 哈希表
题目描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解法1:暴力查找(双循环,时间复杂度不满足)
class Solution {
public int singleNumber(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int j;
// 遍历查找
for (j = 0; j < nums.length; j++) {
// 如果下标相等,为同一元素,跳过
if (i == j){
continue;
}
// 如果找到相等的元素,进行下次循环
if (nums[j] == nums[i]){
break;
}
}
// 结束单层循环,没有匹配的元素,返回
if (nums.length == j){
return nums[i];
}
}
return -1;
}
}
时间复杂度:O(n^2),空间复杂度:O(1)
- 执行用时:359ms
解法2:排序(快排,时间复杂度不满足)
思路:先排序,排好序的数组元素,两两一组
class Solution {
public int singleNumber(int[] nums) {
quickSort(nums,0,nums.length-1);
for (int i = 0; i < nums.length; i+=2) {
if (i==nums.length-1){
return nums[i];
}
if (nums[i] != nums[i+1]){
return nums[i];
}
}
return -1;
}
public static void quickSort(int[] arr, int low, int high) {
int i, j, temp, t;
if (low > high) {
return;
}
i = low;
j = high;
//temp就是基准位
temp = arr[low];
while (i < j) {
//先看右边,依次往左递减
while (temp <= arr[j] && i < j) {
j--;
}
//再看左边,依次往右递增
while (temp >= arr[i] && i < j) {
i++;
}
//如果满足条件则交换
if (i < j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j - 1);
//递归调用右半数组
quickSort(arr, j + 1, high);
}
}
时间复杂度:O(nlogn),空间复杂度:O(1)
- 执行用时:6 ms
解法3:HashSet(空间复杂度不满足)
使用集合存储数字。
遍历数组中的每个数字,如果集合中没有该数字,则将该数字加入集合,如果集合中已经有该数字,则将该数字从集合中删除,最后剩下的数字就是只出现一次的数字。
class Solution {
public int singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i = 0; i < nums.length; i++){
if(!set.add(nums[i])){
set.remove(nums[i]);
}
}
return set.iterator().next();
}
}
时间复杂度:O(n),空间复杂度:O(n)
- 执行用时:11 ms
解法4:HashMap(空间复杂度不满足)
使用哈希表存储每个数字和该数字出现的次数。
遍历数组即可得到每个数字出现的次数,并更新哈希表,最后遍历哈希表,得到只出现一次的数字。
class Solution {
public int singleNumber(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
// 遍历数组,存入hashmap
for (int i = 0; i < nums.length; i++) {
if (!map.containsKey(nums[i])){
map.put(nums[i],1);
}else {
map.put(nums[i],2);
}
}
// 遍历hashmap,寻找value为1的key即为所求
Iterator<Integer> iterator = map.keySet().iterator();
while (iterator.hasNext()){
int index = iterator.next();
if (map.get(index)==1){
return index;
}
}
return -1;
}
}
时间复杂度:O(n),空间复杂度:O(n)
- 执行用时: 18 ms
解法5:位运算(√)
如果要求使用线性时间复杂度和常数空间复杂度,上述解法显然都不满足要求。
对于这道题,可使用异或运算。
异或运算有以下三个性质:
- 任何数和0做异或运算,结果仍然是原来的数
- 任何数和其自身做异或运算,结果是0
- 异或运算满足交换律和结合律
(所有元素异或,通过交换律可知,所有两两相同的元素进行异或运算结果都为0,因此最终得到的值即唯一出现的数字)
class Solution {
public int singleNumber(int[] nums) {
int result = 0;
for(int num : nums){
result ^= num;
}
return result;
}
}
时间复杂度:O(n),空间复杂度:O(1)
- 执行用时: 1 ms