LeetCode hot 100简单篇
爬楼梯(菲波那切数列)
/**
* 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
* 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
*/
const climbStairs = function(n) {
if (n <= 2) {
return n;
}
let l1 = 1;
let l2 = 2;
let result = 0;
for (let i = 3; i <= n; i ++) {
result = l1 + l2;
l1 = l2;
l2 = result;
}
return result;
};
两数之和
/**
* 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
*/
const twoSum = (nums, target) => {
const map = {};
for (let i = 0; i < nums.length; i++) {
const targetNum = target - nums[i];
const targetNumIndex = map[targetNum];
if (targetNumIndex === undefined) {
map[nums[i]] = i;
} else {
return [targetNumIndex, i];
}
}
}
两数相加
/**
* 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
*/
const addTwoNumbers = function(l1, l2) {
let head = null, tail = null;
let carry = 0;
while (l1 || l2) {
const n1 = l1 ? l1.val : 0;
const n2 = l2 ? l2.val : 0;
const sum = n1 + n2 + carry;
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
carry = sum < 10 ? 0 : 1;
if (l1) {
l1 = l1.next;
}
if (l2) {
l2 = l2.next;
}
}
if (carry > 0) {
tail.next = new ListNode(carry);
}
return head;
};
有效的括号
/**
* 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
*/
const isValid = function(s) {
if (s.length % 2 === 1) {
return false;
}
let map = {
'(': ')',
'[': ']',
'{': '}',
};
let arr = [];
for (let i = 0; i < s.length; i++) {
let char = s[i];
if (map[char]) {
arr.push(char)
} else {
if (map[arr[arr.length - 1]] === char) {
arr.pop();
} else {
return false;
}
}
if (arr.length > s.length - i) {
return false;
}
}
return arr.length === 0;
};
只出现一次的数字
/**
* 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
*/
const singleNumber = (nums) => {
return nums.reduce((prev, cur) => {
return prev ^ cur;
}, 0);
};
比特位计数
/**
* 给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
* 输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
*/
const countBits = (n) => {
const bits = new Array(n + 1).fill(0);
let highBit = 0;
for (let i = 1; i <= n; i++) {
if ((i & (i - 1)) == 0) {
highBit = i;
}
bits[i] = bits[i - highBit] + 1;
}
return bits;
};
移动零
/**
* 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
*/
const moveZeroes = (nums) => {
for (let i = 0, l = nums.length; i < l; i++) {
if (nums[i] === 0) {
nums.splice(i, 1);
nums.push(0);
i -= 1;
l -= 1;
}
}
};
找到所有数组中消失的数字
/**
* 给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]
*/
var findDisappearedNumbers = function(nums) {
const n = nums.length;
for (const num of nums) {
const x = (num - 1) % n;
nums[x] += n;
}
const ret = [];
for (const [i, num] of nums.entries()) {
if (num <= n) {
ret.push(i + 1);
}
}
return ret;
};
汉明距离
/**
* 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离。
*/
const hammingDistance = (x, y) => {
let s = x ^ y, res = 0;
while (s != 0) {
s &= s - 1;
res++;
}
return res;
};
合并两个有序链表
/**
* 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
*/
const mergeTwoLists = (list1, list2) => {
const result = new ListNode(-1);
let prev = result;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
prev.next = list1;
list1 = list1.next;
} else {
prev.next = list2;
list2 = list2.next;
}
prev = prev.next;
}
prev.next = list1 === null ? list2 : list1;
return result.next;
};
买卖股票的最佳时机
/**
* 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
*/
const maxProfit = (prices) => {
let minprice = Number.MAX_SAFE_INTEGER;
let maxprofit = 0;
for (let i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
};
多数元素
/**
* 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
*/
const majorityElement = (nums) => {
let count = 0;
let candidate = null;
for (let num of nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
};
环形链表
/**
* 给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
*/
const hasCycle = (head) => {
if (head == null || head.next == null) {
return false;
}
let slow = head;
let fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
};
相交链表
/**
* 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
* 题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
*/
const getIntersectionNode = (headA, headB) => {
if (headA === null || headB === null) {
return null;
}
let pA = headA, pB = headB;
while (pA !== pB) {
pA = pA === null ? headB : pA.next;
pB = pB === null ? headA : pB.next;
}
return pA;
};
反转链表
/**
* 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
*/
const reverseList = (head) => {
let prev = null;
let curr = head;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
};
回文链表
/**
* 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
*/
const isPalindrome = (head) => {
const vals = [];
while (head !== null) {
vals.push(head.val);
head = head.next;
}
for (let i = 0, j = vals.length - 1; i < j; ++i, --j) {
if (vals[i] !== vals[j]) {
return false;
}
}
return true;
};
合并二叉树
/**
* * Definition for a binary tree node.
* * function TreeNode(val, left, right) {
* * this.val = (val===undefined ? 0 : val)
* * this.left = (left===undefined ? null : left)
* * this.right = (right===undefined ? null : right)
* * }
* *
* 给你两棵二叉树: root1 和 root2 。
* 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
* 返回合并后的二叉树。
* 注意: 合并过程必须从两个树的根节点开始
* 输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
* 输出:[3,4,5,5,4,null,7]
*/
const mergeTrees = function(root1, root2) {
if (root1 === null) return root2;
if (root2 === null) return root1;
let queue = [];
queue.push(root1);
queue.push(root2);
while (queue.length) {
let node1 = queue.shift();
let node2 = queue.shift();;
node1.val += node2.val;
if (node1.left !== null && node2.left !== null) {
queue.push(node1.left);
queue.push(node2.left);
}
if (node1.right !== null && node2.right !== null) {
queue.push(node1.right);
queue.push(node2.right);
}
if (node1.left === null && node2.left !== null) {
node1.left = node2.left;
}
if (node1.right === null && node2.right !== null) {
node1.right = node2.right;
}
}
return root1;
};
二叉树的直径
/**
* 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
*/
const depth = (node) =>{
if (node == null) {
return 0; // 访问到空节点了,返回0
}
let L = depth(node.left); // 左儿子为根的子树的深度
let R = depth(node.right); // 右儿子为根的子树的深度
ans = Math.max(ans, L+R+1); // 计算d_node即L+R+1 并更新ans
return Math.max(L, R) + 1; // 返回该节点为根的子树的深度
};
const diameterOfBinaryTree = (root) => {
ans = 1;
depth(root);
return ans - 1;
};
翻转二叉树
/**
* 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
*/
const invertTree = (root) => {
if (root === null) {
return null;
}
const left = invertTree(root.left);
const right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
};
二叉树的最大深度
/**
* 给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数
*/
const maxDepth = function(root) {
if (root == null) {
return 0;
} else {
const leftHeight = maxDepth(root.left);
const rightHeight = maxDepth(root.right);
return Math.max(leftHeight, rightHeight) + 1;
}
};
对称二叉树
/**
* 给你一个二叉树的根节点 root , 检查它是否轴对称
*/
const check = (l, r) => {
if (l == null && r == null) {
return true;
}
if (l == null || r == null) {
return false;
}
return l.val == r.val && check(l.left, r.right) && check(l.right, r.left);
}
const isSymmetric = (root) => {
return check(root, root);
};
二叉树的中序遍历
/**
* 二叉树遍历中的一种,即先遍历左子树,后访问根结点,然后遍历右子树
*/
const inorderTraversal = (root) => {
const res = [];
let predecessor = null;
while (root) {
if (root.left) {
// predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
predecessor = root.left;
while (predecessor.right && predecessor.right !== root) {
predecessor = predecessor.right;
}
// 让 predecessor 的右指针指向 root,继续遍历左子树
if (!predecessor.right) {
predecessor.right = root;
root = root.left;
}
// 说明左子树已经访问完了,我们需要断开链接
else {
res.push(root.val);
predecessor.right = null;
root = root.right;
}
}
// 如果没有左节点,则直接访问右节点
else {
res.push(root.val);
root = root.right;
}
}
return res;
};