前序遍历
preOrder(root) {
// 中(节点输出) 左 右
/**
* 1、二叉树只有左右节点的概念(从root 根结点开始便利 后面所有节点非左即右)
* 2、右边先入栈、再左边节点入栈,迭代输出,可以确保所有节点都是左侧节点优先出栈
*/
if (!root) return []
const result = []
const stack = [root]
while (stack.length > 0) {
const getNode = stack.pop()
if (getNode.right) {
stack.push(getNode.right)
}
if (getNode.left) {
stack.push(getNode.left)
}
result.push(getNode.element)
}
return result
}
后序遍历
postOrder(root) {
// 左 右 中
/**
* 区别于前序遍历
* 1、从根节点开始,遍历左右子节点,如果遇到左节点,继续往下查找
* 2、如果左侧节点没有值了,则开始遍历右子节点
* 3、此时右子节点相当于根结点重复1的流程
*
* 由于节点是从顶层开始往子节点遍历的,因此操作节点应该采用 unshift的方式
*/
if (!root) return []
const result = [], stack = [root]
while (stack.length > 0) {
const getNode = stack.pop()
result.unshift(getNode.element)
if (getNode.left) {
stack.push(getNode.left)
}
if (getNode.right) {
stack.push(getNode.right)
}
}
return result
}
中序遍历
inOrder(root) {
if (!root) {
return []
}
const stack = [], result = []
while (stack.length > 0 || root) {
while(root) {
stack.push(root)
root = root.left
}
const data = stack.pop()
result.push(data.element)
root = data.right
}
return result
}
两数之和
/**
* 1. 两数之和
*
*
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
## 思路
- 从时间复杂度角度出发,采用暴力解法,两层嵌套循环,,时间复杂度为 On2,不可取
- 通过map存储对象的方式可以降低时间复杂度
- 通过将key的差值进行存储,有利于下一次进行比较,比如 第一次 map.has(2) 不存在 那么就set map.set(9 - 2) = 2的序号,第二位 7 map.has(7)存在了,那么与他匹配的序号就是 map.get(7) = 2 了
*/
var twoSum = function(nums, target) {
const map = new Map()
for (var i = 0; i < nums.length; i++) {
if (!map.has(nums[i])) {
map.set(target - nums[i], i)
} else {
return [map.get(nums[i]), i]
}
}
};
整数反转
/**
* 整数反转
*
*
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
*/
/**
* @param {number} x
* @return {number}
*/
var reverse = function(x) {
var flag = x > 0, newValue = 0
if (!flag) x = -x
while (x > 9) {
newValue = newValue * 10 + x % 10 * 10
x = Math.floor(x / 10)
}
newValue += x
if (!flag) {
newValue = -newValue
}
if (newValue > Math.pow(2, 31) - 1 || newValue < Math.pow(2, 31) * -1) {
return 0
} else {
return newValue
}
};
回文数
/**
* 回文数
*
* 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
*/
/**
* @param {number} x
* @return {boolean}
*
* 输入:x = 121
* 输出:true
*
* 输入:x = -121
* 输出:false
*
* 输入:x = 10
* 输出:false
*
*
* 思路:
*
*/
var isPalindrome = function(x) {
if (x < 0 || (x % 10 === 0 && x !== 0)) {
return false
}
let revertedNum = 0
while (x > revertedNum) {
// 从右往左指针移动进行截取,revertedNum为左侧的数再进行反转
revertedNum = revertedNum * 10 + x % 10
// 从右往左指针移动进行截取,x为右侧的数
x = Math.floor(x / 10)
// 因此如果是奇数位那么指针必须在正中间偏左的位置才能保证 x < revertedNum 此时跳出循环
// 如果是偶数位,就有可能对半切(1122)或者revertedNum占三个 x 只有一个 (2211)
}
console.log(x, revertedNum)
// revertedNum === x 偶数位 revertedNum / 10 === x 奇数位(因为奇数位中间那一项不用管)
return (revertedNum === x || Math.floor(revertedNum / 10) === x)
};
// console.log(isPalindrome(121))
最长公共前缀
/**
* 14. 最长公共前缀
*
* 编写一个函数来查找字符串数组中的最长公共前缀。
* 如果不存在公共前缀,返回空字符串 ""。
*
* 输入:strs = ["flower","flow","flight"]
* 输出:"fl"
*
*
* 思路:
* 1、边界判断,如果strs 长度为 0 获取 1 取第一项作为公共前缀
* 2、先将数组内第一个数据作为公共前缀,依次遍历 strs
* 3、开辟一个新的循环, 依次比较strs[0], 如果 不相同记录当前序号
* 4、通过比较获取最小的序号即为公共前缀序号
*/
/**
* @param {string[]} strs
* @return {string}
*/
var longestCommonPrefix = function(strs) {
if (strs.length === 0 || strs.length === 1) return strs[0]
let result = strs[0], matchIndex = null
for (let j = 1; j < strs.length; j++) {
// 遍历每一项
let i = 0
for (; i < result.length && strs[j].length; i++) {
if (result[i] !== strs[j][i]) break
}
if (matchIndex === null) {
matchIndex = i
} else {
matchIndex = Math.min(matchIndex, i)
}
}
return result.slice(0, matchIndex)
};
有效的括号
/**
* 20. 有效的括号
*
* 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
输入:s = "([)]"
输出:false
输入:s = "()[]{}"
输出:true
*/
const isValid = (str) => {
const map = new Map(), container = []
const sample = new Map()
sample.set('(', ')')
sample.set('{', '}')
sample.set('[', ']')
for (let i = 0; i < str.length; i++) {
if (sample.get(str[i])) {
// 存在的话 是左半边
container.push(sample.get(str[i]))
} else {
// 不存在(右半边),将数组最后一位取出来并与之对比
if (container.pop() === str[i]) {
continue
} else {
return false
}
}
}
return container.length === 0
}
// console.log(isValid('([)]'))
删除有序数组中的重复项
/**
* 26. 删除有序数组中的重复项
*
* 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
*
* 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
*
* 说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例一
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例二
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
*/
var removeDuplicates = function(nums) {
const n = nums.length;
if (n === 0) {
return 0;
}
let fast = 1, slow = 1;
while (fast < n) {
// 因为是有序数组,因此只要相邻比较,这样就能确保中间不会发生交叉
if (nums[fast] !== nums[fast - 1]) {
// slow记录当前没有重复的位置,因为是个数,不是序号,因此是从1开始的
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
};
搜索插入位置
/**
* 35. 搜索插入位置
*
* 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
*
* 请必须使用时间复杂度为 O(log n) 的算法。
*
* 示例一
* 输入: nums = [1,3,5,6], target = 5
* 输出: 2
*
* 示例二
* 输入: nums = [1,3,5,6], target = 2
* 输出: 1
*
* 示例三
* 输入: nums = [1,3,5,6], target = 7
* 输出: 4
*
* 示例四
* 输入: nums = [1,3,5,6], target = 0
* 输出: 0
*
* 示例五
* 输入: nums = [1], target = 0
* 输出: 0
*/
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*
* 二分法解
*/
var searchInsert = function(nums, target) {
let len = nums.length, l = 0, r = len - 1
while (l <= r) {
const mid = Math.floor(l + (r - l) / 2)
if (nums[mid] < target) {
l = mid + 1
} else {
r = mid - 1
}
}
return l
};
// console.log(searchInsert([1,3,5,6], 5))
// console.log(searchInsert([1,3,5,6], 2))
// console.log(searchInsert([1,3,5,6], 7))
// console.log(searchInsert([1,3,5,6], 0))
// console.log(searchInsert([1], 0))
最大子数组和
/**
* 53. 最大子数组和
*
* 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
*
* 子数组 是数组中的一个连续部分。
*
* 输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
* 输出:6
* 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
*
* 输入:nums = [1]
* 输出:1
*
* 输入:nums = [5,4,-1,7,8]
* 输出:23
*/
/**
* @param {number[]} nums
* @return {number}
*
*
* 思路:如果之前的数之和加上自身后 反而变小了,显然这个数组没有继续下去的必要了,那就新开一个数组,那样就不至于会接上那些累赘了
*/
var maxSubArray = function(nums) {
const memo = []
memo[0] = nums[0]
let max = nums[0]
for (let i = 1; i < nums.length; i++) {
memo[i] = Math.max(nums[i] + memo[i - 1], nums[i])
max = Math.max(max, memo[i])
}
return max
};
console.log(maxSubArray([5,4,-1,7,8]))
合并两个有序数组
/**
* 合并两个有序数组
*
*
* 输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
* 输出:[1,2,2,3,5,6]
* 解释:需要合并 [1,2,3] 和 [2,5,6] 。
* 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
*/
/**
* @param {number[]} nums1
* @param {number} m
* @param {number[]} nums2
* @param {number} n
* @return {void} Do not return anything, modify nums1 in-place instead.
*/
var merge = function(nums1, m, nums2, n) {
const result = []
nums1 = nums1.slice(0, m)
nums2 = nums2.slice(0, n)
while (nums1.length && nums2.length) {
if (nums1[0] < nums2[0]) {
result.push(nums1.shift())
} else {
result.push(nums2.shift())
}
}
while (nums1.length > 0) {
result.push(nums1.shift())
}
while (nums2.length > 0) {
result.push(nums2.shift())
}
return result
};
// console.log(merge([1,2,3,0,0,0],3, [2,5,6], 3))
斐波那契数列
/**
* 斐波那契数列
*
*/
const fib = function(n) {
if (n <= 1) return n
const cache = []
cache[0] = 0
cache[1] = 1
function memoize(number) {
if (cache[number] !== undefined) {
return cache[number]
}
cache[number] = memoize(number - 1) + memoize(number - 2)
return cache[number]
}
const result = memoize(n)
return result
}
// 优化空间复杂度
const fib2 = function(n) {
if (n <= 1) return n
let prev2 = 0
let prev1 = 1
let result = 0
for (let i = 2; i <=n; i++) {
result = prev1 + prev2
prev2 = prev1
prev1 = result
}
return result
}
求二维数组的全排列组合
var arrays = [["a0","a1"],["b0","b1"],["c0","c1"],["d0","d1"]];
var array = getArrayByArrays(arrays);
function getArrayByArrays(arrays) {
var arr = [""];
for(var i = 0;i<arrays.length;i++) {
arr = getValuesByArray(arr,arrays[i]);
}
return arr;
}
function getValuesByArray(arr1,arr2) {
var arr = [];
console.log(arr1)
for(var i=0;i<arr1.length;i++) {
var v1 = arr1[i];
for(var j=0;j<arr2.length;j++) {
var v2 = arr2[j];
var value = v1 + v2;
arr.push(value);
};
};
return arr;
}
console.log(array)
子集
/**
* 78. 子集
*
* 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
* 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
*/
/**
* @param {number[]} nums
* @return {number[][]}
*
*
function backtrack(start, curr) {
for (let i = start; i < nums.length; i++) {
1、把nums[i] 加入curr数组
2、backtrack(i + 1, curr)
3、把curr数组的最后一个元素移除
}
}
*/
var subsets = function(nums) {
const result = []
function backtrack(start, curr) {
result.push([...curr])
for(let i = start; i < nums.length; i++) {
curr.push(nums[i])
backtrack(i + 1, curr)
curr.pop()
}
}
backtrack(0, [])
return result
};
全排列
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
const res = [];
const used = {};
function dfs(path) {
if (path.length == nums.length) { // 个数选够了
res.push(path.slice()); // 拷贝一份path,加入解集res
return; // 结束当前递归分支
}
for (const num of nums) { // for枚举出每个可选的选项
// if (path.includes(num)) continue; // 别这么写!查找是O(n),增加时间复杂度
if (used[num]) continue; // 使用过的,跳过
path.push(num); // 选择当前的数,加入path
used[num] = true; // 记录一下 使用了
dfs(path); // 基于选了当前的数,递归
path.pop(); // 上一句的递归结束,回溯,将最后选的数pop出来
used[num] = false; // 撤销这个记录
}
}
dfs([]); // 递归的入口,空path传进去
return res;
};
二维数组全排列
/**
* 二维数组全排列
*
*/
function getArrayByArrays(arrays) {
var arr = [""];
for(var i = 0;i<arrays.length;i++) {
// 通过来源两个合并的数组,继续向下一个数组进行合并,递归处理,有点像柯里化
arr = getValuesByArray(arr,arrays[i]);
}
return arr;
}
function getValuesByArray(arr1,arr2) {
// 合并两个数组
var arr = [];
for(var i=0;i<arr1.length;i++) {
for(var j=0;j<arr2.length;j++) {
arr.push(arr2[j] + arr1[i]);
};
};
return arr;
}
var arrays = [["a0","a1"],["b0","b1","b2"]];
var array = getArrayByArrays(arrays);