/*
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<Integer> hashset = new HashSet();//这个地方设置存储Integer类型的对象
HashSet<Integer> 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<Integer> hashset = new HashSet();//这个地方设置存储Integer类型的对象
HashSet<Integer> 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<Integer, Integer> 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<Integer> 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<Integer, Integer> 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<Integer, Integer> 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刷题Day09-哈希表
最新推荐文章于 2024-06-12 10:00:23 发布