哈希表
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
链接
题解
该题目的关键在于遍历循环数组,在遍历的过程中,记录目前值与target的差值,且使用map记录差值情况,若存在差值,则直接返回差值
function twoSum(nums: Array<number>, target: number) {
const len = nums.length;
const hashMap = new Map();
for(let i = 0; i < len; i++) {
// 计算当前值与target的差值
const targetNew = target - nums[i];
if (hashMap.has(targetNew)) {
// 如果存在该差值,则直接返回 [该差值的下标, 与当前值的下标]
return [hashMap.get(targetNew), i];
} else {
// 暂时不存在差值,存入当前值的下标
hashMap.set(nums[i], i);
}
}
};
// const arr = twoSum([3,2,4], 6);
// console.log({ arr });
2. 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
链接
题解
该题目在于将每一项数据根据Unicode编码排序,异位词经过排序后的字符串会相等,后使用map记录数据即可。
function groupAnagrams(strs: string[]): string[][] {
const len = strs.length;
// 讲异位词归类成一个数组 {'ab': ['ab', 'ba']}
const hashMap = new Map<string, string[]>();
// 遍历字符串数组
for(let i = 0; i < len; i++) {
const temp = strs[i];
// 字符串转化为数组 ['b', 'a']
const newStr = (Array.from(temp) as string[])
// 按Unicode编码排序 ['a', 'b']
.sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0))
// 拼接成字符串 ['ab']
.reduce((a, b) => `${a}${b}`, '');
if (hashMap.has(newStr)) {
// 如果存在异位词,则推进数组
(hashMap.get(newStr) || []).push(temp);
} else {
// 不存在异位词,则set初始化数组
hashMap.set(newStr, [temp]);
}
}
const res: string[][] = [];
// 输出结果
hashMap.forEach((value) => {
res.push(value);
});
return res;
};
// const arr = groupAnagrams(['abc', 'cab', 'de', 'ed']);
// // { arr: [ [ 'abc', 'cab' ], [ 'de', 'ed' ] ] }
// console.log({ arr });
3.最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
链接
题解:
function longestConsecutive(nums: number[]): number {
// 去重 [100, 4, 4, 200, 1, 3, 2] => [100, 4, 200, 1, 3, 2]
let num_set: Set<number> = new Set([...nums]);
// 记录当前最长连续序列长度
let longestStreak = 0;
for (const num of num_set) {
// 遍历
if (!num_set.has(num - 1)) {
// 如果不存在比当前值小1的值,则当前值作为起点
// 如100, 1可以作为起点
let currentNum = num;
// 记录当前最长连续序列长度
let currentStreak = 1;
while (num_set.has(currentNum + 1)) {
// 遍历,找比当前值大1的数
currentNum += 1;
currentStreak += 1;
}
// 更新最大长度
longestStreak = Math.max(longestStreak, currentStreak);
}
}
return longestStreak;
};
// const num = longestConsecutive([100, 4, 4, 200, 1, 3, 2]);
// // { num: 4 }
// console.log({ num });
指针
4.移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
题解
// 快排思想
function moveZeroes(nums: Array<number>) {
if (nums.length === 1) {
return nums;
}
let left = 0;
for (let right = 0; right < nums.length; right++) {
if (nums[right] !== 0) {
// 右边不等于0
if (nums[left] === 0) {
// 左边等于0
[nums[right], nums[left]] = [nums[left], nums[right]];
}
// 找到了右边不等于0的目标元素,移动左指针寻找等于0的元素
left++;
}
}
return nums;
};
// const arr = moveZeroes([0, 1, 0, 3, 12]);
// // { num: 4 }
// console.log({ arr });
5.盛水最多的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
说明:你不能倾斜容器。
链接
题解
左右指针法,每次移动短的指针能才有机会使容纳的水变多,因为左右指针收窄时,形成的容器的底是变小的,而能存多少水,容器的高也是一个决定性因素,根据木桶原理,短的木板决定水桶能装多少水,在底变小的情况下,移动短的指针有机会遇到更长的木板,使得容积变大。移动过程中,更新最大值即可。
function maxArea(height: Array<number>): number {
let left = 0, right = height.length - 1;
let res = 0;
while(left < right) {
// 底
const bottom = right - left;
// 高
const h = Math.min(height[left], height[right]);
// 更新最大值
res = Math.max(res, bottom * h);
if (height[left] > height[right]) {
// 右边小,移动小的
right--;
} else {
// 左边小,移动小的
left++;
}
}
return res;
};
// const num = maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7]);
// // { num: 49 }
// console.log({ num });