这种题型遇到过好多次了,于是决定把他们收集整理下来
也方便自己记忆hashmap和hashset的常用方法
只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/single-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
这题中十分关键的一点在于有一个数字出现了一次,其他数字都出现了两次。利用这个特性我们很容易想到hashset不重复的特性。我们可以用hashset存放这些数字,如果这个数字第二次出现了的话,那么hashset必然会添加不了,此时我们把这个数字从hashset中删掉,最后hashset只会有一个数字,也就是那个只出现过一次的数字。
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
HashSet<Integer> set = new HashSet<Integer>();
for(int i = 0;i < nums.length;i++){
if (!set.add(nums[i])) {
set.remove(nums[i]);
}
}
for ( int i : set) {
ans = i;
}
return ans;
}
}
数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
题解
这题和上面的是相似的,不过区别在于这题中的数字不一定是只出现两次的,他们可能会出现多次,不过我们此时要找寻的目标和之前不同,上面我们要找的是只出现一次的数字,这次我们要找的是出现了一次以上的数字,所以我们仍然可以用hashset来完成,只要hashset中存放不了的数字就说明这个数字出现了两次,我们直接返回这个数字就好。
class Solution {
public int findRepeatNumber(int[] nums) {
int ans = 0;
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if (!set.add(nums[i])) {
ans = nums[i];
break;
}
}
return ans;
}
}
当然这题也可以用hashmap来做,不过效率会比hashset更低
class Solution {
public int findRepeatNumber(int[] nums) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for (int i = 0; i < nums.length; i++) {
int count = map.getOrDefault(nums[i], 0);
count++;
map.put(nums[i], count);
}
for (int i : map.keySet()) {
int num = map.get(i);
if (num != 1) {
return i;
}
}
return -1;
}
}
此外根据题意所有数字都在 0~n-1 的范围内的特性,还有一种更加巧妙的做法,不过不通用,只是适用于这个题目。
原地置换法,但是对于这道题来说,这是最好的方法了吗?显然不是,因为我们并没有利用题目给出的所有条件,题目中显示nums里所有的数字都在0-n-1的范围内,如果说没有这个条件,显然方法二在大多数情况下就是最好的方法,但是有了这个条件,就可以一个更神奇的方法,原地置换法,大概的思路就是,因为给定的数组是0-n-1这个范围内,因而比如nums[0]假设是2,那么我们可以放到2这个位置,这样,一旦遇到重复的数字,那么在同一个位置就会发生碰撞,我们就可以检测出重复的数字,利用这巧妙的方法,时间复杂度为o(n),而空间复杂度为o(1)
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
//原地置换法
int n=nums.size();
int i=0;
while(i<n){
//如果nums[i]==i,就说明已经下标和数字匹配,就向下面寻找不匹配的值
if(nums[i]==i){
i++;
continue;
}
else
{
//检测是否发生碰撞,碰撞就说明两者有重复的数字,直接返回重复数字,如果没有碰撞,就把该数字放到其应该放置的位置
if(nums[nums[i]]==nums[i])
return nums[i];
else
swap(nums[i],nums[nums[i]]);
}
}
return 0;
}
};
作者:i2everent-nashvlm
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/jian-zhi-offerdi-yi-pian-ti-jie-liang-ch-cd8x/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例:
s = “abaccdeff”
返回 “b”
s = “”
返回 " "
限制:
0 <= s 的长度 <= 50000
题解
这题和前面不同的地方在于它没有说明除了出现一次的字符之外的字符会出现几次,此外他让我们求的也不是第一个出现多次的字符,而是第一个出现一次的字符,所以这题不能使用hashset来做。
我们使用hashmap来完成这题,下面的写法巧妙的地方在于,map的value使用了boolean,如果出现过不止一次就设定为false,只出现过一次就会设定为true。之所以会这样是因为containskey方法判定了map中是否有这个key,如果有就说明在之前的遍历中已经有key这个字符了,所以我们才会把他put进map中,这时候我们就要设定这个key的value为false,所以我们把containskey的值再取反。
如果这个字符是第一次出现,那么containskey的结果会是false,再取反之后就是true了,所以我们put的时候就会给这个只出现一次的字符的value设定为true。
class Solution {
public char firstUniqChar(String s) {
HashMap<Character,Boolean> map = new HashMap<>();
char[] chara = s.toCharArray();
for (char c : chara) {
map.put(c, !map.containsKey(c));
}
for (char c : chara) {
if (map.get(c)) {
return c;
}
}
return ' ';
}
}
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。