代码随想录leetcode刷题Day09-哈希表

/*
349. 两个数组的交集
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。
输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
 */

/*
使用排序+双指针的方式处理
先把两个数组进行排序
然后定义两个指针遍历数组
如果元素相同而且之前没有使用过,把这个元素放进答案数组
如果不相同,要比较大小,并且使对应的指针后移
时间复杂度:两个数组排序O(nlogn + mlogm), 之后遍历两个数组O(m + n) --> O(nlogn + mlogm)
空间复杂度:排序需要使用额外空间O(nlogn + mlogm)
 */
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);//先把两个数组进行排序(默认快排)

        int index1 = 0, index2 = 0, index = 0;
        int len1 = nums1.length, len2 = nums2.length;
        int[] ans = new int[len1 + len2];//定义一个数组接收答案(长度设置为两个数组长度之和)
        while (index1 < len1 && index2 < len2) {
            if (nums1[index1] == nums2[index2]) {//如果元素相等,一定会后移指针
                if (index == 0 || nums1[index1] != ans[index - 1]) {//保证不放入相同的元素
                    ans[index] = nums1[index1];
                    index++;
                }
                index1++;
                index2++;
            } else if (nums1[index1] < nums2[index2]) {//元素不相等,根据大小移动两个数组的指针
                index1++;
            } else {
                index2++;
            }
        }
        return Arrays.copyOfRange(ans,0,index);//最后返回一个子数组,后面的0元素都不要了
    }
}

/*
使用Set处理
先遍历nums1,把它的元素存储到HashSet里,因为Set元素不重复,所以消去了重复元素
再遍历nums2,如果HashSet也有对应的元素,说明这个元素是两个数组共用的,就把这个元素放到resSet中
最后把resSet转化为数组(使用到一些Set方法)
时间复杂度O(m + n)
空间复杂度O(m + n)(新创建了两个集合)
 */
class Solution1 {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet hashset = new HashSet();//这个地方设置存储Integer类型的对象
        HashSet resset = new HashSet();

        for (int i = 0; i < nums1.length; i++) {
            hashset.add(nums1[i]);//先把nums1的元素存储
        }
        for (int i = 0; i < nums2.length; i++) {
            if (hashset.contains(nums2[i])){
                resset.add(nums2[i]);//如果nums2中的某个元素,hashset也有,那就是公共元素
            }
        }
        /*
        实现HashSet转int[]的操作
        注意不能直接把HashSet转为int[], toArray方法返回的是Object[]类型数组
        我们需要先用Integer[]接收,再得到每个元素的int,调用intValue方法,得到int[]数组
         */
        Integer[] temparr = resset.toArray(new Integer[]{});
        int[] ans = new int[temparr.length];
        for (int i = 0; i < ans.length; i++) {
            ans[i] = temparr[i].intValue();
        }
        return ans;
    }
}

class Solution2 {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet hashset = new HashSet();//这个地方设置存储Integer类型的对象
        HashSet resset = new HashSet();

        for (int i = 0; i < nums1.length; i++) {
            hashset.add(nums1[i]);//先把nums1的元素存储
        }
        for (int i = 0; i < nums2.length; i++) {
            if (hashset.contains(nums2[i])){
                resset.add(nums2[i]);//如果nums2中的某个元素,hashset也有,那就是公共元素
            }
        }
        /*
        实现HashSet转int[]的操作法二:
         */
        Object[] temparr = resset.toArray();
        int[] ans = new int[temparr.length];
        for (int i = 0; i < temparr.length; i++) {
            ans[i] = (int) temparr[i];
        }
        return ans;
    }
}

/*
350. 两个数组的交集 II
给你两个整数数组nums1 和 nums2 ,请你以数组形式返回两数组的交集。
返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致
(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
 */

/*
排序+双指针
先排序,然后双指针遍历两个数组,遇到相同元素放入ans数组中
时间复杂度:O(mlogm + nlogn)
 */
class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int[] ans = new int[nums1.length + nums2.length];
        int t1 = 0, t2 = 0, index = 0;
        while (t1 < nums1.length && t2 < nums2.length) {
            if (nums1[t1] == nums2[t2]) {
                ans[index++] = nums1[t1];
                t1++;
                t2++;
            } else if (nums1[t1] < nums2[t2]) {
                t1++;
            } else {
                t2++;
            }
        }
        return Arrays.copyOfRange(ans, 0, index);
    }
}

/*
可以使用哈希表来处理,使用HashMap
key存储元素值,value存储元素出现的次数
时间复杂度O(m + n),分别对两个数组进行遍历
空间复杂度O(min(m,n)),创建一个哈希表
 */
class Solution1 {
    public int[] intersect(int[] nums1, int[] nums2) {
        //小技巧:用长度小的数组进行第一次遍历,降低时间复杂度
        if (nums1.length > nums2.length){
            return intersect(nums2,nums1);
        }
        HashMap map = new HashMap<>();//创建一个HashMap,key和value都存储Integer;
        //遍历nums1,key就是遍历的数字num,value要进行判断:
        //如果拿着key找不到value,说明第一次出现,就存储0+1;
        //如果能找到,说明不是第一次出现,就存储value+1(map.get(key) + 1))
        for (int num : nums1) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        int[] ans = new int[nums1.length];
        int index = 0;
        for (int num : nums2){
            if (map.containsKey(num) && map.get(num) > 0){//能找到value而且value大于0
                ans[index++] = num;//存储元素
                map.put(num,map.get(num) - 1);//将value-1后重新存储进map
            }
        }
        return Arrays.copyOfRange(ans,0,index);//返回一个子数组
    }
}

/*
202. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

 */

/*
根据题意,输入一个n后有两种情况,要么经过循环返回1,要么经过循环发现某两次计算返回值相同;
如果返回值相同就false,如果等于1就true
所以需要频繁地判断一个元素是否在集合中,可以考虑使用哈希表,使用HashSet结构

我们使用哈希集合而不是向量、列表或数组的原因是因为
我们反复检查其中是否存在某数字。检查数字是否在哈希集合中需要 O(1)的时间,
而对于其他数据结构,则需要 O(n)的时间。选择正确的数据结构是解决这些问题的关键部分。
时间复杂度O(logn), 计算一个数字的各位平方和需要考虑位数,为logn.
空间复杂度O(logn)
 */
class Solution {
    public boolean isHappy(int n) {
        Set hashset = new HashSet<>();
        while (n != 1 && !hashset.contains(n)){//退出循环有两种情况,等于1或者与之前某次返回值相同
            hashset.add(n);//把新的n加入到集合中
            n = getNextNum(n);//计算下一个n
        }
        return n == 1;//是1返回true,其他数返回false
    }
    private int getNextNum(int n){//给一个n,计算各位平方和(不知道几位数)
        int temp;
        int res = 0;
        while (n > 0){//n = 0时,退出循环
            temp = n % 10;//循环中依次取出各位数字
            res += temp * temp;//计算平方和
            n = n / 10;//n每次后移一位
        }
        return res;//返回下一个n
    }
}

/*
使用链表的思路,来看看这个链表有没有环
如果有环,一定是快指针先入环,最终快指针和慢指针在环中相遇 ---> 返回false
如果没有环,一定是快指针先变为1,后面全为1 ---> 返回true
根据while循环结束的两种情况,返回即可
 */

class Solution1 {
    public boolean isHappy(int n) {
        int f = getNextNum(n);//快指针,直接指向下一个数就行
        int s = n;//慢指针
        while (f != 1 && f != s){
            f = getNextNum(getNextNum(f));//一次走两步
            s = getNextNum(s);//一次走一步
        }
        return f == 1;
    }
    private int getNextNum(int n){
        int temp;
        int res = 0;
        while (n > 0){
            temp = n % 10;
            res += temp * temp;
            n = n / 10;
        }
        return res;
    }
}

/*
1. 两数之和
给定一个整数数组 nums 和一个目标值 target,
请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
 */

/*
方法一:双层for循环遍历
时间复杂度O(n^2)
空间复杂度O(1)
 */
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int sum;
        for (int i = 0; i < nums.length; i++) {//外层循环遍历数组
            for (int j = i + 1; j < nums.length; j++) {//内层循环从i+1开始遍历数组
                sum = nums[j] + nums[i];//计算对应位置元素的和
                if (sum == target){//匹配到了
                    return new int[]{i,j};//返回需要的数组
                }
            }
        }
        return null;
    }
}

/*
方法二:哈希表
因为要频繁查找元素,所以考虑使用哈希表,而不用数组遍历
此题需要返回对应下标,可以考虑使用HashMap, 它可以存放key - value
因为要查找的是元素,HashMap查找key的速度快,所以用key存放元素,value存放下标
时间复杂度O(n)(只对数组遍历一次)
空间复杂度O(n)(存放哈希表)
 */
class Solution1 {
    public int[] twoSum(int[] nums, int target) {

        if (nums == null || nums.length == 0){//简单的情况就不进入循环了
            return null;
        }

        int temp;
        HashMap map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            temp = target - nums[i];//想要查找的值是目标和元素的差
            if (map.containsKey(temp)){//看是否在map中
                return new int[]{map.get(temp),i};//如果在,就直接返回对应的下标数组
            }
            map.put(nums[i],i);//如果不在,记得将这一对更新到map中
        }
        return null;
    }
}

/*
454. 四数相加 II
给定四个包含整数的数组列表 A , B , C , D ,
计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
 */

/*
使用哈希表map,因为要存放key - sum和value - 次数两个内容
先使用两个for循环遍历前两个数组,记录他们对应位置元素之和,放入哈希表map中
注意判断一下map中之前有没有对应的sum,如果有,使得value + 1,没有就0 + 1;
之后使用两个for循环遍历后两个数组,记录temp = -(sum)
然后看temp在map中是否出现过,以及出现过几次,使得count增加
 */
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        HashMap map = new HashMap<>();
        int count = 0;
        for(int i : nums1){
            for (int j : nums2){
                int temp = (i + j);
                if (map.containsKey(temp)){//如果以前有这个和
                    map.put(temp,map.get(temp) + 1);
                }else{
                    map.put(temp,1);//没有这个和value就是1
                }
            }
        }
        for (int i : nums3){
            for (int j : nums4){
                int temp = -(i + j);
                if (map.containsKey(temp)){//如果以前有这个和
                    count += map.get(temp);//count加上以前出现的次数
                }
            }
        }
        return count;
    }
}

你可能感兴趣的:(leetcode,散列表,数据结构)