最近很多公司陆陆续续开始了提前批校招,虽然大部分还没开始,所以可以开始积累总结了,现在总结的都是针对剑指offer 里的编程题,很典型也很经典,不定期更新吧
一、数组
数组如果在 js 中用的好的话,笔试中用到的会占大部分。
1)数组调用函数
“包含 Min 函数的栈”:求一维数组中的最小值/ 最大值:Math.min.apply(this/null,目标数组) Math.max.apply(this/null,目标数组)
var arr = [1,3,6,8,2,10]; var minNum = Math.min.apply(null,arr);
var maxNum = Math.max.apply(null,arr);
console.log(minNum,maxNum);
运行结果是:1 , 10 ; Math.min 可以实现得到数组中最小的一项,Math.max 可以实现得到数组中最小的一项
2) 数组常用方法:reduce、map、filter 、concat、foreach、some、unshift(前插)、shift、isArray、join、splice、slice
"变态跳台阶":跳上 n 级台阶可以用 1到n 种单次跳法,求跳上 n 级台阶共有多少种跳法。找规律可以发现,最后一个数的值,是之前值的和。或者认为拆分后,是之前子项的考虑的全部情况。所以这里用 reduce 函数是最方便的。
// write code here
var result = [1,1];
for(var i=2;i<=number;i++){
var temp = result.reduce(function(acc,val){
return acc+val;
})
result[i] = temp;
}
return result[number];
“调整数组里的奇数位于偶数之前”:遍历一次原数组,对于奇数放入数组1,偶数放入数组2。再用concat 连接两数组,返回最后的数组。
3)数组规律的判断:
“顺时针打印矩阵”:给出一个 n*n 的矩阵,顺时针输出其中的数。主要是找到下标变化的规律:
function printMatrix(matrix)
{
// write code here
var row=matrix.length;
var col=matrix[0].length;
var res=[];
if(row==0||col==0){
return res;
}
var left=0,
top=0,
right=col-1,
bottom=row-1;
while(left<=right&&top<=bottom){
for(var i=left;i<=right;i++)
res.push(matrix[top][i]);
for(var i=top+1;i<=bottom;i++)
res.push(matrix[i][right]);
if(top!=bottom)
for(var i=right-1;i>=left;i--)
res.push(matrix[bottom][i]);
if(left!=right)
for(var i=bottom-1;i>top;i--)
res.push(matrix[i][left]);
left++,top++,right--,bottom--;
}
return res;
}
二、链表
链表通常与栈、队列、二叉树这些基本数据结构概念联系在一起。
1)栈实现队列:
“用两个栈实现队列”:初始化两个数组,第一个存储完所有的数据,再判断第二个数组是否为空,若第二个数组为空且第一个数组不为空,则遍历第一个数组中的元素全部存入第二个数组;若第二个数组不空,则弹出。
2)“重建二叉树":
给出一个二叉树的前序遍历和中序遍历,输出完整的二叉树结构。前序遍历的第一个元素是根节点,中序遍历的根节点左边是左子树内容,右边是右子树内容。根据划定的子树范围,用中值下标不断进行细分。
function reConstructBinaryTree(pre, vin)
{
var len = pre.length;
return createTree(pre, vin, 0, len - 1, 0);
}
function createTree(pre, vin, left, right, rootIndex){
var val = pre[rootIndex];
var midIndex = vin.indexOf(val);
var root = new TreeNode(val);
if(midIndex > left) root.left = createTree(pre, vin, left, midIndex - 1, rootIndex + 1 );
if(midIndex < right) root.right = createTree(pre, vin, midIndex + 1, right, rootIndex + midIndex -left + 1 );
return root;
}
3) “从头到尾打印链表”
链表部分只给出了头部,那就新建一个数组,将链表的内容依次存入数组,最后输出链表的翻转,用 reverse() 实现。
4)“输出链表倒数第K个节点”
将链表中的数依次存入数组,输出数组的第 length-k 个数
5)“反转链表”
输入链表,反转链表后,输出新的表头。链表的值依次存入数组,再从数组中弹出存入链表。利用数组的栈性质。
三、递归
很多情况下,笔试题也会涉及到递归调用
1)涉及到1到n 的数字总结类型
比如剑指offer 中的 “跳台阶问题”、“斐波那契数列”、“矩形覆盖”
“矩形覆盖”:总结规律发现第三项开始,每项都是前两项的和。与斐波那契数列的操作一样
2)链表
“合并排序的链表”:输入两个单调递增的链表,输出一个合并后的单调递增链表
function Merge(pHead1, pHead2){
// write code here
let list = {}
if(pHead1 === null){
return pHead2;
} else if (pHead2 === null) {
return pHead1;
}
if(pHead1 > pHead2){
list = pHead2;
list.next = Merge(pHead2.next, pHead1);
} else {
list = pHead1;
list.next = Merge(pHead2, pHead1.next)
}
return list;
}
这里就是递归的链表表现形式,同时也可以看出,链表的表现形式就是对象的集合。
“树的子结构”:判断B是不是A 的子结构
function HasSubtree(pRoot1, pRoot2){
// write code here
if (!pRoot1 || !pRoot2) {
return false;
}
return isSubtree(pRoot1, pRoot2) || HasSubtree(pRoot1.left, pRoot2) ||
HasSubtree(pRoot1.right, pRoot2);
}
function isSubtree(root1, root2) {
if (!root2) {
return true;
}
if (!root1) {
return false;
}
if (root1.val == root2.val) {
return isSubtree(root1.left, root2.left) &&
isSubtree(root1.right, root2.right);
} else {
return false;
}
}
“二叉树的镜像”:将一个二叉树转化为镜像输出。一开始想的是把节点的左右子节点是否为空作为初始的判断,但是一直报错,所以用 root 判断即可
function(root){
if(root === null) {
return;
}
var temp = root.left;
root.left = root.right;
root.right = temp;
Mirror(root.left);
Mirror(root.right);
}
四、自定义函数
这边的应用也有很多,都是为了在算法内部的自定义调用
1)排序函数:有时自带的排序函数 sort 并不能达到想要的效果,就可以在排序函数内部自定义是递增或递减函数。比如“旋转最小数字”
五、基础知识
1)正则表达式:
正则表达式,用的好就能很方便完成一些操作。那就顺便复习一下正则表达式:
通用形式:/\s 或 \S 或 \d 或 \D 等...../g
匹配数量:* :任意数量,+:大于等于1 ,{n} : n 个,{n , } : 至少匹配n 次,{n , m }:匹配n 到 m 次
匹配对象:x|y : 匹配 x 或y,[xyz] : 匹配其中的任意一个字符(“adxasd”,匹配的就是其中的 x),[^xyz] : 匹配非其中的任意一个字符(“adxasd”,匹配的就是其中的 adasd),[a-z]:字母范围,[^a-z]:匹配非字母范围中的字符,\d: 数字,\D: 非数字,\s :匹配空格制表符换页符等,\S 前者取非。
“替换空格”:替换字符串中的空格为其他任意字符串:str.replace( /\s/g , "任意字符串" );
2)二进制操作
二进制之前没有非常注意,这里单独有一道题就是考察二进制的。
“二进制中1的个数”:给出一个数,将数转换为二进制后,判断其中1的个数。这里考察的是基本运算符的本质问题,基本运算符中分逻辑运算符:或(||) 与(&&)非(!),和位运算符或(|) 与(&)非(~)异或(^)左移(<<)右移(>>)零填充右移(>>>)
function(n){
var count = 0,flag=1;
while(flag){
if(n&flag)count++;
flag=flag<<1;
}
return count;
}
这里的小知识点就是,flag 每次右移1位,就相当于每次拿到的数是2的次幂值:2、4、8...,当输入的数与flag 做位与运算时,若在该次幂的位置上有1,则为true,否则为false。以此来统计目标数值中含有多少个1
3)“数值的整数次方”
这里就是对所有情况的数值的整数次做出对应的判断,求 base 的 exponent 次方。这里考虑 exponent 为 正、负、0是那种基本情况,其中在exponent 为负时,base 取到数次幂,同时判断不能为负值;在exponent 为正时,考虑base 的奇偶性。