面试题中常常会有手写算法题, 现在总结了几个, 并会持续更新中, 有疑问多多提出
1. 数组全排列
- 数组[1,2,3,4] 得到数组的全排列[1,2,4,3] [2,1,4,3] ...
- 思路: 回溯 + 递归
function totalArr(arr) {
const result = []; // 返回的最终结果
const obj = {}; // 用来判断是否有使用过某个数
// 深度优先遍历
function dfs(arrItem) {
// 两个数组长度相同, 结果推进arr中
if (arrItem.length === arr.length) {
result.push([...arrItem]);
return;
}
for (const num of arr) {
// for of循环
// 遍历数组
if (obj[num]) continue; // 跳过本次循环
arrItem.push(num);
obj[num] = true;
dfs(arrItem); // 递归找
arrItem.pop(); // 回溯
obj[num] = false;
}
}
dfs([]);
return result;
}
const arr = totalArr([1, 2, 3]);
2.最长不重复字串
- 思路: 滑动窗口 如果出现重复字符 清空数组 重新计算数组长度
function maxCountNotReapeat(s) {
let max = 0;
let arr = [];
for (let i = 0; i < s.length; i++) {
// arr.push(s);
let index = arr.indexOf(s[i]); // 每个元素的索引
if (index !== -1) {
arr.splice(0, index+1); // 截取从0到index+1的所有字符
}
arr.push(s[i]); // 重新往arr里面添加值
max = Math.max(arr.length, max); // 更新max的值
}
return max;
}
// console.log(maxCountNotReapeat("abbbcdsfgs"));
3. 数组中第K大的数
- sort后拿对应的索引
function kNum(arr, k) {
return arr.sort((a, b) => b - a)[k - 1];
}
// 如果需要不重复, 可以使用new Set() 去重
console.log(kNum([1, 2, 3, 4, 5, 4, 3, 2, 3], 3));
4. 回文字符串
题目: 输入: 'a man, nam,a'
输出: true
- 双指针
- l指向头部, r指向尾部
- 两头的指针向中间靠拢, 以相遇为结束条件, 若指向的字符不同, 则不是回文字符串
- 另本题忽略大小写及其他字符, 遇到这些字符, 需要跳出本次循环
function isPalindrome(s) {
let l = 0;
let r = s.length - 1;
let flag = true;
while (l < r) {
// 验证字符是否是字母或数字, 如果不是跳过本次循环
if (!/[^A-Za-z0-9]/.test(s[l])) {
l++;
continue; // 跳出本次迭代
}
if (!/[^A-Za-z0-9]/.test(s[r])) {
r--;
continue;
}
if (l < r && s[l].toLowerCase() !== s[r].toLowerCase()) {
flag = false;
break; // 跳出循环
}
l++;
r--;
}
return flag;
}
isPalindrome("232, a232");
isPalindrome("232, a232");
isPalindrome("abcba");
isPalindrome("ab");
5.js大数相加
JavaScript能精确表示的数字是有限的,JavaScript可以精确到个位的最大整数是9007199254740992,也就是2的53次方,超过这个范围就会精度丢失,造成JavaScript无法判断大小
- 解法: 转成字符串相加
- 例:1255 + 456
初始化 进位 t = 0
0: x + y + t /10 商(t) 余(p)
1: 5 + 6 + 0 = 11 /10 1 1
2: 5 + 5 + 1 = 11 /10 1 1
3: 2 + 4 + 1 = 7 /10 0 7
4: 1 + 0 + 0 = 1 /10 0 1
结果:1711
// 48 是字符 0 的ASCII码
/**
* @param {string} num1
* @param {string} num2
* @return {string}
*/
var addStrings = function (num1, num2) {
let res = ''
let i = num1.length - 1, j = num2.length - 1, flag = 0
while (i >= 0 || j >= 0 || flag !== 0) {
if (i >= 0) flag += num1.charCodeAt(i--) - 48
if (j >= 0) flag += num2.charCodeAt(j--) - 48
res = '' + flag % 10 + res
flag /= 10
// 向下取整
flag = ~~flag
}
return res
};
6. 链表中倒数第k个节点
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
// 1. 快慢指针
// 快指针先走k,慢指针再走,快指针到头,慢指针正好倒数k,此时返回慢指针即为所求。
var getKthFromEnd = function(head, k) {
let fast = head;
let slow = head;
let flag = 0;
while (fast) {
if (flag >= k) {
slow = slow.next;
}
fast = fast.next;
flag ++;
}
return slow;
};
// 2. 数组
// 由于链表的长度无法直接获取,因此必须遍历链表。
// 利用数组中找到倒数第k个元素十分方便的特性,可以把链表转化为数组后直接输出目标链表节点。
var getKthFromEnd = function(head, k) {
let p = head
const map = []
while(p.next!==null){
map.push(p)
p = p.next
}
map.push(p)
return map[map.length-k]
};
数组转成树形结构
var source = [
{
id: 1,
pid: 0,
name: 'body',
},
{
id: 5555,
pid: 0,
name: 'script',
},
{
id: 55551,
pid: 5555,
name: 'js',
},
{
id: 2,
pid: 1,
name: 'title',
},
{
id: 3,
pid: 1,
name: 'div',
},
{
id: 4,
pid: 3,
name: 'span',
},
{
id: 5,
pid: 3,
name: 'icon',
},
{
id: 6,
pid: 4,
name: 'subspan',
},
];
function toTree(data) {
let result = [];
if (!Array.isArray(data)) {
return result;
}
let map = {};
data.forEach(item =>{
map[item.id] = item
})
data.forEach(item =>{
let parent = map[item.pid]
if(parent){
parent.children = parent.children || []
parent.children.push(item)
}else{
result.push(item)
}
})
return result
}
let res = toTree(source)
// console.log(res, 'res');