面试经典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+1
,num+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]
进行保存,并将当前区间的左右边界更新为left
和right
。
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()][]);
}