前端面试知识点整理——前端题库2(WXG)
文章目录
一、1013.将数组分成和相等的三个部分
/**
* @param {number[]} arr
* @return {boolean}
*/
var canThreePartsEqualSum = function(arr) {
//分成三部分 每一个部分都是sum/3
let sum = arr.reduce((a, b) => a + b);
let num = 3;
let temp = 0;
for(let k of arr) {
temp += k;
if(temp === sum/3) {
num--;
temp = 0;
}
}
return num <= 0;
};
二、704.二分查找
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
let left = 0;
let right = nums.length - 1;
while(left <= right) {
//这里floor和ceil都可以
let middle = Math.floor(left + (right - left)/2);
if(nums[middle] == target) {
return middle;
} else if(nums[middle] > target) {
right = middle - 1;
} else {
left = middle + 1;
}
}
return -1;
};
三、100.相同树
/**
* 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)
* }
*/
/**
* @param {TreeNode} p
* @param {TreeNode} q
* @return {boolean}
*/
var isSameTree = function(p, q) {
//深度优先搜索
if(p == null && q == null) return true;
if(p == null || q == null) return false;
if(p.val != q.val) return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
};
四、257.二叉树的所有路径
/**
* 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)
* }
*/
/**
* @param {TreeNode} root
* @return {string[]}
*/
var binaryTreePaths = function(root) {
let res = [];
let temp = '';
function adding(root, temp) {
if(root == null) return;
if(root.left == null && root.right == null) { //叶结点
temp = temp + root.val;
res.push(temp);
return;
} else {
temp = temp + root.val + '->';
}
adding(root.left, temp);
adding(root.right, temp);
}
adding(root, temp);
return res;
};
五、198.打家劫舍
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
//本来想用回溯来做的 算了写不出来
let max = 0;
if(nums.length == 0) return max;
if(nums.length == 1) return nums[0];
//动态规划 temp[i] = Math.max(temp[i - 1], temp[i - 2] + nums)
let temp = [];
temp[0] = nums[0];
temp[1] = Math.max(temp[0], nums[1]);
max = Math.max(temp[0], temp[1]);
for(let i = 2; i < nums.length; i++) {
temp[i] = Math.max(temp[i - 1], temp[i - 2] + nums[i]);
max = Math.max(max, temp[i]);
}
return max;
};
六、106.从中序与后序遍历序列构造二叉树
/**
* 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)
* }
*/
/**
* @param {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
//创建一个map存储所有的数组值和对应的下标 方便查找(对中序遍历数组的查找)
const map = new Map();
inorder.forEach((value, index) => {
map.set(value, index);
});
//对于后序遍历数组,从后往前取值,作为每一个根节点
let post;
post = postorder.length - 1;
//中序遍历数组的一棵子树的范围
const helper = (left, right) => {
if (left > right) {
return null;
}
const value = postorder[post];
const root = new TreeNode(value);
// 根据 root 所在位置分成左右两棵子树
const index = map.get(value); //获得在inorder中的位置
post--;
//这里要先建右子树 要注意postorder的从后往前的根状况
root.right = helper(index + 1, right);
root.left = helper(left, index - 1);
return root;
}
return helper(0, inorder.length - 1);
};
七、2.两数相加
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var addTwoNumbers = function(l1, l2) {
let addition = 0;
let head = null, tail = null;
while(l1 || l2) {
const n1 = l1? l1.val:0;
const n2 = l2? l2.val:0;
let sum = n1 + n2 + addition;
if(!head) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
addition = Math.floor(sum/10);
if(l1) {
l1 = l1.next;
}
if(l2) {
l2 = l2.next;
}
}
if(addition > 0) {
tail.next = new ListNode(addition);
}
return head;
};
八、153.寻找旋转排序数组中的最小值
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
//我的思路是用二分搜索
let min = nums[0];
function binary(left, right) {
if(left > right) return; //定义递归结束条件
let middle = Math.floor(left + (right - left)/2);
if(nums[middle] >= min) {
binary(middle + 1, right);
} else {
min = nums[middle];
binary(left, middle - 1);
}
}
let left = 0, right = nums.length - 1;
binary(left, right);
return min;
};
九、189.旋转数组
使用多种方法:
/**
* @param {number[]} nums
* @param {number} k
* @return {void} Do not return anything, modify nums in-place instead.
*/
var rotate = function(nums, k) {
for(let i = 0; i < k; i++) {
nums.unshift(nums.pop());
}
return nums;
};
十、4.寻找两个正序数组的中位数
自己做:(写得有点长)时间复杂度O(m + n)
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function(nums1, nums2) {
let m = nums1.length;
let n = nums2.length;
//记录中位数的位置
let middle = Math.floor((m + n + 1) / 2);
let cnt = 0; //判断目前取出的数字个数
let flag; //判断是两数相加/2 还是 直接中位数
if((m + n) % 2 === 0) {
flag = 1;
} else {
flag = 0;
}
let res; //中位数
while(nums1.length && nums2.length) {
if(nums1[0] <= nums2[0]) {
cnt++;
var temp = nums1.shift();
} else {
cnt++;
var temp = nums2.shift();
}
if(cnt == middle && flag) { //中位数要两者相加除二
res = temp;
}
if(cnt == middle + 1 && flag) {
res = (res + temp) / 2;
break;
}
if(cnt == middle && !flag) { //直接中位数
res = temp;
break;
}
}
while(nums1.length) {
cnt++;
var temp = nums1.shift();
if(cnt == middle && flag) { //中位数要两者相加除二
res = temp;
}
if(cnt == middle + 1 && flag) {
res = (res + temp) / 2;
break;
}
if(cnt == middle && !flag) { //直接中位数
res = temp;
break;
}
}
while(nums2.length) {
cnt++;
var temp = nums2.shift();
if(cnt == middle && flag) { //中位数要两者相加除二
res = temp;
}
if(cnt == middle + 1 && flag) {
res = (res + temp) / 2;
break;
}
if(cnt == middle && !flag) { //直接中位数
res = temp;
break;
}
}
return res;
};
O(log(m + n))搞了两个小时搞不出来,算了,,以下是存在漏洞的代码
var findMedianSortedArrays = function (nums1, nums2) {
let m = nums1.length;
let n = nums2.length;
let total = m + n;
if (total % 2 == 1) {
let median = getKnum(nums1, nums2, Math.floor(total / 2) + 1);
return median;
} else {
// let res1 = getKnum(nums1, nums2, total / 2);
// let res2 = getKnum(nums1, nums2, total / 2 + 1);
// console.log(res1, res2);
let median = (getKnum(nums1, nums2, total / 2) + getKnum(nums1, nums2, total / 2 + 1)) / 2;
return median;
}
};
function getKnum(nums1, nums2, k) {
//注意这边操作数组不能修改元素组,因此先进行深拷贝
let s1 = JSON.stringify(nums1);
let s2 = JSON.stringify(nums2);
let n1 = JSON.parse(s1);
let n2 = JSON.parse(s2);
while (true) {
console.log(k);
if (n1.length == 0) {
console.log(k, n2[k - 1]);
return n2[k - 1];
}
if (n2.length == 0) {
console.log(k, n1[k - 1]);
return n1[k - 1];
}
if (k == 1) {
return Math.min(n1[0], n2[0]);
}
let index = Math.floor(k / 2) - 1;
if (n1.length < index) {
k = k - n1.length;
for (let i = 0; i < n1.length; i++) {
n1.shift()
}
continue;
} else if (n2.length < index) {
k = k - n2.length;
for (let i = 0; i < n2.length; i++) {
n2.shift();
}
continue;
}
if (n1[index] <= n2[index]) {
for (let i = 0; i <= index; i++) {
n1.shift();
}
} else {
for (let i = 0; i <= index; i++) {
n2.shift();
}
}
k = k - Math.floor(k / 2);
// console.log(n1, n2);
}
}
let res = findMedianSortedArrays([2, 3, 4, 5, 6], [1]);
console.log(res);
十一、912.排序数组
1.快速排序
var sortArray = function(nums) {
quicksort(nums, 0, nums.length - 1);
return nums;
};
//快速排序
function quicksort(nums, l, r) {
if(l >= r) return;
let pivot = nums[l];
swap(nums, l, r);
let i = l;
for(let j = l; j < r; j++) {
if(nums[j] < pivot) {
swap(nums, i, j);
i++;
}
}
swap(nums, i, r); //换回轴值
quicksort(nums, l, i - 1);
quicksort(nums, i + 1, r);
}
function swap(nums, i, j) {
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
2.堆排序
var sortArray = function(nums) {
let res = heapSort(nums);
return res;
};
//堆排序(最小堆)
function heapSort(nums) {
if(nums.length <= 1) return nums;
let heapSize = nums.length;
//建堆
buildHeap(nums, heapSize);
//排序
let res = [];
for(let i = 0; i < nums.length; i++) {
res.push(nums[0]); //获取最小元素
swap(nums, 0, --heapSize);
siftdown(nums, 0, heapSize);
}
return res;
}
function buildHeap(nums, heapSize) {
for(let i = Math.floor(heapSize/2) - 1; i >= 0; i--) {
siftdown(nums, i, heapSize);
}
}
function siftdown(nums, i, heapSize) {
let smallest = i;
let left = 2*i + 1;
let right = 2*i + 2;
if(left < heapSize && nums[left] < nums[smallest]) {
smallest = left;
}
if(right < heapSize && nums[right] < nums[smallest]) {
smallest = right;
}
if(smallest != i) {
swap(nums, i, smallest);
siftdown(nums, smallest, heapSize);
}
}
function swap(nums, i, j) {
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
3.归并排序
var sortArray = function(nums) {
let res = mergeSort(nums);
return res;
};
//归并排序
function mergeSort(nums) {
if(nums.length <= 1) return nums;
let middle = Math.floor(nums.length/2);
let arr1 = nums.slice(0, middle);
let arr2 = nums.slice(middle);
return merge(mergeSort(arr1), mergeSort(arr2));
}
function merge(arr1, arr2) {
let res = [];
while(arr1.length > 0 && arr2.length > 0) {
if(arr1[0] <= arr2[0]) {
res.push(arr1.shift());
} else {
res.push(arr2.shift());
}
}
return res.concat(arr1).concat(arr2);
}
十二、70.爬楼梯
超时的:
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
let res = 0;
function cnt(n) {
if(n < 0) return;
if(n == 0) {
res++;
return;
}
//迭代(递归)
// +1
cnt(n - 1);
// +2
cnt(n - 2);
}
cnt(n);
return res;
};
动态规划
var climbStairs = function(n) {
//f(n) = f(n - 1) + f(n - 2)
if(n == 1) return 1;
if(n == 2) return 2;
let n1 = 1;
let n2 = 2;
let sum = 0;
for(let i = 3; i <= n; i++) {
sum = n1 + n2;
n1 = n2;
n2 = sum;
}
return sum;
};
十三、61.旋转链表
/**
* 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} k
* @return {ListNode}
*/
var rotateRight = function(head, k) {
if(!head || !head.next || k === 0) return head;
//首先算出链表的长度n 并且闭合为环
let n = 1;
let curr = head;
while(curr.next) {
curr = curr.next;
n++;
}
//开始计数断开环 并返回结果
let add = n - k % n;
if(add === n) return head; //特殊情况
curr.next = head; //注意要在后面成环 不然上面特殊情况返回的是一个环
while(add) {
curr = curr.next;
add--;
}
const res = curr.next;
curr.next = null;
return res;
};
十四、剑指offer 22.链表中倒数第k个节点
这个用闭合为环会超时
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var getKthFromEnd = function(head, k) {
//双指针做法
let left = head;
let right = head;
//右指针先走k步
for(let i = 0; i < k; i++) {
right = right.next;
}
//左右指针同时走 直到right === null
while(right) {
left = left.next;
right = right.next;
}
return left;
};
十五、46.全排列
回溯法:
/**
* @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());
return;
}
for(let k of nums) {
if(used[k]) continue; //已经使用过
path.push(k);
used[k] = true;
dfs(path);
path.pop(); //回到上一层
used[k] = false;
}
}
dfs([]);
return res;
};
十六、剑指offer 38.字符串的排列
/**
* @param {string} s
* @return {string[]}
*/
var permutation = function(s) {
let res = [];
let used = {};
function dfs(str) { //回溯
if(str.length === s.length) {
res.push(str);
return;
}
for(let i = 0; i < s.length; i++) {
if(used[i]) { //已经使用过
continue;
}
used[i] = true;
dfs(str + s[i]);
used[i] = false;
}
}
dfs("");
res = Array.from(new Set(res)); //去重
return res;
};