记录一下 leetcode top100javascript
该部分只记录 easy 难度,因为为 easy 难度,故基本直接放解答html
两数和 - 找到无序数组中和为定值的两个数,返回下标java
由于须要返回下标,所以先排序后用两个指针扫(前->后, 后->前)的方式(NlogN)不行。 故选用相似 hash
的形式解; key
为数组值, value
为下标node
/** * @param {number[]} nums * @param {number} target * @return {number[]} */
var twoSum = function(nums, target) {
const map = {};
nums.forEach((num, index) => map[num] = index);
const len = nums.length;
for(let i = 0; i < len; i++) {
const otherIndex = map[target - nums[i]];
if(otherIndex && otherIndex != i) {
return [Math.min(i, otherIndex), Math.max(i, otherIndex)]
}
}
};
复制代码
判断输入的字符串是否是只包含(
, )
, {
, }
, [
and ]
算法
/** * @param {string} s * @return {boolean} */
var isValid = function(s) {
const stack = [];
const helpMap = {
'(': ')',
'[': ']',
'{': '}'
}
for(let i = 0; i < s.length; i++) {
const char = s[i];
if(char in helpMap) {
stack.push(helpMap[char]);
} else if(char !== stack.pop()){
return false;
}
}
return stack.length === 0;
};
复制代码
合并两个已排序的链表并将其做为新链表返回。新链表应该经过拼接前两个链表的节点来完成。数组
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */
var mergeTwoLists = function (l1, l2) {
if (!l1) return l2;
if (!l2) return l1;
if (l1.val <= l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
};
复制代码
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */
var mergeTwoLists = function (l1, l2) {
if (!l1) return l2;
if (!l2) return l1;
const newList = new ListNode(null);
let newPointer = newList;
while (l1 && l2) {
if (l1.val < l2.val) {
newPointer.next = l1;
l1 = l1.next;
} else {
newPointer.next = l2;
l2 = l2.next;
}
newPointer = newPointer.next;
}
if (l1) newPointer.next = l1;
if (l2) newPointer.next = l2;
return newList.next;
};
复制代码
给定排序数组和目标值,若是找到目标,则返回索引。若是没有,请返回索引按顺序插入的索引。缓存
相似于二分查找的变种题安全
平时二分查找递归的写多了,这里来个不使用递归的版本闭包
/** * @param {number[]} nums * @param {number} target * @return {number} */
var searchInsert = function(nums, target) {
let start = 0, end = nums.length - 1;
let mid = Math.ceil((end + start) / 2);
while(nums[mid] !== target) {
if(end <= start) {
// 若是没有找到要看看插哪里
return nums[end] < target ? end + 1 : end;
}
if(nums[mid] > target) {
// 保护一下不要变成负的
end = Math.max(mid - 1, 0);
} else {
// 保护一下不要越界
start = Math.min(mid + 1, nums.length - 1);
}
mid = Math.floor((end + start) / 2);
}
return mid;
};
复制代码
经典题:找一个数组中最大子序列和app
解法:从头至尾遍历每个数组元素,如何前面元素的和为正,则加上本元素的值继续搜索;如何前面元素的和为负,则此元素开始新的和计数。以及整个过程当中要注意更新和的最大值。
/** * @param {number[]} nums * @return {number} */
var maxSubArray = function(nums) {
let cache = 0;
let max = -Infinity;
nums.forEach(num => {
cache += num;
if(cache > max) max = cache;
if(cache < 0) cache = 0;
})
return max;
};
复制代码
跳台阶,经典DP
/** * @param {number} n * @return {number} */
var climbStairs = function(n) {
const dp = [0, 1, 2];
for(let i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n]
};
复制代码
判断俩二叉树是否是彻底相同 很差的写法:利用短路等特性把代码写在一行,注意作取值保护
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} p * @param {TreeNode} q * @return {boolean} */
var isSameTree = function(p, q) {
return (p || q) ? (p || {}).val === (q || {}).val && isSameTree((p || {}).left, (q || {}).left) && isSameTree((p || {}).right, (q || {}).right) : true
};
复制代码
判断一颗二叉树是否是镜像 解法一:继续一行写完
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {boolean} */
var isSymmetric = function(root) {
return childIsSymmetric((root || {}).left, (root || {}).right);
};
function childIsSymmetric (left, right) {
return (left || right) ? (left || {}).val === (right || {}).val && childIsSymmetric((left || {}).left, (right || {}).right) && childIsSymmetric((left || {}).right, (right || {}).left) : true;
}
复制代码
解法二:
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {boolean} */
var isSymmetric = function(root) {
if(!root) return true;
function areMirror(left, right) {
if(!left && !right) return true;
if((left && !right) || (right && !left)) return false;
if(left.val != right.val) return false;
return areMirror(left.left, right.right) && areMirror(left.right, right.left);
}
return areMirror(root.left, root.right);
};
复制代码
找二叉树深度
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {number} */
var maxDepth = function(root, deep = 0) {
if(!root) return deep;
return Math.max(maxDepth(root.left) + 1, maxDepth(root.right) + 1);
};
复制代码
给一个数组,其中第i个元素是第i天给定股票的价格。只能进行一次买进和卖出,求最大利润
首先设定最大利润和最低价格:
/** * @param {number[]} prices * @return {number} */
var maxProfit = function(prices) {
if(!prices.length) return 0;
let minPrice = Infinity, maxProfit = -Infinity;
prices.forEach(price => {
if(price < minPrice) {
minPrice = price;
}
if(maxProfit < (price - minPrice)) {
maxProfit = (price - minPrice);
}
});
return maxProfit;
};
复制代码
经典题,一行代码异或解决
/** * @param {number[]} nums * @return {number} */
var singleNumber = function(nums){
return nums.reduce((a,b) => { return a ^ b});
}
复制代码
判断链表是否有环,不能使用额外空间 解法:用两个指针,快指针每次走两步,慢指针每次走一步。这样每走一次快指针比慢指针多一步,若是有环最终可以相遇。
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} head * @return {boolean} */
var hasCycle = function (head) {
let slow = head;
let fast = head;
while (fast) {
slow = slow.next;
fast = fast.next && fast.next.next;
if (slow === fast && fast !== null) {
return true;
}
}
return false;
};
复制代码
设计一个支持push
,pop
,top
和在恒定时间内检索最小元素的堆栈。
最优解 - O(1):
该题要求的是实现一个栈,栈的修改元素的操做只有 pop()
和 push(x)
,所以咱们能够在 push
的时候维护一个做用相似于 缓存 的,记录这个时候最小元素是什么的栈 minStack
。在每次 push
的时候,更新一下咱们的缓存,这个时候只须要对比一下当前入栈元素和 minStack
栈顶元素,而后取小的推入 minStack
便可,表明着这个时候最小值应该是 以前栈中最小的 和 新来的 中最小的一个元素。
/** * initialize your data structure here. */
var MinStack = function() {
this.stack = [];
this.minStack = [];
};
/** * @param {number} x * @return {void} */
MinStack.prototype.push = function(x) {
this.stack.push(x);
const minStackTop = this.minStack[this.minStack.length - 1];
this.minStack.push(Math.min(x, minStackTop === undefined ? Infinity : minStackTop));
};
/** * @return {void} */
MinStack.prototype.pop = function() {
this.stack.pop();
this.minStack.pop();
};
/** * @return {number} */
MinStack.prototype.top = function() {
return this.stack[this.stack.length - 1];
};
/** * @return {number} */
MinStack.prototype.getMin = function() {
return this.minStack[this.minStack.length - 1];
};
/** * Your MinStack object will be instantiated and called as such: * var obj = Object.create(MinStack).createNew() * obj.push(x) * obj.pop() * var param_3 = obj.top() * var param_4 = obj.getMin() */
复制代码
找到两个单链表公共开头的节点。
null
。同时咱们还要处理两个链表没有公共节点的状况:
如上图,从 A 出发的指针在走了 a + b 个节点后,从 B 出发的指针也走了 b + a 个节点,所以他们此时再走一步之后就都是 undefined
, 也就是说两个链表没有公共节点的话,只要判断两个指针都是 undefined
就能够知道了。
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} headA * @param {ListNode} headB * @return {ListNode} */
var getIntersectionNode = function(headA, headB) {
let nodeA = headA, nodeB = headB;
while(nodeA !== nodeB) {
nodeA = nodeA ? nodeA.next : headB;
nodeB = nodeB ? nodeB.next : headA;
}
return nodeA || null;
};
复制代码
找出数组中出现次数超过 `⌊ n/2 ⌋ 的数 这个题比较有意思一点,给出以下几种方案:
A
及其个数 numA
,在遍历过程当中,若是当前元素和记录元素 A
相等,则 numA
加 1;若是不相等,则 numA
减 1。若是 numA
为零,则更新 A
和重置 numA
。本质是:在遍历数组时,若是numA
为0,表示当前并无候选元素,也就是说以前的遍历过程当中并无找到超过半数的元素。那么,若是超过半数的元素A
存在,那么A
在剩下的子数组中,出现次数也必定超过半数。所以咱们能够将原始问题转化为它的子问题。这里有一个可视化的流程/** * @param {number[]} nums * @return {number} */
var majorityElement = function(nums) {
let result, count = 0
nums.forEach(num => {
if(result !== num) {
if(count === 0) {
count = 1;
result = num;
} else {
count--;
}
} else {
count++
}
});
return result;
};
复制代码
你是一名专业强盗,计划沿着一条街打家劫舍。每间房屋都储存有必定数量的金钱,惟一能阻止你打劫的约束条件是:因为房屋之间有安全系统相连,若是同一个晚上有两间相邻的房屋被闯入,它们就会自动联络警察,所以不能够打劫相邻的房屋。给定一列非负整数,表明每间房屋的金钱数,计算出在不惊动警察的前提下一夜最多能够打劫到的金钱数。
DP:对于第i个房间咱们的选择是偷和不偷
dp[i] = nums[i-1] + dp[i -2]
, 假设dp[i]
表示打劫到第 i 间房屋时累计取得的金钱最大值dp[i] = dp[i -1]
dp[i] = max(dp[i - 2] + nums[i - 1], dp[i - 1] )
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
if(!nums.length) return 0;
const dp = [nums[0]];
for(let i = 1; i < nums.length; i++){
dp[i] = Math.max(dp[i - 1], (dp[i - 2] || 0) + nums[i]);
}
return dp[nums.length - 1];
};
复制代码
反转链表
能够老老实实的循环/递归:
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} head * @return {ListNode} */
var reverseList = function (head) {
if (!head) return head;
let start = head;
let end = head
while (end.next) {
const node = end.next;
end.next = node.next;
node.next = start;
start = node;
}
return start;
}
复制代码
也能够骚操做一下:
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} head * @return {ListNode} */
var reverseList = function (head, pre) {
if(!head) return head
const next = head.next;
head.next = pre || null
return next ? reverseList(next, head) : head;
};
复制代码
反转二叉树
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {TreeNode} */
var invertTree = function(root) {
if(!root) return [];
[root.left, root.right] = [root.right, root.left];
invertTree(root.left);
invertTree(root.right);
return root
};
复制代码
判断一个链表是否是回文,要求 O(n) 时间, O(1) 空间
本题暴力解法就是遍历完链表后转为字符串,而后看是否是回文,符合时间复杂度要求,可是不符合空间复杂度要求
要求 O(1) 的空间,那就只能从链表自己动手了。首先判断回文无非就是从两边到中间或者从中间到两边。因为咱们能够对链表自己动手,那就考虑让链表可以倒着访问(由于要求O(1)空间,因此不能直接改造为双向链表)。因为咱们只能让链表顺着一个方向走,因此能够想到选择从中间到两边的方式,左边的向前(pre
),右边的向后(next
)。
那么咱们如何找到中间的节点呢 - 中间节点即为链表的一半,那咱们使用一个快指针一次走两步,一个慢指针一次走一步,那么快指针走到尾时,慢指针应该走到链表中间。同时要注意区分链表长度是奇数仍是偶数:若是是奇数的话,正中间的节点不须要作判断,应该用它先后两个节点开始比较。
最后的代码以下:
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */
/** * @param {ListNode} head * @return {boolean} */
var isPalindrome = function(head) {
if(!head) return true;
if(!head.next) return true;
let fast = head.next, slow = head;
let pair = null;
while(fast != null && fast.next != null) {
slow.next.pre = slow;
slow = slow.next;
fast = fast.next.next;
}
if(!fast || fast.next) {
// 奇数
pair = slow.next;
slow = slow.pre;
} else {
// 偶数
pair = slow.next;
}
while(pair) {
if(pair.val !== slow.val) return false;
pair = pair.next;
slow = slow.pre;
}
return true;
};
复制代码
给定一个数组nums,写一个函数将全部0移动到它的末尾,同时保持非零元素的相对顺序。 额外要求:
zeroesNums
,而后将每个非零的数向前移动 zeroesNums
,最后在数组末尾填上 zero/** * @param {number[]} nums * @return {void} Do not return anything, modify nums in-place instead. */
var moveZeroes = function(nums) {
let zeroesNums = 0;
nums.forEach((num, index) => {
if(num === 0) {
zeroesNums++;
} else {
nums[index - zeroesNums] = num;
}
});
for(let i = nums.length - 1; zeroesNums > 0; i--) {
nums[i] = 0;
zeroesNums--;
}
};
复制代码
给一颗每一个节点都是整数(可正可负)的二叉树,求有多少条路径加起来等于一个给定值。注意,路径不须要在根或叶子处开始或结束,但必须向下(即仅从父节点行进到子节点)。
解法一: 暴力解,使用 BFS 和递归来搜索符合条件的路径。须要注意的是这种方法没有利用任何缓存,即计算每条路径和的时候都从新遍历了全部路径节点。时间复杂度为 O(n²)
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @param {number} sum * @return {number} */
var pathSum = function(root, sum) {
if(!root) return 0;
let result = 0;
const queue = [root];
while(stack.length) {
const node = queue.shift();
result += reslove(node, sum);
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
return result
};
function reslove(root, sum) {
if(!root) return 0;
let result = 0;
if(sum === root.val) result++;
return result + reslove(root.left, sum - root.val) + reslove(root.right, sum - root.val);
}
复制代码
解法二: 若是不想用 queue
,那也能够直接用递归来作搜索
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @param {number} sum * @return {number} */
var pathSum = function(root, sum) {
if(!root) return 0;
return reslove(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
};
function reslove(root, sum) {
if(!root) return 0;
return ((sum === root.val) ? 1 : 0) + reslove(root.left, sum - root.val) + reslove(root.right, sum - root.val);
}
复制代码
解法三(O(n)): 在前两种解法中,咱们自顶而下重复遍历了每层节点(第一层被遍历一次,第二层被遍历两次,……)。这个时候咱们就该想办法利用缓存来减小遍历次数。
所以便有了以下 O(n) 复杂度的解法(思路写在了注释中了)
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @param {number} sum * @return {number} */
var pathSum = function(root, sum) {
// 缓存
const hashMap = {};
let currentSum = 0;
return pathSumRecursive(root, currentSum, hashMap, sum);
};
function pathSumRecursive(node, currentSum, hashMap, target) {
if (!node) {
return 0;
}
const newCurrentSum = currentSum + node.val;
// 看一看能不能利用以前的缓存,巧妙的在一次遍历中算出了全部线段
// 当前路径和 - 目标值 —— 本质是看 中间有没有一段路径和 等于 目标值
// 好比 2 - 5 - 3 的路径, 目标为 8,那么在 3 这个节点时,路径和为 10 , 减去目标值8 后为 2, 以前路径上有1条路线和为 2,所以中间有一段和为目标值 8
let totalPaths = hashMap[newCurrentSum - target] || 0;
if (newCurrentSum === target) {
totalPaths++;
}
// 更新一下缓存
if (hashMap[newCurrentSum]) {
hashMap[newCurrentSum]++;
} else {
hashMap[newCurrentSum] = 1;
}
totalPaths += pathSumRecursive(node.left, newCurrentSum, hashMap, target);
totalPaths += pathSumRecursive(node.right, newCurrentSum, hashMap, target);
// 因为是共用一个缓存,所以遍历完后续节点后,要在退回上一层的时候把自身从缓存中删掉,来保证缓存数据的正确性(只应该有以前路径的)
hashMap[newCurrentSum]--;
return totalPaths;
}
复制代码
在字符串中寻找同构体:给定一个字符串s和一个非空字符串p,找到s中p的同构体的全部起始索引。什么是同构体:两个字符串字母同样,字母在字符串中的顺序可能不同,好比 ab 和 ba 是同构的
解法一:
对于这个首先想到的是利用缓存来提升效率,这里咱们先才用 Map 的形式作映射。同时使用滑动窗口 - 两个指针(下标)来指向当前子串。而后从前日后扫,来经过 Map 看是否是匹配。若是
/** * @param {string} s * @param {string} p * @return {number[]} */
var findAnagrams = function(s, p) {
const targetMap = {};
// 构建map
for(let char of p) {
if(targetMap[char]) {
targetMap[char]++;
} else {
targetMap[char] = 1;
}
}
let start = 0;
let cacheLen = p.length;
const result = [];
for(let i = 0; i < s.length; i++) {
const char = s[i];
// 若是 char 还有
if(targetMap[char]) {
targetMap[char]--;
cacheLen--;
// 若是都匹配上了
if(cacheLen === 0) {
result.push(start); // 推动去
// 全部的向前移动一位
targetMap[s[start]]++;
start++;
cacheLen++;
}
} else if(targetMap[char] === 0) {
// char 有,可是超过个数了,就要向前走把char去掉一个
while(s[start] !== char) {
targetMap[s[start]]++;
start++;
cacheLen++;
}
start++;
} else {
// char 根本没有,就跳过以前这段
while(start < i) {
targetMap[s[start]]++;
start++;
}
start++;
cacheLen = p.length;
}
}
return result;
};
复制代码
解法二: 上图中用 Obj 作 Mapping,咱们也能够用数组结合字符下标来作 Mapping
/** * @param {string} s * @param {string} p * @return {number[]} */
var findAnagrams = function (s2, s1) {
const map = Array(128).fill(0);
let start = 0,
end = 0,
counter = s1.length;
const res = [];
for (let i = 0; i < s1.length; ++i) {
map[s1.charCodeAt(i)]++;
}
while (end < s2.length) {
if (map[s2.charCodeAt(end)] > 0) {
counter--;
}
map[s2.charCodeAt(end)]--;
end++;
while (counter == 0) {
if (end - start == s1.length) {
res.push(start);
}
if (map[s2.charCodeAt(start)] == 0) {
counter++;
}
map[s2.charCodeAt(start)]++;
start++;
}
}
return res;
};
复制代码
常规解法:由于题目上给出条件说数组里的数字都在 [1, n],且要求不适用额外空间,所以能够想到该题为套路题:对原位置上的数字移动/加减/位运算等解法。 此题常规能够选用反转对应位置上数字的方法:把出现的数字的对应位上的数字变为负数,而后遍历找出那些正数,其下标+1则为没有出现过的数字
/** * @param {number[]} nums * @return {number[]} */
var findDisappearedNumbers = function(nums) {
nums.forEach(num => {
num = Math.abs(num);
if(nums[num - 1] > 0) {
nums[num - 1] = -nums[num - 1]
}
});
const result = [];
nums.forEach((num, index) => {
if(num > 0) {
result.push(index + 1);
}
});
return result;
};
复制代码
位运算骚操做版: 首先须要简单理解几个位运算是干什么的:
|
运算,则会把一个数(不管正负)变成负数(只修改符号位)&
运算,则会把一个数(不管正负)变成正数(只修改符号位) 该解法用以上方式避开对符号的判断/** * @param {number[]} nums * @return {number[]} */
var findDisappearedNumbers = function(nums) {
for (var i = 0; i < nums.length; i++) {
nums[(nums[i] & ((1 << 31) - 1)) - 1] |= (1 << 31);
// 统一正负数运算 变成负数
}
ans = [];
for (var i = 0; i < nums.length; i++) {
// 若是不是负数,就推动去
if ((nums[i] & (1 << 31)) != (1 << 31))
ans.push(i+1);
}
return ans;
};
复制代码
Hamming Distance 表示两个等长字符串在对应位置上不一样字符的数目,也度量了经过替换字符的方式将字符串x变成y所须要的最小的替换次数。
1.常规解法:转成二进制之后一位一位的算,须要手动补位或手动判断 undefined
/** * @param {number} x * @param {number} y * @return {number} */
var hammingDistance = function (x, y) {
let binaryX = x.toString(2);
let binaryY = y.toString(2);
const len = Math.max(binaryX.length, binaryY.length);
if (binaryX.length < len) {
binaryX = binaryX.padStart(len, '0')
} else {
binaryY = binaryY.padStart(len, '0')
};
let result = 0;
for (let i = len - 1; i >= 0; i--) {
if (binaryX[i] !== (binaryY[i])) {
result++
}
}
return result;
};
复制代码
2.位运算:按位异或,不须要考虑补长度,更简洁
/** * @param {number} x * @param {number} y * @return {number} */
var hammingDistance = function (x, y) {
var res = x ^ y;
var count = 0;
while (res != 0) {
if (res % 2 == 1) count++;
res = Math.floor(res / 2); // res = res >> 1;
}
return count;
};
复制代码
二叉搜索树上的每个节点要加上全部大于他的节点的值:原始BST的每一个 key 都更改成原始 key 加上大于BST中原始 key 的全部 key 的总和。
解法一:
cacheVal
来缓存比当前节点大的值来达到 O(n) 的时间复杂度cacheVal
的传递而不是在外层保存该值(麻烦一点,由于须要处理右子树最左节点:代码29行)/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {TreeNode} */
var convertBST = function(root) {
if(root) {
converVal(root);
return root;
} else {
return [];
}
};
function converVal(root, cacheVal = 0) {
if(root.right) {
cacheVal = converVal(root.right, cacheVal);
}
root.val += cacheVal;
cacheVal = root.val;
if(root.left) {
// 处理右子树最左节点,返回给上一层递归来使用(此时右子树最左节点为上一层节点须要加的值)
return converVal(root.left, cacheVal);
}
return root.val;
}
复制代码
解法二:把解法一中的 cacheVal
提出来放在外围搞一个闭包,而后就不用每次递归传进去了,这样只须要从大到小遍历便可,简单易懂。
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {TreeNode} */
var convertBST = function(root) {
let sum = 0;
return function inner(root) {
if (root == null) return null;
inner(root.right);
root.val += sum;
sum = root.val;
inner(root.left);
return root;
}(root);
};
复制代码
给定二叉树,计算树的直径长度 - 二叉树的直径是树中任意两个节点之间最长路径的长度。此路径可能会也可能不会经过根节点。
递归,由于是寻找一条最长的路径,所以分红两个状况考虑:
最后找出这二者更大的一个便可
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} root * @return {number} */
var diameterOfBinaryTree = function(root) {
if(!root) return 0;
// 返回一个最大的
return Math.max(...diameterOfSubtree(root)) - 1;
};
function diameterOfSubtree(root) {
if(!root.left && !root.right) return [1, 1];
let left = 0, leftBig = 0, right = 0, rightBig = 0;
if(root.left) [left, leftBig] = diameterOfSubtree(root.left);
if(root.right) [right, rightBig] = diameterOfSubtree(root.right);
// 当前子树最长路径
const cacheBig = Math.max(leftBig, rightBig, left + right + 1);
return [1 + Math.max(left, right), cacheBig];
}
复制代码
判断一棵树是否是另外一颗树的子结构
解法一:直接递归看一下是否是子树,但这样有重复遍历
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} s * @param {TreeNode} t * @return {boolean} */
var isSubtree = function (s, t) {
return !!(subtree(s, t) ||
(s.left && isSubtree(s.left, t)) ||
(s.right && isSubtree(s.right, t)));
};
function subtree(s, t) {
if (!s && !t) return true;
return ((s || {}).val === (t || {}).val) &&
subtree(s.left, t.left) &&
subtree(s.right, t.right);
}
复制代码
解法二:前序遍历树,存成字符串,而后看看 source 里面是否是包含 target 便可
var isSubtree = function(s, t) {
let string1 = {str: ""};
let string2 = {str: ""};
treeString(s, string1);
treeString(t, string2);
return string1.str.includes(string2.str);
}
function treeString(root, string) {
if (!root) {
string.str += "X";
return;
}
string.str += `,${root.val},`
treeString(root.left, string);
treeString(root.right, string);
}
复制代码
最短未排序连续子数组:给定一个整数数组,您须要找到一个最短的连续的子数组,要求是若是序对此子数组进行升序排序后,整个数组也将按升序排序。
第一种简单的方法是把数组进行排序,那么原数组和新数组不同的个数即为界限,可是这种的复杂度为 O(nlgn) (排序后遍历)
或者咱们能够用两个指针,一个从前向后,一个从后向前
而后就能得出哪一段是没有按照升序排序的了
/** * @param {number[]} nums * @return {number} */
var findUnsortedSubarray = function(nums) {
let last = 0, first = -1, max = -Infinity, min = Infinity;
for(let i = 0, j = nums.length - 1; j >= 0; j--, i++){
max = Math.max(max, nums[i]);
if(nums[i] !== max) first = i;
min = Math.min(min, nums[j]);
if(nums[j] !== min) last = j;
}
return first - last + 1;
};
复制代码
该题有疑似有恶性 bug - testcase:
Input: [] []
Expected: []
Output: null
复制代码
递归:
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */
/** * @param {TreeNode} t1 * @param {TreeNode} t2 * @return {TreeNode} */
var mergeTrees = function(t1, t2) {
if (!t1) {
return t2;
}
if (!t2) {
return t1;
}
t1.val += t2.val;
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
return t1;
};
复制代码
非递归:利用栈加树的层次遍历写法
var mergeTrees = function (t1, t2) {
if (t1 === null) {
return t2;
}
const stack = [];
stack.push([t1, t2]);
while (stack.length !== 0) {
const t = stack.pop();
if (t[0] === null || t[1] === null) {
continue;
}
t[0].val += t[1].val;
if (t[0].left === null) {
t[0].left = t[1].left;
} else {
stack.push([t[0].left, t[1].left]);
}
if (t[0].right === null) {
t[0].right = t[1].right;
} else {
stack.push([t[0].right, t[1].right]);
}
}
return t1;
};
复制代码