LeetCode 454.四数相加II
题目链接:454.四数相加II
解题思路
分析题目可知:
- 这道题需要快速在数组中查找出满足题目条件的所有可能,我们需要使用哈希表。
- 计算有多少个元组 (i, j, k, l) ,我们不需要输出具体的位置
- nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0,我们将这个条件转换为nums1[i] + nums2[j] == -( nums3[k] + nums4[l])。
思路一(哈希表的应用—map)
我可以将nums1[i] + nums2[j] 和nums3[k] + nums4[l]分开进行遍历,将nums1[i] + nums2[j] 放进map(key = nums1[i] + nums2[j] ,value = 出现的次数),然后遍历-(nums3[k] + nums4[l]),再在map中查找,在对查找出来的次数进行累加。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int sum = 0;
Map<Integer, Integer> map1 = new HashMap<Integer, Integer>();
Map<Integer, Integer> map2 = new HashMap<Integer, Integer>();
for(int i = 0; i < nums1.length; i++){
for(int j = 0; j < nums2.length; j++){
map1.put(nums1[i] + nums2[j], map1.getOrDefault(nums1[i] + nums2[j], 0) + 1);
}
}
for(int i = 0; i < nums3.length; i++){
for(int j = 0; j < nums4.length; j++){
if(map1.containsKey(-(nums3[i] + nums4[j]))){
sum = sum + map1.get(-(nums3[i] + nums4[j]));
}
}
}
return sum;
}
}
LeetCode 383. 赎金信
题目链接:383. 赎金信
解题思路
分析题目:
- 这道题需要快速找到每个字母进行比较,我需要使用哈希表来解题
- ransomNote能不能由magazine里面的字符构成,则ransomNote的字母组成都是有magazine组成。
- magazine 中的每个字符只能在 ransomNote 中使用一次,我们需要考虑字母出现的次数,选择哈希表的数据结构:set集合无法使用。
- ransomNote和 magazine由小写英文字母组成,我们将ransomNote和magazine转换为char,因为小写字母ASCII是连续的,长度固定为26,可以使用数组。
思路一(哈希表的应用—map)
我们使用map作为我们的哈希表的数据结构,我们由题意可知ransomNote必须全由magazine的字母组成,所以将ransomNote存入map(字母,出现的次数),然后遍历magazine遍历对map进行查询如果存在相同字母就将该字母的次数减一,如果该字母的次数小于零或不存在该字母都不符合题意,输出false。
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length()){
return false;
}
Map<Character, Integer> map = new HashMap<Character, Integer>();
for(int i = 0; i < magazine.length(); i++){
char c = magazine.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
for(int i = 0; i < ransomNote.length(); i++){
char s = ransomNote.charAt(i);
if(!map.containsKey(s)||map.get(s) == 0){
return false;
}
map.put(s, map.get(s) - 1);
}
return true;
}
}
思路二(哈希表的应用—数组)
与思路一不同,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!因为两个字符串都是由小写字母构成,我们可以设置一个长度为26的数组,每个索引代表一个字母(通过ASCII码实现),其他步骤和map的解题思路后面大致一样。
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] res = new int[26];
for(char c : magazine.toCharArray()){
res[c -'a']++;
}
for(char s : ransomNote.toCharArray()){
res[s -'a']--;
if(res[s -'a'] < 0){
return false;
}
}
return true;
}
}
LeetCode 15. 三数之和
题目链接:15. 三数之和
解题思路
思路一(双指针法)
- 我们首先应该将nums进行排序。
- a:我们将第一个遍历作为a的选取,但是我们需要对a进行去重,a[i] != a[i - 1],前提时i > 1。
b和c:我们设置两个指针分别为left,right,left = a + 1, right = nums.length - 1,然后让两个指针进行移动,分以下三种情况: 如果nums[i] + nums[left] + nums[right] > 0,我们将right往左移,如果nums[i] + nums[left] + nums[right] < 0,让left往右移,如果等于则存入list,我们还需要对b和c去重,我们在对进行移动时对left和right进行判断如果nums[right] == nums[right - 1],就让right一值移动到不是重复值的后面,left也同理进行去重。
步骤如下图:
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){
return list;
}
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
int left = i + 1;
int right = nums.length - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] > 0){
right--;
}else if(nums[i] + nums[left] + nums[right] < 0){
left++;
}else{
list.add(Arrays.asList(nums[i], nums[left], nums[right]));
while(right > left && nums[right] == nums[right - 1]){
right--;
}
while(right > left && nums[left] == nums[left + 1]){
left++;
}
right--;
left++;
}
}
}
return list;
}
}
思路二(哈希表的应用—set)(不推荐)
上图展示了解题思路
- 如果开头大于0,a就会往左移,如果a与a的前一个值相等,一直移动到与前一个值不相等的地方开始遍历b
- 如果set中没有-(a+b)则将b放入到set集合中,如果由就将abc移入list中。
- b移动的规则,如果移动到b > a + 2的位置时我要考虑b前两个位置是不是都与自己相等,如果相等就一直移动到与前两个值有至少有一个不相等的地方(-1 ,0,1,1,2)
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){
return list;
}
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
Set<Integer> set = new HashSet<Integer>();
for(int j = i + 1; j < nums.length; j++){
if(j > i + 2 && nums[j] == nums[j - 1] && nums[j - 1] == nums[j - 2]){
continue;
}
int c = 0 - (nums[i] + nums[j]);
if(set.contains(c)){
list.add(Arrays.asList(nums[i], nums[j], c));
set.remove(c);
}else{
set.add(nums[j]);
}
}
}
return list;
}
}
LeetCode 18. 四数之和
题目链接:18. 四数之和
解题思路
思路一(双指针法)
思路与LeetCode 15. 三数之和的思路一是一样的,只是在其基础上加入一个for(){}。
需要注意几点:
- 这道题是四数相加之和等于target,所以我们不能使用,num[n] > targrt(num[n]代表排序后开头的数),如果target = -4 ,num[] = {-5,-1-1,0,0,1},所以我们只能写当num[n] > target,且num[n] > 0,直接输出list,其他的地方没有什么不太一样的,照葫芦画瓢就可以实现。
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
for(int n = 0; n < nums.length; n++){
if(nums[n] > target && nums[n] > 0){
return list;
}
if(n > 0 && nums[n] == nums[n - 1]){
continue;
}
for(int i = n + 1; i < nums.length; i++){
if(i > n + 1 && nums[i] == nums[i - 1]){
continue;
}
int left = i + 1;
int right = nums.length - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] + nums[n] > target){
right--;
}else if(nums[i] + nums[left] + nums[right] + nums[n] < target){
left++;
}else{
list.add(Arrays.asList(nums[i], nums[left], nums[right], nums[n]));
while(right > left && nums[right] == nums[right - 1]){
right--;
}
while(right > left && nums[left] == nums[left + 1]){
left++;
}
right--;
left++;
}
}
}
}
return list;
}
}