欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

剑指offer js算法练习(1-10)

程序员文章站 2022-07-01 09:04:46
1.二维数组中的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 时间限制:1秒 空间限制:32768K 分析:由于每一行都按照从左到右递增的顺序排序 ......

1.二维数组中的查找
       在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

       时间限制:1秒 空间限制:32768k

分析:由于每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,所以右上角的数字就是该行的最大值,该列的最小值。则可以通过把目标数与右上角的数字比较来判断其位置,如果目标数比该数字小,由该数字为该列的最小值可得这一列都不会有目标数,我们就可以将该列删除,并得到新的二维数组,再次比较目标数与右上角的数字的大小关系。如果目标数比该数字大,由该数字为该行的最大值可得这一行都不会有目标数,我们就可以将该行删除,并得到新的二维数组,再次比较目标数与右上角的数字的大小关系。如果目标数与右上角的数字相等,则返回true,找到了该目标数。如果当行数和列数都被删完了还没找到,则该二维数组中就没有该目标数。

function find(target, array)
{
    if(array==undefined||array.length<0||array[0].length<0){
        return false;
    }
    let rows=array.length;
    let columns=array[0].length;
    let row=0;
    let column=columns-1;
    while(row<rows&&column>=0){
        if(array[row][column]==target){
            return true;
        }
        if(array[row][column]>target){
            column--;
        }else{
            row++;
        }
    }
    return false;
}

2.替换空格
        请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为we are happy.则经过替换之后的字符串为we%20are%20happy。

       时间限制:1秒 空间限制:32768k

分析:这里用replace+正则表达式即可替换。

function replacespace(str)
{
    return str.replace(/ /g,'%20')
}

3.从头到尾打印链表
       输入一个链表,按链表值从尾到头的顺序返回一个arraylist。链表节点如下:

function listnode(x){
    this.val = x;
    this.next = null;
}

       时间限制:1秒 空间限制:32768k 

分析:首先我们应该判断链表是否存在,如果链表存在的话,就用一个循环来判断节点是否存在,依次将节点中的值放入栈(题目要求从尾到头的顺序)即可。

function printlistfromtailtohead(head)
{
    if(head==undefined)
    {
        return 0;
    }else
    {
        var arr=new array();
        var curr=head;
        while(curr)
        {
            arr.push(curr.val);
            curr=curr.next;
        }
        return arr.reverse();
    }
}


4.重建二叉树
       输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。树节点如下:

function treenode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
} 


       时间限制:1秒 空间限制:32768k 

分析:首先我们要知道前序遍历的顺序是根左右,中序遍历的顺序是左根右。前遍历的第一个数总是根,所以我们就可以在前序遍历中先找到根,再在中序遍历中分别找到左子树和右子树,然后再分别在左子树和右子树中按照相同的方法去找它们的根节点,左右子树,直到在某个子树中的前序和后序遍历的长度都为0。由此我们可以写出如下的递归代码:

function reconstructbinarytree(pre, vin)
{
    if(pre.length==0&&vin.length==0)
    {
        return null;
    }
    var index=vin.indexof(pre[0]);
    var left=vin.slice(0,index);
    var right=vin.slice(index+1);
    var root=new treenode(pre[0]);
    root.left=reconstructbinarytree(pre.slice(1,index+1),left);
    root.right=reconstructbinarytree(pre.slice(index+1),right);
    return root;
}


5.用两个栈实现队列
       用两个栈来实现一个队列,完成队列的push和pop操作。 队列中的元素为int类型。

       时间限制:1秒 空间限制:32768k 

分析:首先我们要明白栈的特点是后进先出,队列的特点是先进先出。这里我们可以用栈stack1压入队列,不做特殊处理,就用栈的方法压入。再在另一个用于pop的方法中,把stack1中数依次弹出并压入栈stack2,这样就满足了最先进入栈stack1在栈stack2的栈顶,由于pop操作每次只弹出一个值,所以需要弹出stack2的栈顶值,用一个变量存起来,然后再把stack2中数依次弹出并压入栈stack1,以便下次正常压入弹出,最后返回变量即可。

var stack1=new array();
var stack2=new array();
 
function push(node)
{
    stack1.push(node);
}
function pop()
{
    var temp=stack1.pop();
    while(temp){
        stack2.push(temp);
        temp=stack1.pop();
    }
    var result=stack2.pop();
    temp=stack2.pop();
    while(temp){
        stack1.push(temp);
        temp=stack2.pop();
    }
    return result;
}

6.旋转数组的最小数字
        把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 note:给出的所有元素都大于0,若数组大小为0,请返回0。

        时间限制:3秒 空间限制:32768k

分析:这道题可以用二分法来做,但是会出现超时,所以用js的math.min.apply(null,arr)方法来求是最高效的。

function minnumberinrotatearray(rotatearray)
{
    if(rotatearray.length==0){
        return 0;
    }
    return math.min.apply(null,rotatearray);
}


7.斐波那契数列
        大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39

        时间限制:1秒 空间限制:32768k

分析:斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........这个数列从第3项开始,每一项都等于前两项之和。解决斐波那契数列可以使用递归的解法,但是这个却不是最优解,因为递归会产生很多重复的计算。为避免重复计算,我们可以根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3)......依次类推就可以算出第n项了。

function fibonacci(n){
    if(n<0||n>39){
        return;
    }
    if(n==0){
        return 0;
    }
    if(n==1){
        return 1;
    }
    var fibnminusone=0;
    var fibnminustwo=1;
    var fibn=0;
    for(var i=2;i<=n;i++){
        fibn=fibnminusone+fibnminustwo;
        fibnminusone=fibnminustwo;
        fibnminustwo=fibn;
    }
    return fibn;
}


8.跳台阶
        一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

        时间限制:1秒 空间限制:32768k

分析:首先我们考虑最简单的情况。如果只有1级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳法:一种是分两次跳,每次跳1级;另一种就是一次跳2级。接着我们再来讨论一般情况。我们把n级台阶时的跳法看成n的函数,记为f(n)。当n>2时,第一次跳的时候就有两种不同的选择:一次第一次只跳1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);二是第一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。因此,n级台阶的不同跳法的总数f(n)=f(n-1)+f(n-2)。这实际上就是斐波那契数列。

function jumpfloor(number){
    if(number <= 0){
        return 0;
    }
    if(number == 1){
        return 1;
    }
    if(number == 2){
         return 2;
    }
    var jumpnminusone=1;
    var jumpnminustwo=2;
    var jumpn=0;
    for(var i=3;i<=number;i++){
        jumpn=jumpnminusone+jumpnminustwo;
        jumpnminusone=jumpnminustwo;
        jumpnminustwo=jumpn;
    }
    return jumpn;
}


9.变态跳台阶
        一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

        时间限制:1秒 空间限制:32768k​​​​​​​

分析:首先我们考虑最简单的情况。如果只有1级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳法:一种是分两次跳,每次跳1级;另一种就是一次跳2级。如果只有3级台阶,那就有4种跳法:①分三次跳,每次跳1级;②分二次跳,第一次跳2级第二次跳1级;③分二次跳,第一次跳1级第二次跳2级;④分一次跳,直接跳3级。接着我们再来讨论一般情况。我们把n级台阶时的跳法看成n的函数,记为f(n)。当n>1时,第一次跳的时候就有n种不同的选择:第一次只跳1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);第一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2);同理第一次跳n-1级,此时跳法数目等于后面剩下的1级台阶的跳法数目,即为f(1);第一次跳n级,此时跳法数目等于后面剩下的0级台阶的跳法数目,即为f(0)也就是0次。因此,n级台阶的不同跳法的总数f(n)=f(n-1)+f(n-2)+...+f(0)。又因f(n-1)=f(n-2)+f(n-3)+...+f(0)。所以f(n)=2*f(n-1)。

function jumpfloorii(number)
{
    if(number <= 0){
        return 0;
    }
    if(number == 1){
        return 1;
    }
    var jumpnminus=1;
    var jumpn=0;
    for(var i=2;i<=number;i++){
        jumpn=jumpnminus*2;
        jumpnminus=jumpn;
    }
    return jumpn;
}

10.矩形覆盖
        我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?​​​​​​​

        时间限制:1秒 空间限制:32768k​​​​​​​

分析:

             剑指offer js算法练习(1-10)

             剑指offer js算法练习(1-10)

 

        我们讨论一般情况。我们把放的方法的看成n的函数,记为f(n)。当第一个小矩形这样横着放时,剩下的放法即为f(n-2)。

            剑指offer js算法练习(1-10)

 

        当第一个小矩形这样竖着放时,剩下的放法即为f(n-1)。由此我们可以得到f(n)=f(n-1)+f(n-2),所以这个问题其实还是斐波那契数列。

function rectcover(number)
{
    if(number==1){
        return 1;
    }
    if(number==2){
        return 2;
    }
    var rectnminusone=1;
    var rectnminustwo=2;
    var rectn=0;
    for(var i=3;i<=number;i++){
        rectn=rectnminusone+rectnminustwo;
        rectnminusone=rectnminustwo;
        rectnminustwo=rectn;
    }
    return rectn;
}