文章目录
1 哈希表理论
哈希表其实就是存储的数据都带有索引的表格,常见的哈希表有数组、集合以及映射。哈希表最常见的问题叫作哈希碰撞,实际上就是同一个索引对应了多个值。
【哈希表一般用来快速判断一个元素是否在集合里】
解决哈希碰撞的方法有拉链法:将碰撞的数据存储在一个链表中
和线性探测法:从哈希表上找一个空位来存储碰撞的数据,前提是哈希表的大小要大于数据所需要存储的数据数目的大小。
2 有效的字母异或位
class Solution {
public boolean isAnagram(String s, String t) {
int[] hashRecord = new int[26];
for (int i = 0; i< s.length(); i ++) {
hashRecord[s.charAt(i) - 'a'] ++; //ASCII码转字符 将对应哈希表位置索引加1
}
for (int i = 0; i < t.length(); i ++) { //将对应哈希表位置索引减1
hashRecord[t.charAt(i) - 'a'] --;
}
for (int count : hashRecord) { //遍历哈希表
if (count != 0) {
return false;
}
}
return true;
}
}
3. 赎金信 (判断一个字符串是不是可以由另一个字符串的某些字符组成)
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] hashRecord = new int[26];
for (char c : magazine.toCharArray()) { //哈希表记录magazine中的字母索引 转换为字符数组
hashRecord[c - 'a'] ++;
}
for (char c : ransomNote.toCharArray()) {
hashRecord[c - 'a'] --;
}
for (int record : hashRecord) { //如果不能构成的话会出现-1的情况
if (record < 0) {
return false;
}
}
return true;
}
}
4. 两个数组的交集
如果用数组来做哈希表的话,很可能会浪费空间。这里求交集要求输出是不重复的,因此使用集合做哈希表能够很好解决这个问题。
哈希表转换为数组的函数为:reSet.stream().mapToInt(x->x).toArray();
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0]; //这种情况下返回一个空数组
}
Set<Integer> set1 = new HashSet<>(); //创建一个哈希集合 存放第一个数组元素
Set<Integer> reSet = new HashSet<>(); //存放交集
//遍历第一个数组并放入集合1中
for (int num : nums1) {
set1.add(num);
}
//遍历第二个数组,判断其中的元素是否包含在第一个集合中
for ( int num : nums2) {
if (set1.contains(num) ) { //包含
reSet.add(num);
}
}
//第一种方法:接下来将reSet集合转换为数组
// return reSet.stream().mapToInt(x -> x).toArray(); //映射为数组,映射规则为x->x
//第二种方法:另外申请一个数组存放reSet中的元素,并返回数组
int[] arr = new int[reSet.size()];
int i = 0;
for (int re : reSet) {
arr[i] = re;
i ++;
}
return arr;
}
}
5. 快乐数:一个正整数,将它的每一位平方和,再将结果不断平方和,最终结果为1的话就是快乐数,否则不是。用set
class Solution {
public boolean isHappy(int n) {
Set<Integer> record = new HashSet<>(); //构造一个哈希表
while ( n != 1 && !record.contains(n)) { //如果哈希表中不存在这个和就添加到哈希表中,直到这个数为1就截止
record.add(n);
n = getSum(n);
}
return n == 1;
}
private int getSum(int n) {
int temp = 0;
int sum = 0;
while (n > 0) {
temp = n % 10; //个位
n = n / 10; //个位之前的所有数
sum += temp * temp; //计算平方和
}
return sum;
}
}
6. 两数之和:寻找数组中两个数的和为target的数组索引 用map
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2]; //用于存放返回的数组下标
if (nums == null || nums.length == 0) {
return nums;
}
Map<Integer, Integer> map = new HashMap<>(); //构造一个map哈希表
//遍历数组,从哈希表中找是否与数组的某一个元素和为target的元素
for (int i = 0; i < nums.length; i ++) {
int curNum = target - nums[i];
//如果哈希表中的key与当前值相等
if (map.containsKey(curNum)) {
res[0] = map.get(curNum); //获得value值
res[1] = i;
break;
}
map.put(nums[i],i); //找不到匹配的就将当前的数组值和索引放入map中
}
return res;
}
}
7. 四数相加:map
实例描述:给定四个包含整数的数组列表 A , B , C , D 。计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int count = 0; //记录元组的个数
//遍历前两个数组,将它们的和与和出现的次数放到map里面
for (int i : nums1) {
for (int j : nums2) {
int temp = i + j;
if (map.containsKey(temp)) {
map.put(temp, map.get(temp) + 1); //如果map里面有包含这个数,就将这个数出现的次数加1
}
else{
map.put(temp, 1); //没有的话次数就确定为1
}
}
}
//遍历后面两个数组 用0减去这两个数的和 如果在map中能找到相等的就提取map中两个和出现的次数
for (int i : nums3) {
for (int j : nums4) {
int temp = 0 - i - j;
if (map.containsKey(temp)) { //如果存在相等的
count += map.get(temp);
}
}
}
return count;
}
}
8. 三数之和:双指针法
从一个数组中找出和为0的所有三元组集合
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<>(); //存储三元组
Arrays.sort(nums); //先排序
for (int i = 0; i < nums.length; i ++) {
if (nums[i] > 0) { //如果当前元素已经大于0,就没有必要继续往后了
return list;
}
if (i > 0 && nums[i] == nums[i - 1]) { //如果a有重复的话就跳过本次循环,从下一个a开始继续
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum < 0) {
left ++;
}
else if (sum > 0){
right --;
}
else{
list.add( Arrays.asList(nums[i], nums[left], nums[right])); //相等的时候
while (right > left && nums[left] == nums[left + 1]) left ++; //对b去重
while (right > left && nums[right] == nums[right - 1]) right --; //对c去重
right --;
left ++;
}
}
}
return list;
}
}
9. 四数之和:双指针法
找出数组中和为target的四个元素
【涉及到去重的话双指针会更好处理一点】
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums); //先从小到大排序
for (int i = 0; i < nums.length; i ++) {
/*如果mums[i] < 0 的话,尽管nums[i] > target
那么其后的四个数加起来仍然有可能小于target
*/
if (nums[i] > 0 && nums[i] > target) { //当前已经大于target了,就从下一个元素开始
return list;
}
//a元素去重
if ( i > 0 && nums[i] == nums[i - 1]) {
continue;
}
//三数之和
for (int j = i + 1; j < nums.length; j ++) {
//b元素去重
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (left < right) {
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum < target) {
left ++;
}
else if(sum > target) {
right --;
}
else{
list.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); //相等就加入到list列表中
//对c,d去重
while (right > left && nums[left] == nums[left + 1]) left ++;
while (right > left && nums[right] == nums[right - 1]) right --;
left ++;
right --;
}
}
}
}
return list;
}
}