剑指offer 004 只出现一次的数字
题目
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,100]
输出:100
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/WGki4K
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
方法一:排序+栈
思路:首先先排序,排序结束之后将数字依次压栈,压栈条件是栈顶元素和待入栈元素如果相同的话,入栈。最后只剩下一个元素的数字。
public int singleNumber(int[] nums) {
Arrays.sort(nums);
Deque<Integer> stack = new ArrayDeque<>();
int idx = 0;
for (int num : nums) {
if (stack.isEmpty() || stack.peek() == num) {
stack.push(num);
idx++;
}
if (idx == 3) {
stack.clear();
idx=0;
}
}
return stack.peek();
}
这种效率比较低。不推荐。
方法二:双指针+排序
指定左右指针,左指针指向最左端,右指针指向第三个元素,如果这两个数字相等,则证明这三个数字是相同的,判断下一组三个数字即可。否则的话,返回最左端的元素。
public int singleNumber(int[] nums) {
Arrays.sort(nums);
int left = 0,right = 2;
int n = nums.length;
while (right<n){
if(nums[left] != nums[right]){
return nums[left];
}
left = left+3;
right = left+2;
}
return nums[left];
}
比较推荐这种方法,效率比较好。
方法三:位运算
参考题解
由于整个数组除了目标数字,其余所有的数都出现了三次,因此可以考虑对于数组中的每一个数如果按位求和的话那么所求的和对3进行取余之后,如果该位的数字都出现了三次,那么余数一定是0,反之如果不是0,那么该位的数字(其实这个数字一定是1)一定是那个单独出现一次的数字所占的位。
步骤:
遍历数字的每一位,因为数据大小不超过整形范围,因此遍历在范围[0,31]即可。
求和:对数组中的每一个数进行右移同时与1进行与运行算,获得当前位数字加入到sum当中。
取余,同时在将和右移i位加入到结果当中。
代码:
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
for (int i = 0; i < 32; i++) {
int sum = 0;
for (int num : nums) {
sum += ((num >>= i) & 1);
}
sum %= 3;
ans += (sum << i);
}
return ans;
}
}
方法四:HashMap记录次数
只讲思路,最开始想到的方法,但是肯定效率特别低,有可能都通不过测试用例,只是提供一种思路。
首先,遍历数组,将数组出现的次数以key为数组中的数字,value为数组出现的次数,存进map后,遍历map,取出value为1的值。