【12.18】————————————————————————————————————————————
【第一题】只出现一次的数字
分析:不需要额外空间的方法,就往位运算上想。
//异或
//执行用时:1 ms, 在所有 Java 提交中击败了99.91% 的用户
//内存消耗:38.6 MB, 在所有 Java 提交中击败了80.24% 的用户
class Solution{
public int SingleNumber(int[] nums){
int single = 0;
for(int num:nums){
single^=num;
}
return single;
}
}
//哈希表
//执行用时:14 ms, 在所有 Java 提交中击败了14.51% 的用户
//内存消耗:39 MB, 在所有 Java 提交中击败了44.07% 的用户
class Solution{
public int singleNumber(int[] nums){
Map<Integer,Integer> map = new HashMap<>();
for(Integer i:nums){
Integer count = map.get(i);
count = count == null ? 1:++count;
map.put(i,count);
}
for(Integer i:map.keySet()){
Integer count = map.get(i);
if(count == 1){return i;}
}
return -1;
}
}
//哈希集
public static int SingleNumber(int[] nums){
HashSet<int> set = new HashSet<int>();
for(int i = 0;i < nums.length;i++){
//假如之前添加过,那么第二次出现的时候将之前放入的移除,此时出现两次的元素全部被移除
if(!set.Add(nums[i])){
set.Remove(nums[i]);
}
}
//只剩下唯一一个出现一次的元素。
return set.First();
}
//先排序,比较前后元素是否相等
class Solution{
public int singleNumber(int[] nums){
Arrays.sort(nums);
for(int i = 0;i < nums.length;i+=2){
if(nums[i]!=nums[i+1]){
return nums[i];
}
}
return nums[nums.length-1];
}
}
【第二题】两句话中的不常见单词
分析:哈希表。
为何我总能想到复杂度超高的方法?orz
//执行用时:7 ms, 在所有 Java 提交中击败了21.05% 的用户
//内存消耗:38.7 MB, 在所有 Java 提交中击败了20.23% 的用户
class Solution {
public String[] uncommonFromSentences(String A, String B) {
String con = A+" "+B;
String[] arr1 = con.split(" ");
StringBuffer buf = new StringBuffer();
Map<String,Integer> map = new HashMap<>();
for(String i:arr1){
Integer count = map.get(i);
count = count == null?1:++count;
map.put(i,count);
}
for(String j:map.keySet()){
Integer count = map.get(j);
if(count == 1){
buf.append(j).append(" ");
}
}
if(buf.toString().length() >0){
return buf.toString().trim().split(" ");
}else{
return new String[buf.length()];
}
}
}
//执行用时:6 ms, 在所有 Java 提交中击败了26.96% 的用户
//内存消耗:38.7 MB, 在所有 Java 提交中击败了20.66% 的用户
class Solution{
public String[] uncommonFromSentences(String A,String B){
Map<String,Integer> count = new HashMap();
String s = A+" "+B;
for(String word:s.split(" "))
count.put(word,count.getOrDefault(word,0)+1);
List<Stirng> ans = new LinkedList();
for(String word:count.keySet()){
if(count.get(word) == 1)
ans.add(word);
}
return ans.toArray(new String[ans.size()]);
}
}
【第三题】拼写单词
分析:
第一种方法:26个桶。
第二种方法,用两个哈希表,分别记录chars的各个字母个数,以及words中各个字符串的各个字母个数,比较个数是否够,够的话就加在总长度上。
//执行用时:8 ms, 在所有 Java 提交中击败了69.38% 的用户
//内存消耗:39 MB, 在所有 Java 提交中击败了65.17% 的用户
class Solution {
public int countCharacters(String[] words, String chars) {
int[] arr = new int[26];
int sum = 0;
for(int i = 0;i < chars.length();i++){
arr[chars.charAt(i)-'a']++;
}
for(int j = 0;j < words.length;j++){
int[] arrCopy = new int[26];
System.arraycopy(arr,0,arrCopy,0,arr.length);
int k;
for(k = 0;k < words[j].length();k++){
if(arrCopy[words[j].charAt(k)-'a']==0){
break;
}else{
arrCopy[words[j].charAt(k)-'a']--;
}
}
if(k >=words[j].length()){
//System.out.println(words[j]);
sum+=words[j].length();
}
}
return sum;
}
}
class Solution {
public int countCharacters(String[] words, String chars) {
Map<Character, Integer> charsCnt = new HashMap<Character, Integer>();
int length = chars.length();
for (int i = 0; i < length; ++i) {
char c = chars.charAt(i);
charsCnt.put(c, charsCnt.getOrDefault(c, 0) + 1);
}
int ans = 0;
for (String word : words) {
Map<Character, Integer> wordCnt = new HashMap<Character, Integer>();
int wordLength = word.length();
for (int i = 0; i < wordLength; ++i) {
char c = word.charAt(i);
wordCnt.put(c, wordCnt.getOrDefault(c, 0) + 1);
}
boolean isAns = true;
for (int i = 0; i < wordLength; ++i) {
char c = word.charAt(i);
if (charsCnt.getOrDefault(c, 0) < wordCnt.getOrDefault(c, 0)) {
isAns = false;
break;
}
}
if (isAns) {
ans += word.length();
}
}
return ans;
}
}
//执行用时:63 ms, 在所有 Java 提交中击败了19.41% 的用户
//内存消耗:39.1 MB, 在所有 Java 提交中击败了55.94% 的用户
public int countCharacters(String[] words, String chars) {
int ans=0;
//统计chars中字母出现的次数
Map<Character,Integer> map=new HashMap<>();
for(char c:chars.toCharArray())
map.put(c, map.getOrDefault(c, 0)+1);
for(String word:words) {
boolean can=true;
//统计word中字母出现的次数
Map<Character,Integer> letterCount=new HashMap<>();
for(char c:word.toCharArray()) {
letterCount.put(c, letterCount.getOrDefault(c, 0)+1);
}
//判断可否拼写该单词
for(char c:word.toCharArray()) {
if(!map.containsKey(c)||map.get(c)<letterCount.get(c)) {
can=false;
break;
}
}
if(can)
ans+=word.length();
}
return ans;
}
【第四题】快乐数
分析:这个题就变成了判断是否有环的问题。
判断是否有环——1.假如新进入的数字原本已经存在;2.快慢指针
//哈希集合判重
class Solution{
private int getNext(int n){
int totalSum = 0;
//第一步,将数字各个位分解
while(n > 0){
int d = n%10;
n = n/10;
totalSum +=d*d;
}
return totalSum;
}
public boolean isHappy(int n){
Set<Integer> seen = new HashSet<>();
while(n!=1 && !seen.contains(n)){
seen.add(n);
n = getNext(n);
}
return n == 1;
}
}
//快慢指针断环,快指针每次走两步,慢指针每次走一步,他俩无论从哪个位置开始,一定会在某个位置相遇
//执行用时:1 ms, 在所有 Java 提交中击败了99.81% 的用户
//内存消耗:35.4 MB, 在所有 Java 提交中击败了76.77% 的用户
class Solution{
public int getNext(int n){
int totalSum = 0;
//第一步,将数字各个位分解
while(n > 0){
int d = n%10;
n = n/10;
totalSum +=d*d;
}
return totalSum;
}
public boolean isHappy(int n){
int slowRunner = n;
int fastRunner = getNext(n);
while(fastRunner !=1 && slowRunner !=fastRunner){
slowRunner = getNext(slowRunner);
fastRunner = getNext(getNext(fastRunner));
}
return fastRunner == 1;
}
}
12.19_________________________________________________________________________
【第五题】计数质数
分析:我一看,这么简单的题,我啪的一下就提交了,直接超时…
//埃氏筛:假如质数是x,那么把x*x,x(x+1)…标记成合数,也就是说将x的倍数排除。
线性筛:对埃氏筛进行优化,优化的目标是将每个合数只被标记一次,保证线性的时间复杂度。
//超时版本
class Solution {
public int countPrimes(int n) {
int count = 0;
for(int i = 0;i < n;i++){
if(isPrime(i)){
System.out.println(i);
count++;
}
}
return count;
}
public boolean isPrime(int a){
if(a == 0 || a == 1){return false;}
for(int i = 2;i <= Math.sqrt(a);i++){
if(a % i == 0){
return false;
}
}
return true;
}
}
//埃氏筛:
//执行用时:27 ms, 在所有 Java 提交中击败了44.73% 的用户
//内存消耗:42.5 MB, 在所有 Java 提交中击败了33.17% 的用户
class Solution{
public int[] countPrimes(int n){
int[] isPrime = new int[n];
Arrays.fill(isPrime,1);
int ans = 0;
for(int i = 2;i < n;i++){
if(isPrime[i] == 1){
ans+=1;
if((long)i * i < n){
//x*x x(x+1) x(x+2)
for(int j = i*i;j < n;j+=i){
isPrime[j] = 0;
}
}
}
}
return ans;
}
}
class Solution{
public int countPrimes(int n){
List<Integer> primes = new ArrayList<Integer>();
int[] isPrime = new int[n];
Arrays.fill(isPrime,1);
for(int i = 2;i < n;i++){
if(isPrime[i] == 1){
primes.add(i);
}
for(int j = 0;j < primes.size() && i * primes.get(j) < n;++j){
isPrime[i*primes.get(j)] = 0;
if(i % primes.get(j) == 0){
break;
}
}
}
return primes.size();
}
}
【第六题】两个数组的交集II
分析:题意就是求交集。
建立哈希表,记录每个key出现的次数。然后扫描另一个数组,假如他的元素在映射表可以找到,那么比较value,相等的话将其加入buffer,否则将一个元素加入。最后将buf转array。
//执行用时:16 ms, 在所有 Java 提交中击败了5.09% 的用户
//内存消耗:38.8 MB, 在所有 Java 提交中击败了55.34% 的用户
class Solution{
public int[] intersect(int[] nums1,int[] nums2){
//存放列表1中的元素
List<Integer> list1 = new ArrayList<>();
for(int num:nums1){
list1.add(num);
}
//存放交集元素
List<Integer> list2 = new ArrayList<>();
for(int num:nums2){
if(list1.contains(num)){
list2.add(num);
//匹配一次之后将数字从list1中删除
list1.remove(Integer.valueOf(num));
}
}
//将存放交集元素的list2转换成数组
int[] res = new int[list2.size()];
int i = 0;
for(int num:list2){
res[i++] = num;
}
return res;
}
}
//利用映射实现
执行用时:3 ms, 在所有 Java 提交中击败了79.80% 的用户
内存消耗:38.6 MB, 在所有 Java 提交中击败了76.34% 的用户
class Solution{
public int[] intersect(int[] nums1,int[] nums2){
//将列表1中的元素和频次都放入映射中
Map<Integer,Integer> map = new HashMap<>(nums1.length);
int i = 0;
for(int num:nums1){
Integer count = map.get(num);
if(count == null){
map.put(num,1);
}else{
map.put(num,++count);
}
//map.put(nums1[i++],map.getOrDefault(i,1)+1);
}
//list存放交集
List<Integer> list = new ArrayList<>();
for(int num:nums2){
//获取映射中该数值出现的频次
Integer count = map.get(num);
if(count != null && count!=0){
list.add(num);
map.put(num,--count);
}
}
int[] res = new int[list.size()];
for(int j= 0;j < list.size();j++){
res[j] = list.get(j);
}
return res;
}
}
【第七题】第k个缺失的正整数
分析:开辟一个空间为2500的桶
//执行用时:1 ms, 在所有 Java 提交中击败了61.42% 的用户
//内存消耗:38.3 MB, 在所有 Java 提交中击败了61.18% 的用户
class Solution {
public int findKthPositive(int[] arr, int k) {
int count = 0;
int[] map = new int[2500];
for(int i = 0;i < arr.length;i++){
map[arr[i]] = arr[i];
}
int j = 1;
while(j < map.length){
if(count <k){
if(map[j]!=0){
j++;
}else{
j++;
++count;
//System.out.println(j);
}
}else{
return j-1;
}
}
return -1;
}
}
【第八题】验证外星语词典
分析:意思就是说,words里的单词的排序要根据order来排。
1.难点就在于如何利用order对words中的字母进行比较先后。
:利用索引下标。
class Solution{
public boolean isAlienSorted(String[] words,String order){
//建立大小为26的数组,根据字母来存放其在order的下标
int[] index = new int[26];
for(int i = 0;i < order.length();++i){
index[order.charAt(i) - 'a'] = i;
}
search:for(int i = 0;i < words.length-1;++i){
String word1 = words[i];
String word2 = words[i+1];
for(int k = 0;k < Math.min(word1.length(),word2.length());++k){
//找到第一个不相同的字符
if(word1.charAt(k) != word2.charAt(k)){
if(index[word1.charAt(k)-'a'] > index[word2.charAt(k)-'a'])
return false;
continue search;
}
}
if(word1.length() > word2.length())
return false;
}
return true;
}
}
public boolean isAlienSorted(String[] words, String order) {
int[] index = new int[26];
for (int i = 0; i < order.length(); ++i)
index[order.charAt(i) - 'a'] = i;
search: for (int i = 0; i < words.length - 1; ++i) {
String word1 = words[i];
String word2 = words[i+1];
boolean continueCompare = true;
// Find the first difference word1[k] != word2[k].
for (int k = 0; k < Math.min(word1.length(), word2.length()) && continueCompare;
k++) {
if (word1.charAt(k) != word2.charAt(k)) {
// If they compare badly, it's not sorted.
if (index[word1.charAt(k) - 'a'] > index[word2.charAt(k) - 'a'])
return false;
else //已经比较出高下了
continueCompare = false;
}
}
// If we didn't find a first difference, the
// words are like ("app", "apple").
if (continueCompare && word1.length() > word2.length())
return false;
}
return true;
}
【第九题】找不同
分析:
方法一:设置两个哈希表,分别记录两个字符串中字母的个数,根据相同的key比较value。
方法二:设置列表,访问两个字符串(将两个字符串拼在一起),不存在的字符加入列表,已存在的再次碰到则删除,最后剩下的那个就是新添加的。
方法三:异或。
//执行用时:10 ms, 在所有 Java 提交中击败了15.66% 的用户
//内存消耗:37 MB, 在所有 Java 提交中击败了40.56% 的用户
class Solution {
public char findTheDifference(String s, String t) {
Map<Character,Integer> map1 = new HashMap<>();
Map<Character,Integer> map2 = new HashMap<>();
if(s.length() == 0){return t.charAt(0);}
for(char c:s.toCharArray()){
map1.put(c,map1.getOrDefault(c,0)+1);
}
for(char c:t.toCharArray()){
map2.put(c,map2.getOrDefault(c,0)+1);
}
for(Character c:map2.keySet()){
if(map1.getOrDefault(c,0) == 0){
return c;
}else{
if(map1.get(c) != map2.get(c)){
return c;
}
}
}
return ' ';
}
}
//执行用时:6 ms, 在所有 Java 提交中击败了16.30% 的用户
//内存消耗:38.4 MB, 在所有 Java 提交中击败了5.03% 的用户
class Solution{
public char findTheDifference(String s, String t) {
HashSet<Character> set = new HashSet<Character>();
String con = s+t;
for(char c:con.toCharArray()){
if(!set.add(c)){
set.remove(c);
}
}
for(Character c:set){
return c;
}
//return set.toString();
return 0;
}
}
//执行用时:3 ms, 在所有 Java 提交中击败了44.67% 的用户
//内存消耗:36.7 MB, 在所有 Java 提交中击败了82.89% 的用户
class Solution{
public char findTheDifference(String s,String t){
int ret = 0;
for(int i = 0;i < s.length();++i){
ret ^= s.charAt(i);
}
for(int i = 0;i < t.length();++i){
ret ^=t.charAt(i);
}
return (char)ret;
}
}
//————————————————————————————————————————————————————————————
//执行用时:3 ms, 在所有 Java 提交中击败了44.67% 的用户
//内存消耗:36.9 MB, 在所有 Java 提交中击败了58.63% 的用户
class Solution{
public char findTheDifference(String s, String t) {
int single = 0;
String con = s+t;
for(Character c:con.toCharArray()){
single ^= c;
}
return (char)single;
}
}
【第十题】错误的集合
分析:
方法1:直接26个桶,将数字映射进之后,找到value = 2,和value=0的key。
方法2:额外数组。数组索引代表数字,数组值代表数字出现次数。
方法3:排序后,判断相邻两个数字是否只差1就可找到缺失数字。
方法4:一个重复数字/字符——>异或!!!!
方法5:哈希。
//执行用时:2 ms, 在所有 Java 提交中击败了92.45% 的用户
//内存消耗:39.9 MB, 在所有 Java 提交中击败了76.32% 的用户
class Solution{
public int[] findErrorNums(int[] nums){
int[] map = new int[nums.length+1];
int[] ret = new int[2];
for(int i = 0;i < nums.length;i++){
if(map[nums[i]] == 0){
map[nums[i]] = nums[i];
}else{
ret[0] = nums[i];
}
}
for(int i = 1;i <map.length;i++){
if(map[i] == 0){
ret[1] = i;
}
}
return ret;
}
}
public class Solution {
public int[] findErrorNums(int[] nums) {
int[] arr = new int[nums.length + 1];
int dup = -1, missing = 1;
for (int i = 0; i < nums.length; i++) {
arr[nums[i]] += 1;
}
for (int i = 1; i < arr.length; i++) {
if (arr[i] == 0)
missing = i;
else if (arr[i] == 2)
dup = i;
}
return new int[]{dup, missing};
}
}
public class Solution {
public int[] findErrorNums(int[] nums) {
Arrays.sort(nums);
int dup = -1, missing = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1])
dup = nums[i];
else if (nums[i] > nums[i - 1] + 1)
missing = nums[i - 1] + 1;
}
return new int[] {dup, nums[nums.length - 1] != nums.length ? nums.length : missing};
}
}
public class Solution {
public int[] findErrorNums(int[] nums) {
int xor = 0, xor0 = 0, xor1 = 0;
for (int n: nums)
xor ^= n;
for (int i = 1; i <= nums.length; i++)
xor ^= i;
int rightmostbit = xor & ~(xor - 1);
for (int n: nums) {
if ((n & rightmostbit) != 0)
xor1 ^= n;
else
xor0 ^= n;
}
for (int i = 1; i <= nums.length; i++) {
if ((i & rightmostbit) != 0)
xor1 ^= i;
else
xor0 ^= i;
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] == xor0)
return new int[]{xor0, xor1};
}
return new int[]{xor1, xor0};
}
}
public class Solution {
public int[] findErrorNums(int[] nums) {
Map < Integer, Integer > map = new HashMap();
int dup = -1, missing = 1;
for (int n: nums) {
map.put(n, map.getOrDefault(n, 0) + 1);
}
for (int i = 1; i <= nums.length; i++) {
if (map.containsKey(i)) {
if (map.get(i) == 2)
dup = i;
} else
missing = i;
}
return new int[]{dup, missing};
}
}
【第十一题】两个列表的最小索引总和
分析:找出两个字符串数组的共同字符串,并且索引和最小。双指针。
难点:如何根据索引找到值,因为答案可能不止一个,说明一个索引可能对应多个值。用数组不行,用哈希不行
如何保存所有下标和相同的字符串对。
方法一:哈希表。key为索引和,value为字符串列表;(好慢好慢好慢)
方法二:遍历下标和。升序遍历,同时只需要遍历一个list,另一个list的下标直接可以由sum-i获得。一旦有匹配,将其加入字符串数组。
方法三:哈希表,只为一个表建立哈希条目。结果字符串数组只保留下标和为min的字符串。(最快!)
//执行用时:112 ms, 在所有 Java 提交中击败了16.87% 的用户
//内存消耗:38.9 MB, 在所有 Java 提交中击败了92.83% 的用户
public class Solution{
public String[] findRestaurant(String[] list1,String[] list2){
HashMap<Integer,List<String>> map = new HashMap<>();
for(int i= 0;i < list1.length;i++){
for(int j = 0;j < list2.length;j++){
if(list1[i].equals(list2[j])){
if(!map.comtainsKey(i+j))
map.put(i+j,new ArrayList<String>());
map.get(i+j).add(list1[i]);
}
}
}
int min_index_sum = Integer.MAX_VALUE;
for(int key:map.keySet())
min_index_sum = Math.min(min_index_sum,key);
String[] res = new String[map.get(min_index_sum).size()];
return map.get(min_index_sum).toArray(res);
}
}
//执行用时:57 ms, 在所有 Java 提交中击败了22.02% 的用户
//内存消耗:38.9 MB, 在所有 Java 提交中击败了95.22% 的用户
public class Solution {
public String[] findRestaurant(String[] list1, String[] list2) {
List < String > res = new ArrayList < > ();
for (int sum = 0; sum < list1.length + list2.length - 1; sum++) {
for (int i = 0; i <= sum; i++) {
if (i < list1.length && sum - i < list2.length && list1[i].equals(list2[sum - i]))
res.add(list1[i]);
}
if (res.size() > 0)
break;
}
return res.toArray(new String[res.size()]);
}
}
//执行用时:8 ms, 在所有 Java 提交中击败了92.18% 的用户
//内存消耗:39.1 MB, 在所有 Java 提交中击败了78.48% 的用户
public class Solution {
public String[] findRestaurant(String[] list1, String[] list2) {
HashMap < String, Integer > map = new HashMap < String, Integer > ();
for (int i = 0; i < list1.length; i++)
map.put(list1[i], i);
List < String > res = new ArrayList < > ();
int min_sum = Integer.MAX_VALUE, sum;
for (int j = 0; j < list2.length && j <= min_sum; j++) {
if (map.containsKey(list2[j])) {
sum = j + map.get(list2[j]);
if (sum < min_sum) {
res.clear();
res.add(list2[j]);
min_sum = sum;
} else if (sum == min_sum)
res.add(list2[j]);
}
}
return res.toArray(new String[res.size()]);
}
}