赎金信
- 题目链接:数组hash典型
- 我的解法:看成源字符串和目标字符串都由大小写字母组成了,记录一下
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int magcnt[] = new int[52];//0~25小写字母,26~51大写字母
for(int i = 0; i < magazine.length(); i++)
{
char c = magazine.charAt(i);
if(c >= 'a' && c <= 'z') magcnt[(c - 'a')]++;
else magcnt[(c - 'A' + 26)]++;
}
for(int i = 0; i < ransomNote.length(); i++)
{
char c = ransomNote.charAt(i);
if(c >= 'a' && c <= 'z')
{
magcnt[(c - 'a')]--;
if(magcnt[(c - 'a')] < 0) return false;
}
else{
magcnt[(c - 'A' + 26)]--;
if(magcnt[(c - 'A' + 26)] < 0) return false;
}
}
return true;
}
}
总结:String类型遍历可以使用str.charAt(i),也可以str.toCharArray()之后用foreach遍历
str.indexOf(c, index)
返回字符c从String下标index开始出现的第一个位置,没有返回-1
- 复杂度:时间复杂度 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)
n数之和
排序-降维至双指针(剪枝&去重)-双指针相向锁定答案(将和与target比较大小)
两数之和
- 题目链接:两数之和返厂
- 双指针解法:
class Solution {
public int[] twoSum(int[] nums, int target) {
int m=0,n=0,k,board=0;
int[] res=new int[2];
int[] tmp1=new int[nums.length];
//备份原本下标的nums数组
System.arraycopy(nums,0,tmp1,0,nums.length);
//将nums排序
Arrays.sort(nums);
//双指针
for(int i=0,j=nums.length-1;i<j;){
if(nums[i]+nums[j]<target)
i++;
else if(nums[i]+nums[j]>target)
j--;
else if(nums[i]+nums[j]==target){
m=i;
n=j;
break;
}
}
//找到nums[m]在tmp1数组中的下标
for(k=0;k<nums.length;k++){
if(tmp1[k]==nums[m]){
res[0]=k;
break;
}
}
//找到nums[n]在tmp1数组中的下标
for(int i=0;i<nums.length;i++){
if(tmp1[i]==nums[n]&&i!=k)
res[1]=i;
}
return res;
}
}
总结:本题记录的二元组由下标组成,所以哈希表实现的方式更简单。
三数之和
- 题目链接:三数之和
- 我的解法:看成三元组存的是不重复下标了,还是想降维的思想用不了了,根本没想到去重这事。。。
- 正确解法:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
//排序为了之后去重,本题不需记录下标
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++)
{
//排序后正整数数组中三数之和不能为0
if(nums[i] > 0) break;
//对第一个数去重
/*使用num[i]!=nums[i-1]防止漏解相同数字不在相同位置如{-1,-1,2}*/
if(i > 0 && nums[i] == nums[i - 1]) continue;
//创指针选择第二三个数
int left = i + 1;
int right = nums.length - 1;
while(left < right)
/*因为不重复,所以有效范围应该是left!=right*/
{
//上来不直接去重,因为可能漏解{0,0,0}
if(nums[i] + nums[left] + nums[right] < 0) left++;
else if(nums[i] + nums[left] + nums[right] > 0) right--;
else{
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
//第二三个数去重,左右落点都在重复的最后一个数上
while(left < right && nums[right] == nums[right - 1]) right--;
while(left < right && nums[left] == nums[left + 1]) left++;
//无论有无重复,都要从不重复的左右开始判断
right--;
left++;
}
}
}
return res;
}
}
总结:不重复包括两层意思:三元组内存储元素对应下标彼此不重复,用一指针定一然后其他指针在后移二解决;还有就是三元组间组成的整体三个数字不重复,通过排序去重实现。
四数之和(四数组版)
- 题目链接:四数之和
- 我的解法:转化为两数之和问题,时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( n 2 ) O(n^2) O(n2)
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer, Integer> plus = new HashMap<Integer, Integer>();
int ans = 0;
for(int i = 0; i < nums1.length; i++)
for(int j = 0; j < nums2.length; j++)
{
int sum = nums1[i] + nums2[j];
if(plus.containsKey(sum))
{
plus.replace(sum, (plus.get(sum) + 1));
}
else
{
plus.put(sum, 1);
}
}
for(int i = 0; i < nums3.length; i++)
for(int j = 0; j < nums4.length; j++)
{
int sum = nums3[i] + nums4[j];
if(plus.containsKey((0-sum)))
{
ans += plus.get((0 - sum));
}
}
return ans;
}
}
总结:第一个二重循环插入(两数之和,两数之和出现次数)进入hash表时判断是否存在再将次数+1的逻辑可以用一句话
javahashmap.put(sum, hashmap.getOrDefault(sum, 0) + 1);
代替
四数之和(单数组版)
- 题目链接:不用去重的四数之和
- 正确解法:本解法延续三数之和的双指针去重思想
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++)
{
// 这里不加剪枝后续也会发生溢出,要搭配转为long类型的四元素求和使用
if (nums[i] > 0 && nums[i] > target) {
return res;
}
if(i > 0 && nums[i] == nums[i - 1]) continue;
for(int j = i + 1; j < nums.length; j++)
{
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) right--;
else if(sum < target) left++;
else{
res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while(left < right && nums[right] == nums[right - 1]) right--;
while(left < right && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
}
return res;
}
}
总结:n数之和,在记录的是不同n元组相加的元素值时使用双指针更方便,外层是n-2重循环去重前n-2个加入n元组的数,内层使用双指针锁定答案。
注意:随着相加元素的增多,和会溢出,这就要求转为long类型,同时及时剪枝,但由于复杂度是 O ( n n ) O(n^n) O(nn),n不会太大。