面试经典150题0506

本文解析了LeetCode中的五个问题,涉及算法实现:判断快乐数、查找重复元素、最长连续序列、区间汇总和合并区间,展示了使用哈希集合、哈希表等数据结构解决这些问题的方法。
摘要由CSDN通过智能技术生成

面试经典150题0506

Leetcode202 快乐数

使用一个集合保存在计算过程中出现的数,以避免无限循环的出现。

public static boolean isHappy(int n){
    Set<String> set = new HashSet<>();
    String str = Integer.toString(n);
    while (!set.contains(str)){
        set.add(str);
        if(str.equals("1")){
            return true;
        }
        int tmp = 0;
        for (int i = 0; i < str.length(); i++) {
            int a = str.charAt(i) - '0';
            tmp += a * a;
        }
        str = Integer.toString(tmp);
    }
    return false;
}
Leetcode219 存在重复元素Ⅱ

使用HashMap记录出现过的数字和其下标,新遍历的数字如果出现在HashMap中,则判断两者下标是否满足abs(i - j) <= k,如果满足直接返回;不满足则更新该元素的下标。

public static boolean containsNearbyDuplicate(int[] nums, int k){
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        if(!map.isEmpty() && map.containsKey(nums[i]) && i - map.get(nums[i]) <= k){
            return true;
        }
        map.put(nums[i], i);
    }
    return false;
}
Leetcode128 最长连续序列

思路一:哈希集合

遍历数组中的每一个元素,如果遍历到某个元素num,当num-1也在集合中时,说明从num-1出发的最长序列一定比从num出发的最长序列要长,所以此时可以直接跳过。如果集合中没有出现num-1,则需要从num开始,依次判断num+1num+2…是否在集合中,同时记录从num出发的最长序列长度,用于更新最长序列长度。

public static int longestConsecutive(int[] nums){
    // 要求时间复杂度O(n)
    Set<Integer> set = new HashSet<>();
    for(int num:nums){
        set.add(num);
    }
    int count = 0;
    for(int num:nums){
        // 包含比自己小1的数,那么从自己出发寻找连续序列一定不是最长
        if(!set.contains(num - 1)){
            int tmp = 1;
            int start = num;
            // 向后依次寻找增1的数,记录序列长度
            while (set.contains(++start)){
                tmp++;
            }
            count = Math.max(count, tmp);
        }
    }
    return count;
}

思路二:哈希表

使用哈希表记录右边界,用于减少遍历次数。做法是建立一个哈希表,记录每个元素num能够连续到达的右边界,这样在内层循环遍历到一个新元素时,无需经过多次+1(遍历+判断)才能到达右边界,直接取值即可。

public static int longestConsecutiveByHashMap(int[] nums){
    Map<Integer, Integer> map = new HashMap<>();
    for(int num:nums){
        // 初始化每个元素的右边界为自己
        map.put(num, num);
    }
    int count = 0;
    for(int num:nums){
        if(!map.containsKey(num - 1)){
            int right = map.get(num);
            while (map.containsKey(right + 1)){
                right = map.get(right + 1);
            }
            // 更新num的右边界
            map.put(num, right);
            count = Math.max(count, right - num + 1);
        }
    }
    return count;
}
Leetcode228 区间汇总

使用双指针找出每个区间的左右端点。

遍历数组,当right + 1 < nums.length && nums[right+1] == nums[right] + 1时,右指针right向右移动;否则区间[nums[left], nums[right]]找到,将其加入答案。然后将right加1,并将left移动到right位置,继续寻找下一个区间。

public static List<String> summaryRanges(int[] nums){
    List<String> res = new ArrayList<>();
    int left = 0, right = 0;
    while (right < nums.length){
        if(right + 1 < nums.length && nums[right+1] == nums[right] + 1){
            right++;
            continue;
        }
        if(left == right){
            res.add(Integer.toString(nums[left]));
        }
        else {
            res.add(nums[left] + "->" + nums[right]);
        }
        right++;
        left = right;
    }
    return res;
}
Leetcode056 合并区间
  • 根据区间的起点进行排序,起点小的靠前,大的靠后。
  • 根据当前遍历的区间的起点和前一个区间的终点进行比较,看是否有重合,判断是否可以合并。
  • 合并后的区间起点一定是前一个区间的起点(已经排序),终点是两个区间中终点更大的。
  • 如果不可以合并,将上一个[left, right]进行保存,并将当前区间的左右边界更新为leftright

image-20240506154821378

public static int[][] merge(int[][] intervals){
    int m = intervals.length;
    int n = intervals[0].length;
    List<int[]> res = new ArrayList<>();
    Arrays.sort(intervals, new Comparator<int[]>() {
        @Override
        public int compare(int[] o1, int[] o2) {
            int compare = Integer.compare(o1[0], o2[0]);
            if(compare != 0){
                return compare;
            }
            return Integer.compare(o1[1], o2[1]);
        }
    });
    int left = intervals[0][0];
    int right = intervals[0][1];
    for (int i = 1; i < m; i++) {
        if(intervals[i][0] <= right){
            // 区间合并
            right = Math.max(right, intervals[i][1]);
        }
        else {
            // 新区间
            // 先保存上一个区间
            res.add(new int[]{left, right});
            left = intervals[i][0];
            right = intervals[i][1];
        }
    }
    res.add(new int[]{left, right});
    return res.toArray(int[][]::new);
}
Leetcode057 插入区间

分三种情况:

  • 插入区间在当前遍历区间左侧,如果未插入,则更新标志位isInsert = true并进行插入,同时插入当前遍历区间;如果已经插入,则直接插入当前遍历区间即可。
  • 插入区间在当前遍历区间右侧,此时直接插入当前遍历区间即可。
  • 插入区间和当前遍历区间存在重叠,进行区间合并,更新newInterval的左右边界。

最后,需要判断是否已经插入,如果没有插入,则将待插入区间直接插入到列表最后。

public static int[][] insert(int[][] intervals, int[] newInterval){
    // 给定区间无重叠,按照区间起始端点进行排序
    // 在intervals中插入新区间newInterval并保证按照start进行升序排列,且区间不重叠(有必要的话可以合并)
    int m = intervals.length;
    List<int[]> res = new ArrayList<>();
    boolean isInsert = false;
    int start = newInterval[0];
    int end = newInterval[1];
    for(int[] interval: intervals){
        int left = interval[0];
        int right = interval[1];
        // 三种情况:
        // 插入区间在当前区间左侧
        if(end < left){
            if(!isInsert){
                res.add(new int[]{start, right});
                isInsert = true;
            }
        }
        // 插入区间在当前区间右侧
        else if(start > right){
            res.add(interval);
        }
        else {
            // 重叠
            start = Math.min(start, left);
            end = Math.max(end, right);
        }
    }
    // 新插入区间在最右侧的情况,遍历完成后还没有插入
    if(!isInsert){
        res.add(new int[]{start, end});
    }
    return res.toArray(new int[res.size()][]);
}
h.min(start, left);
            end = Math.max(end, right);
        }
    }
    // 新插入区间在最右侧的情况,遍历完成后还没有插入
    if(!isInsert){
        res.add(new int[]{start, end});
    }
    return res.toArray(new int[res.size()][]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值