Leetcode 热门百题斩(第三天)

本文分享了解决LeetCode上热门算法题的几种方法,如使用双指针优化的两数之和、字符异位词分组、最长连续序列、移动零和盛最多水容器问题,以及哈希表在三数之和中的应用,展示了如何提高代码效率并解决超时问题。
摘要由CSDN通过智能技术生成

介绍

针对leetcode的热门一百题,解决大多数实习生面试的基本算法题。通过我自己的思路和多种方法,供大家参考。 

1.两数之和(题号:1)

方法一 

最先想到的就是两个for去遍历匹配。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        for(int i = 0; i < nums.length; i++)
            for(int j = i + 1; j < nums.length; j++) {
                if(nums[i] + nums[j] == target) {
                    return new int[] {i, j};
                }
            }
            return null;
    }
}

效率低下。

尝试其他方法。

 方法二

 使用Hash表进行匹配。也是先进行一次遍历,任何直接使用containKey匹配符合条件的值。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> numMap = new HashMap<>();
        //在为map赋值的时候顺便进行判断
        //判断当前map中是否存在其他key2可以得到当前key1相加等于target
        for(int i = 0; i < nums.length; i++) {
            if(numMap.containsKey(target - nums[i])) {
                //存在直接返回数据即可,此时是没有包含本身值 + 本身值是否=target
                return new int[] {i, numMap.get(target - nums[i])};
            }
            numMap.put(nums[i], i);
        }
        return new int[2];
    }
}

效率马上就高了。

2. 字符异位词分组(题号:49)

方法一(暴力法,超时)

创建标记数组(标记数组是否被使用),通过二次遍历,将符合添加的数据存储到一个list中,最终返回数据。 

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        //创建一个标记数组
        int[] sign = new int[strs.length];
        List<List<String>> result = new LinkedList<>();
        //遍历列表并将重排序后相同的字符串存放在一个list中
        for(int i = 0; i < strs.length; i++) {
            //直接跳过
            if(sign[i] == 1) {
                continue;
            }
            //创建异位list
            List<String> tempList = new LinkedList<>();
            tempList.add(strs[i]);
            //对当前的字符串进行重排序
            char[] firstArray = strs[i].toCharArray();
            //直接对引用数组进行排序
            Arrays.sort(firstArray);
            String firstSortTemp = new String(firstArray);
            //将当前字符串标记为已使用(0为未使用,1为已使用)
            sign[i] = 1;
            for(int j = i + 1; j < strs.length; j++) {
                //对后续的字符串进行重排序
                if(sign[j] == 1) {
                    //直接跳过
                     continue;
                }
                char[] secondArray = strs[j].toCharArray();
                Arrays.sort(secondArray);
                String secondSortTemp = new String(secondArray);
                if(firstSortTemp.equals(secondSortTemp)) {
                    //符合条件放入list中
                    tempList.add(strs[j]);
                    //标记为已使用
                    sign[j] = 1;
                }
            }
            result.add(tempList);
        }
        return result;
    }
}

超时。

 方法二(使用hash表进行匹配)

使用hash表,对每个字符串重新排序,key存储排序后的字符串,value存储的就是符合异位的字符串的集合,最终返回values的集合。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        //使用hash表进行匹配,key存储排序后的字符串,vlaue存储的就是异位字符串的list 
        //(有点类似插入排序, 将对应的字符串存储到对应的键值对上)
        Map<String, List<String>> resultMap = new HashMap<>();
        for(int i = 0; i < strs.length; i++) {
            char[] tempStr = strs[i].toCharArray();
            Arrays.sort(tempStr);
            //排序好的字符串
            String sortTemp = new String(tempStr);
            List<String> tempList = resultMap.getOrDefault(sortTemp, new LinkedList<String>());
            //插入自己,并修改对应list
            tempList.add(strs[i]);
            resultMap.put(sortTemp, tempList);
        }
        //直接返回value集合
        return new LinkedList<List<String>>(resultMap.values());
    }
}

通过。

3.最长连续序列(题号:128)

最开始的时候题目要求将时间复杂度控制在O(N),就没有想到使用双重循环(主要是怕超时)。

主要的思路就是使用set去重,然后遍历,从每个连续序列的排头进行遍历判断,求出最长的序列长度。(要注意的就是,去除多余循环的次数,防超时)

class Solution {
    public int longestConsecutive(int[] nums) {
        //使用HashSet去重
        Set<Integer> set = new HashSet<Integer>();
        for(int i = 0; i < nums.length; i++) {
            set.add(nums[i]);
        }
        int maxSize = 0;
        for(int i = 0; i < nums.length; i++) {
            //判断当前数值是否为排头,如果是排头就将长度设置为1,反之直接跳过
            int currentSize = 0;
            int tempNum = nums[i];
            //判断是否存在前值,如果存在直接跳过,防止超时
            if(!set.contains(tempNum - 1)) {
                //不存在,就说明是排头
                currentSize = 1;
                //循环判断后续长度
                while(set.contains(tempNum + 1)) {
                    currentSize++;
                    tempNum++;
                }
                maxSize = Math.max(currentSize, maxSize);
            }
        }
        return maxSize;
    }
}

 执行通过。

4.移动零(题号:283)

第一时间想到的是使用双指针进行数值的移动,当时涉及到大量的移动操作,效率非常低下,因此直接新建一个数组进行操作。

class Solution {
    public void moveZeroes(int[] nums) {
        int[] result = new int[nums.length];
        int point = 0;
        for(int i = 0; i < nums.length; i++) 
            if(nums[i] != 0) 
                result[point++] = nums[i];
        for(int i = 0; i < nums.length; i++) 
            nums[i] = result[i];
    }
}

 运行通过。

5.盛最多水的容器(题号:11)

在开始的时候就是想到使用两重遍历来解决,但是最后发现会超时。

class Solution {
    public int maxArea(int[] height) {
        int maxSize = 0;
        for(int i = 0; i < height.length; i++) {
            //对每条边进行遍历
            for(int j = i + 1; j < height.length; j++) {
                int len = Math.min(height[i], height[j]);
                int currentSize = len * (j - i);
                maxSize = (maxSize < currentSize) ? currentSize : maxSize;
            }
        }
        return maxSize;
    }
}

执行超时。

接着开始考虑其他方法,使用双指针来解决,左右向中间进行遍历,小的一侧向内移动。成功解决。 
 

class Solution {
    public int maxArea(int[] height) {
        int maxSize = 0;
        //使用双指针,两个指针分别左右进行压缩
        int first = 0, second = height.length - 1;
        while(first < second) {
            int currentLen = Math.min(height[first], height[second]);
            int currentSize = currentLen * (second - first);
            maxSize = (currentSize > maxSize) ? currentSize : maxSize;
            //小的一方就往内移一格
            if(height[first] > height[second]) {
                second--;
            } else {
                first++;
            }
        }
        return maxSize;
    }
}

执行通过。

6.三数之和(题号:15)

最开始的时候想到使用深度优先搜索,但是运行超时了。

class Solution {
    Set<List<Integer>> result = new HashSet<>();
    public List<List<Integer>> threeSum(int[] nums) {
        for(int mid = 0; mid < nums.length; mid++) {
            check(0, mid, nums.length - 1, nums);
        }
        return new LinkedList<List<Integer>>(result);
    }

    public void check(int i, int j, int k, int[] nums) {
        if(i == j || i == k || j == k || i >= nums.length || k < 0) {
            return ;
        }
        if(nums[i] + nums[j] + nums[k] == 0) {
            List<Integer> newSum = new LinkedList<>();
            newSum.add(nums[i]);
            newSum.add(nums[j]);
            newSum.add(nums[k]);
            Collections.sort(newSum);
            result.add(newSum);
        }
        //向下遍历
        check(i + 1, j, k, nums);
        check(i, j, k - 1, nums);
    }
}

运行超时。

 所以我们还是需要使用双指针,通过对一个数值作为target,其他两个来凑这个target,从而找到三数,通过双指针来控制和的大小,注意要进行去重。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
      Arrays.sort(nums);
      List<List<Integer>> result = new LinkedList<>();
      //第一位数值作为目标值,且要小于0,这样才存在三数和
      for(int targetIndex = 0; targetIndex < nums.length - 2; targetIndex++) {
          if(nums[targetIndex] > 0) {
              break;
          }
          if(targetIndex > 0  && nums[targetIndex] == nums[targetIndex - 1]) {
              continue;
          }
          int i = targetIndex + 1, j = nums.length - 1;
          while(i < j) {
            int tempSum = nums[i] + nums[j] + nums[targetIndex];
            if(tempSum > 0) {
                //j后退一个,且要去重
                while(i < j && nums[j] == nums[--j]);
            } else if(tempSum < 0) {
                //i前进一个,且要去重
                while(i < j && nums[i] == nums[++i]);
            } else {
                result.add(new LinkedList<Integer>(Arrays.asList(nums[targetIndex], nums[i], nums[j])));
                 //j后退一个,且要去重
                while(i < j && nums[j] == nums[--j]);
                 //i前进一个,且要去重
                while(i < j && nums[i] == nums[++i]);
            }
          }
      }
      return result;
    }
}

运行通过。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值