目录
前端面试算法题准备:
1、二维数组中的查找
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
分析:因为数组是一个比较特殊的数组,首先我们分析他的数组结构
- 每一行都按照从左到右递增的顺序排序
- 每一列都按照从上到下递增的顺序
可以知道每一列的最后一个元素在该列中是最大,在该行中确实最小,所以我们可以从数组矩阵的左下角进行查找,这个时候会有两种情况,
- 第一种情况是左下角的数字大于我们需要找的目标数,这个时候因为该数字是这一列最大的数,所以我们就需要在这一列中继续查找,将左下角的位置向上移一个位置
- 第二种情况是左下角的数字小于我们需要找的数,所以我们就不需要在这一列中继续查找,则将左下角的位置向右移一位重复过程找到最大的数字
js代码:
function Find(target, array)
{
var row = 0;//用来记录当前所在列
var rows = array[0].length;//列数
var cols = array.length;//行数
var col = cols-1;//用来记录当前所在行,初始坐标在数组矩阵的左下角
if(cols == 0){
return false;
}
while(col>= 0 && row < rows){//当遍历到右上角时,遍历结束
if(array[col][row] == target){
return true;
}else if(array[col][row] < target){//当前元素于目标数时,坐标向右移动查找,所以列数+1
row++;
}else{//否则行数减一
col--;
}
}
return false;
}
2、替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
分析:直接使用js的替换函数 str.replace(); 用法:二个参数,第一为正则表达式,第二个需要替换成为的字符(后面专门学一会正则表达式)
js代码:
function replaceSpace(str)
{
return str.replace(/\s/g,'%20');
}
3、从头到尾打印链表
题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
分析:链表的初体验,注意这题要求我们返回的是一个数组,将链表遍历,每次将当前值输出到数组的前面(js的unshift函数)
js代码:
/*function ListNode(x){
this.val = x;
this.next = null;
}*/
function printListFromTailToHead(head)
{
var Arraylist = [];
while(head){//常规的遍历
Arraylist.unshift(head.val);//这里注意需要将链表的值输入进去,不要直接把节点输入进去
head = head.next;
}
return Arraylist;
}
4、重建二叉树(重点)
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
分析:这题算是遇到的第一个比较难度的题,也是一个很经典的题。
先复习一下js的一些字符串常用的函数,以后会大量用到
- splice(start,num) 开始位置,删除个数
- slice (start,end)开始位置,结束位置(不包括end的位置元素)
- indexOf(str) 从头部开始找str
- lastIndexOf(str) 从尾部开始找 str
- shift() 删除数组的第一个元素,常模拟队列
- pop() 删除数组的最后一个元素,模拟栈
然后具体的方法其实就是模拟我们手动建树的过程,首先我们找到前序遍历的第一个节点(根节点),然后再中序遍历中查找这个根节点,会发现该序列被分成了两部分,即根节点的左子树和右子树,然后将前序遍历的第一个节点删除,继续对中序遍历的左右子树进行同样的做法,需要注意的是,中序遍历序列被分成两半之后,需要根据这两半的长度将前序遍历也分成两部分。反复进行完成递归;
js代码:
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function reConstructBinaryTree(pre, vin)
{
var result = null;//result用来储存树
if(pre.length > 1){//当前序序列长度大于2时说明还可以继续分割
var root = pre[0];//获得前序遍历序列的第一个元素,即为跟节点
var index = vin.indexOf(root);//在中序遍历序列中查找根节点所在的位置。
var vinleft = vin.slice(0,index);
var vinright = vin.slice(index+1,vin.length);//根据根节点位置将中序遍历序列分成两个部分即一个左子树和一个右子树的中序序列
pre.shift();//将根节点去除
var preleft = pre.slice(0,vinleft.length);
var preright = pre.slice(vinleft.length,pre.length);//根据左右子树中序序列的长度,得到该两个子树的前序遍历的数组
result ={
val : root,
left : reConstructBinaryTree(preleft,vinleft),
right: reConstructBinaryTree(preright,vinright)//对左右子树进行递归建树
}
}else if(pre.length == 1){//前序序列长度为一时,即为叶节点
result = {
val: pre[0],
left: null,
right: null
}
}
return result;
}
5、用两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
分析:队列先进先出,一个栈负责进,一个栈负责出,然后pop出的栈的元素,即可;
js代码:
let instock = [];
let outstock = [];
function push(node)
{
instock.push(node);
}
function pop()
{
if(!outstock.length){
while(instock.length){
outstock.push(instock.pop());
}
}
return outstock.pop();
}
6、旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
分析:简单题,因为是非递减数组,当找到第一个跳跃点即后面的数小于前面的一个数时,那这个后面的数就是最小的数;
js代码:
function minNumberInRotateArray(rotateArray)
{
if(rotateArray.length==0){
return 0;
}
var min = rotateArray[0];//得设置成第一个数,防止没有旋转
for(var i=0;i<rotateArray.length-1;i++){
if(rotateArray[i+1] < rotateArray[i]){
min = rotateArray[i+1];
}
}
return min;
}
7、斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
科普:斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
分析:经典的递归,后面的跳台阶问题会应用到这个递推公式;
js代码:
function Fibonacci(n)//用了非递归方法,递归方法可能容易爆栈
{
if(n<=1){
return n;
}
else{
var f0 = 0;
var f1 = 1;
for(var i = 2; i<=n; i++){//从第二项开始,模拟递推过程
f = f0 + f1;
f0 = f1;
f1 = f;
}
return f;
}
}
8、跳台阶问题
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
分析:递归处理,可以这么看,这里有n个台阶,我们在跳最后一步的时候,有两种可能
- 第一种可能,我们最后一步跳一步,那这时候就有F(n-1) 种方法,因为之前我们已经跳了n-1个台阶;
- 第二种可能,我们最后一步跳两步,那么这时候我们就有F(n-2)种方法,因为之前我们已经跳了n-2个台阶;
所以当有n个台阶的时候,我们就有f(n-1)+f(n-2) 种方法;
得递推公式
f(n) = f(n-1) +f(n-2)
- 当n=1的时候 有1种方法
- 当n=2的时候 有2种方法
js代码:
function jumpFloor(number)
{
if(number == 1){
return 1;
}else if(number == 2){
return 2;
}
return jumpFloor(number-1)+jumpFloor(number-2);//进行递归
}
9、变态跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
分析:这题咋一看我懵了,看了一下题解,可以用递推公式,然后错位相减得f(n) = 2的n-1次方,然后不解释,直接Math.pow(2,n-1);
推算过程: f(n)=f(n-1)+f(n-2)+……f(1)
f(n-1)=f(n-2)+……f(1)
两式相减得 f(n)=2f(n-1)
js代码:
function jumpFloorII(number)
{
return Math.pow(2,number-1);
}
10、矩形覆盖:
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
比如n=3时,2*3的矩形块有3种覆盖方法:
分析:简单的递推,最后一块可以横着放也可以竖着放
- 竖着放的时候:则有f(n-1)种方法
- 横着放的时候:则有f(n-2)种方法
和跳台阶一样;
js代码:
function rectCover(number)
{
if(number == 0){
return 0;/*有坑,必须考虑等于0的情况,否则就数组越界*/
}
if(number == 1){
return 1;
}
if(number == 2){
return 2;
}
return rectCover(number-1)+rectCover(number-2);
}
11、进制转换题
不想看跳了
12、数值的整数平方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
分析:直接库函数,奥利给!!!
js代码:
function Power(base, exponent)
{
// write code here
return Math.pow(base,exponent);
}
13、调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
分析:这题其实有个坑,就是数组数之间的相对位置不变,这时候想到的数冒泡排序,因为冒泡排序是一个稳定的排序,算法复杂度为O(n²),可以用一个flag进行简单的优化;
知识点:冒泡排序用的是大小都为length的for循环,将每个元素和相邻的元素进行比较,符合条件交换,每次都会有有一个元素会到他最终的位置,是一个稳定的排序。
js代码:
function reOrderArray(array)
{
for(var i=0; i<array.length; i++){
var flag = 1;
for(var j=0; j<array.length-1; j++){
if(array[j]%2==0 && array[j+1]%2 ==1){//若前面是偶数,后面是奇数则将前面的偶数和后面的奇数进行交换
var temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = 0;
}
}
if(flag == 1){//用来优化,说明本次遍历没有进行交换可以不用再冒泡了
break;
}
}
return array;
}
14、链表中的倒数第k个节点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
分析:链表倒置打入数组中,注意这次是输出节点,所以需要把每个节点打入数组,不是数值,然后输入数组第k个元素;
js代码:
/*function ListNode(x){
this.val = x;
this.next = null;
}*/
function FindKthToTail(head, k)
{
var arraylist =[];
while(head){
arraylist.unshift(head);//注意这里节点不是数值
head = head.next;
}
return arraylist[k-1];
}
今天写到这,明天继续更新后面的题