楼主于数个月前面了wxg(经验2.5年),前前后后3、4面,几乎每一面都有算法,哈哈哈,不愧是微信事业群,狠重视算法,结果蛮遗憾的,没有能走到GM面[哭的很大声]。 但整个过程下来,还是蛮有收获的。一方面自己对于数据结构有了更深的理解,一方面也对认识了自己的不足。在此也感谢给予我帮助的大佬。再接再厉,下次再战。
以下是一些记录。
一、有印象的几道算法题如下:
1、实现二叉树镜像(翻转)
function treeFun(root) {
if (root === null) return null;
function treeList(node) {
let left = node.left;
let right = node.right;
node.left = right;
node.right = left;
}
// 前序遍历 || 后序遍历
treeFun(root);
treeList(root.left);
treeList(root.right);
return root;
}
2、使用非递归的方式实现二叉树前序遍历
// 前序遍历
const preorderTraversal = (root) => {
const list = [];
const stack = [];
if(root) stack.push(root)
while(stack.length > 0) {
const curNode = stack.pop()
list.push(curNode.val)
if(curNode.right !== null) {
stack.push(curNode.right)
}
if(curNode.left !== null) {
stack.push(curNode.left)
}
}
return list
}
3、字符串转整形
let strToNumberFun = function (str) {
let res = parseInt(str); // 如果遇到不能转数字的,则为NAN
let max = Math.pow(2, 31);
let min = Math.pow(-2, 31);
if (isNaN(res)) {
return 0;
} else if (res > max) {
return max;
} else if (res < min) {
return min;
} else {
return res;
}
};
还有没有其他的解法? 另外一种解法是使用正则,有兴趣的同学可以试试。
4、找出数组中的质数
// 省略
5、写一个快排
function quickArrFun(arr) {
let leng = arr.length;
if (leng === 1) return arr;
let quick = arr[0];
let left = [],
right = [],
res = [];
for (let i = 1; i < leng; i++) {
if (quick < arr[i]) right.push(arr[i]);
if (quick > arr[i]) left.push(arr[i]);
}
res = [...quickArrFun(left), quick, ...quickArrFun(right)];
return res;
}
6、实现一个apply
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
其他的就不一一列举了...
二、以下是自己复习到的部分算法:
1、二叉树
/*二叉树 start*/
function treeFun(root) {
if (root === null) return null;
let temp = root.left;
root.left = root.right;
root.right = temp;
treeFun(root.left);
treeFun(root.right);
}
// 二叉树中序遍历
function treeFunMid(root) {
let result = [];
var inorderTraversal = (node) => {
if (node) {
// 先遍历左子树
inorderTraversal(node.left);
// 再根节点
result.push(node.val);
// 最后遍历右子树
inorderTraversal(node.right);
}
};
inorderTraversal(root);
return result;
}
const inorderTraversal = (root) => {
let list = [];
let stack = [];
let node = root;
while (node || stack.length) {
// 遍历左子树
while (node) {
stack.push(node);
node = node.left;
}
node = stack.pop();
list.push(node.val);
node = node.right;
}
return list;
};
var inorderTraversal = function (root) {
if (root === null) return [];
let stack = [root]; // 利用栈来遍历树
let res = [];
while (stack.length) {
let current = stack.pop();
if (current === null) continue;
if (!current.visited) {
// 节点未被访问
current.visited = true; // 设置了一个变量,标记该节点是否被访问了
stack.push(current.right, current, current.left); // 不管三七二十一,按照右根左的顺序全部入栈,即使有null的也会在上面continue的时候跳过
} else {
// 节点已经被访问了,输出值,遍历栈里的下一个节点
res.push(current.val);
}
}
return res;
};
// 二叉树的第k大节点
function treeKFun(root, k) {
if (root === null) return null;
let res = [];
function treeNode(node) {
treeNode(node.left);
res.push(node.val);
treeNode(node.right);
}
treeNode(root);
return res[res.length - k];
}
/**
leetcode 112. 路径之和
*/
/**
* @param {TreeNode} root
* @param {number} targetSum
* @return {boolean}
*/
var hasPathSum = function(root, targetSum) {
if(root === null) return false;
if(root.left === null && root.right === null) return targetSum === root.val;
targetSum = targetSum - root.val;
return hasPathSum(root.left, targetSum) || hasPathSum(root.right, targetSum);
};
/*二叉树 end*/
2、排序
/*排序 start*/
// 冒泡排序
function arrFun(arr) {
let leng = arr.length;
if (leng === 1) return arr;
for (let i = 0; i < leng; i++) {
for (let j = i + 1; j < leng; j++) {
if (arr[i] > arr[j]) {
let temp = arr[j];
arr[i] = temp;
arr[j] = arr[i];
}
}
}
return arr;
}
// 选择排序
function arrSelectFun(arr) {
let leng = arr.length;
if (leng === 1) return arr;
for (let i = 0; i < leng; i++) {
let mix = i;
for (let j = i + 1; j < leng; j++) {
if (arr[mix] > arr[j]) {
mix = j;
}
let temp = arr[i];
arr[i] = arr[mix];
arr[mix] = temp;
}
}
return arr;
}
// 快速排序
function quickArrFun(arr) {
let leng = arr.length;
if (leng === 1) return arr;
let quick = arr[0];
let left = [],
right = [],
res = [];
for (let i = 1; i < leng; i++) {
if (quick < arr[i]) right.push(arr[i]);
if (quick > arr[i]) left.push(arr[i]);
}
res = [...quickArrFun(left), quick, ...quickArrFun(right)];
return res;
}
/*排序 end*/
3、链表
/*链表 start*/
/* leetcode 19. 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
*/
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function (head, n) {
var ret = new ListNode(0, head);
var slow = (fast = ret);
while (n--) {
fast = fast.next;
}
if (!fast) return ret.next;
while (fast.next) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return ret;
};
// leetcode024. `反转链表`
/*
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
*/
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function (head) {
if (!head || !head.next) return head;
let temp = null,
pre = null,
cur = head;
while (cur) {
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
// temp = cur = null;
return pre;
};
/*
* @lc app=leetcode.cn id=19 lang=javascript
*
* [19] 删除链表的倒数第 N 个结点
*/
// @lc code=start
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function (head, n) {
let fast = head,
slow = head;
for (let i = 0; i < n; i++) {
fast = fast.next;
}
while (!fast) {
return head.next;
}
while (fast.next) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return head;
};
// @lc code=end
/*
* @lc app=leetcode.cn id=21 lang=javascript
*
* [21] 合并两个有序链表
*/
// @lc code=start
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function (list1, list2) {
// 如果list1或者list2一开始就是空链表,那么没有任何操作需要合并,返回对方即可
if (list1 === null) return list2;
if (list2 === null) return list1;
if (list1.val < list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}
if (list1.val > list2.val) {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
};
// @lc code=end
/*链表 end*/
4、LeetCode 相关
/*LeetCode start*/
// leetcode 88、合并两个有序数组
/*输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
*/
function concactArrFun(nums1, m, nums2, n) {
nums1.splice(m, n, ...nums2);
nums1.sort((a, b) => {
a - b;
});
return nums1;
}
// leetcode415 字符串相加
/*给定两个字符串形式的非负整数 num1 和 num2 ,计算它们的和。例如:"111" + ”2222“ = ”2333“*/
function addStringFun(num1, num2) {
let result = '';
let temp = 0;
let arr1 = num1.splice('');
let arr2 = num2.splice('');
while (arr1.length || arr2.length || temp) {
temp += ~~arr1.pop() + ~~arr2.pop();
result = (temp % 10) + result;
temp = ~~(result / 10);
}
return result.replace(/^0+/, '');
}
// 腾讯&leetcode43:字符串相乘
/* 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = "2", num2 = "3"
输出: "6"
示例 2:
输入: num1 = "123", num2 = "456"
输出: "56088"
*/
function stringMultiply(num1, num2) {
let result = 0;
let temp = 0;
let arr1 = num2.splice('');
while (arr1.length) {
result += ~~num1 * ~~arr1.pop() * factorial(temp);
temp += 1;
}
function factorial(n) {
if (n === 0) return 1;
return 10 * factorial(n - 1);
}
return result;
}
// 5. 最长回文子串 leetcode
var longestPalindrome = function (s) {
if (s.length < 2) {
return s;
}
let res = '';
for (let i = 0; i < s.length; i++) {
// 回文子串长度是奇数
helper(i, i);
// 回文子串长度是偶数
helper(i, i + 1);
}
function helper(m, n) {
while (m >= 0 && n < s.length && s[m] == s[n]) {
m--;
n++;
}
// 注意此处m,n的值循环完后 是恰好不满足循环条件的时刻
// 此时m到n的距离为n-m+1,但是mn两个边界不能取 所以应该取m+1到n-1的区间 长度是n-m-1
if (n - m - 1 > res.length) {
// slice也要取[m+1,n-1]这个区间
res = s.slice(m + 1, n);
}
}
return res;
};
console.log(longestPalindrome('asdasddsadfg'));
/* 腾讯&leetcode647:回文子串 #107
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
*/
// 暴力拆解
function backStringFun(str) {
let count = 0;
let leng = str.length;
for (let i = 0; i < leng; i++) {
for (let j = i; j < leng; j++) {
if (addFun(str.substring(i, j + 1))) {
count++;
}
}
}
function addFun(res) {
let i = 0,
j = res.length - 1;
while (i < j) {
if (res[i] != s[j]) return false;
i++;
j--;
}
return true;
}
}
/*寻找两个正序数组的中位数
https://github.com/sisterAn/JavaScript-Algorithms/issues/162
*/
const arr1 = [1, 2, 10];
const arr2 = [2, 5, 7, 8];
function findMiddleNumber(arr1, arr2) {
// 容错处理
if (!arr1.length && !arr2.length) return null;
// 合并并排序
const total = [...arr1, ...arr2].sort((a, b) => a - b);
// 中位数索引
let midIndex = (total.length - 1) / 2;
// 两位
if (String(midIndex).includes('.')) {
const left = parseInt(midIndex);
const right = parseInt(midIndex) + 1;
const midNumber = (total[left] + total[right]) / 2;
return midNumber.toFixed(5);
} else {
// 一位
return total[midIndex].toFixed(5);
}
}
console.log(findMiddleNumber(arr1, arr2));
/*
const arr = [101,19,12,51,32,7,103,8];
问题一: 找出连续最大升序的数量
问题二: 找出不连续最大升序的数量
*/
const findLengthOfLCIS = (nums) => {
if (nums.length <= 1) return nums.length;
let max = 1,
count = 1;
for (let i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1]) {
count += 1;
} else {
count = 1;
}
max = Math.max(max, count);
}
return max;
};
function queryFn(arr) {
if (!arr.length) return 0;
let res = 1;
let num = 0;
let lastVal = 0;
for (let i = 0, len = arr.length - 1; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[j] > lastVal) {
num++;
lastVal = arr[j];
}
}
res = Math.max(res, num);
}
return res;
}
/*接雨水 https://github.com/sisterAn/JavaScript-Algorithms/issues/122
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)
。*/
// 双指针
function trap(height) {
let total = 0;
let left = 0,
right = height.length - 1,
leftMax = 0,
rightMax = 0;
while (left <= right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (leftMax < rightMax) {
total += leftMax - height[left];
left++;
} else {
total += rightMax - height[right];
right--;
}
}
return total;
}
/*
* @lc app=leetcode.cn id=1 lang=javascript
*
* [1] 两数之和
*/
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
/* 暴力拆解
for(let i=0; i<nums.length; i++) {
for(let j=i+1; j<nums.length; j++) {
if(nums[i] + nums[j] === target) {
return [i, j]
}
}
}
*/
// 哈希表
let map = new Map();
for(let i=0, leng = nums.length; i< leng; i++) {
if(map.has(target - nums[i])) {
return [map.get(target - nums[i]), i]
} else {
map.set(nums[i], i)
}
}
};
/*
* @lc app=leetcode.cn id=3 lang=javascript
*
* [3] 无重复字符的最长子串
*/
// @lc code=start
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function (s) {
let len = s.length;
let arr = [];
let max = 0;
for (let i = 0; i < len; i++) {
if (arr.indexOf(s[i]) === -1) {
arr.push(s[i]);
} else {
arr.shift();
i--;
continue;
}
max = Math.max(max, arr.length);
}
return max;
};
// @lc code=end
/*
* @lc app=leetcode.cn id=5 lang=javascript
*
* [5] 最长回文子串
*/
// @lc code=start
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function (s) {
let start = -1;
let len = s.length;
let max = 0;
for (let i = 0; i < len; i++) {
let num = 1;
let l = i - 1;
while (s[i + 1] === s[i]) {
num++;
i++;
}
let r = i + 1;
while (s[l] === s[r] && s[l]) {
l--;
r++;
num += 2;
}
if (max < num) {
max = num;
start = l + 1;
}
}
return s.slice(start, start + max);
};
// @lc code=end
/*
* @lc app=leetcode.cn id=9 lang=javascript
*
* [9] 回文数
*/
// @lc code=start
/**
* @param {number} x
* @return {boolean}
*/
var isPalindrome = function (x) {
if (x < 0) return false;
let s = x.toString();
let len = s.length;
let max = 0;
for (let i = 0; i < len; i++) {
let num = 1;
let l = i - 1;
while (s[i + 1] === s[i]) {
i++;
num++;
}
let r = i + 1;
while (s[l] === s[r] && s[l]) {
l--;
r++;
num += 2;
}
if (max < num) {
max = num;
}
}
return max === len ? true : false;
};
// @lc code=end
/*
* @lc app=leetcode.cn id=22 lang=javascript
*
* [22] 括号生成
*/
// @lc code=start
/**
* @param {number} n
* @return {string[]}
*/
var generateParenthesis = function(n) {
let set = new Set(['()']);
for(let i = 2; i <= n; i++) {
let setChild = new Set();
for(let item of set) {
for(let sitem = 0; sitem < item.length; sitem++) {
setChild.add(item.slice(0, sitem) + '()' + item.slice(sitem))
}
}
set = setChild
}
return [...set];
};
// @lc code=end
/*
* @lc app=leetcode.cn id=169 lang=javascript
*
* [169] 多数元素
*/
// @lc code=start
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function (nums) {
let leng = nums.length;
let map = new Map();
for (let i = 0; i < leng; i++) {
let num = 1;
if (map.has(nums[i])) {
map.set(nums[i], map.get(nums[i]) + 1);
} else {
map.set(nums[i], num);
}
}
for (let item of map.entries()) {
// console.log(item[0], item[1]);
if (item[1] > leng / 2) {
return item[0];
}
}
};
// @lc code=end
/*LeetCode end*/