11天刷剑指offer——JavaScript版——第十一天

本文精选了二叉树序列化、二叉搜索树的第k个结点、数据流中的中位数等经典算法题目,提供了详细的解题思路与代码实现,帮助读者深入理解并掌握算法设计与分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

61、序列化二叉树

题目描述

请实现两个函数,分别用来序列化和反序列化二叉树

思路
  • 序列化,将节点值存入数组中,空节点则使用特殊标记存入数组中。
  • 反序列化,从数组中获取元素,为number类型则生成节点,为特殊标记,则为空节点
代码

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。
通过文件内容重建原来的二叉树过程叫做二叉树反序列化。
例子采用先序遍历

var arr=[];
function Serialize(pRoot)
{
    // write code here
    if(pRoot==null){
        arr.push('#') //特殊标记
        return;
    }
    arr.push(pRoot.val);
    Serialize(pRoot.left) //先左节点
    Serialize(pRoot.right)  //后右节点
}
function Deserialize(s)
{
    // write code here
    if(arr==null){
        return null;
    }

    if(arr.length<1){
        return null;
    }
    var root=null;
    //shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
    var temp=arr.shift();
    if(typeof temp=='number'){
        root=new TreeNode(temp);
        root.left=Deserialize(arr);
        root.right=Deserialize(arr);
    }
    return root;
}

62、二叉搜索树的第k个结点

题目描述

给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

思路

二叉搜索树,若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;任意节点的左、右子树也分别为二叉查找树;
所以采用中序遍历的方法,遍历后的结果就是从小到大顺序的结果

代码
function KthNode(pRoot, k)
{
    // write code here

    var arr=[];
    if(pRoot===null||k<1){
        return null;
    }
    function midInorder(root){//中序排序
        if(root.left!==null){
             midInorder(root.left);
        }
        arr.push(root);
        if(root.right!==null){
            midInorder(root.right);
        }
    }
    midInorder(pRoot);
    return arr[k-1];
}

63、数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

思路一
代码
var arr=[]
function Insert(num)
{
    // write code here
    arr.push(num);
    arr.sort()//数值排序
}
function GetMedian(){
    // write code here
    var len=arr.length;
    if(len%2!=0){
        return arr[Math.floor(len/2)];
    }else{
        return (arr[len/2-1]+arr[len/2])/2
    }
}
思路二

大小顶堆,数据流传输时,第偶个数,存入大顶堆中,然后将大顶堆中最大元素取出放入小顶堆中,第奇个数,存入小顶堆,然后将小顶堆中最小元素取出放入大顶堆中。最后小顶堆存放的元素都大于大顶堆存放元素。

当数量为偶数时候,访问大顶堆中最大元素和小顶堆中最小元素平均值,当数量为奇数时,访问小顶堆中最小元素。

代码
var arr1=[],arr2=[],count=0;
function Insert(num)
{
    // write code here
    if(count%2==0){//第偶个数,存入大顶堆中
        arr1.push(num);
        //从小到大排序
        arr1.sort(function(a,b){
            return a-b;
        })
        arr2.push(arr1.pop());
        //从大到小排序
        arr2.sort(function(a,b){
            return b-a;
        })
    }else{//第奇个数,存入小顶堆
        arr2.push(num);
        arr2.sort(function(a,b){
            return b-a;
        })
        arr1.push(arr2.pop());
        arr1.sort(function(a,b){
            return a-b;
        })
    }
    count++
}
function GetMedian(){
    // write code here

    if(count%2!=0){
        return arr2[arr2.length-1]
    }else{
        return (arr1[arr1.length-1]+arr2[arr2.length-1])/2
    }
}

64、滑动窗口的最大值

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路

算出滑动窗口个数=数组元素个数-滑动窗口大小+1,然后遍历循环,将队列头元素取出,存入新元素。

代码

求出给定参数中最大的数——Math.max.apply(obj,parms);//这个obj对象将代替Function类里this对象,第二个传进来的是参数——加入apply解决数组的调用

function maxInWindows(num, size)
{
    // write code here
    if(size==0) return[];
    var result=[],windows=[];
    for(var i=0;i<size;i++){
        windows.push(num[i])
    };
    var len=num.length-size+1;//滑动窗口大小
    for(var i=0,j=size;i<len;i++){
        result.push(Math.max.apply(null,windows));
        windows.shift();
        windows.push(num[j++]);//迭代加入后一个值
    }
    return result;
}

65、矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

思路

回溯法
回溯法思路的简单描述是:把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解

代码
function hasPathCore(matrix, rows, cols, row, col, path, pathIndex, visited) {
  var hasPath = false;
  if (row < rows && col < cols && row >= 0 && col >= 0 && visited[row][col] === false) {
    if (matrix[row * cols + col] === path[pathIndex]) {
      visited[row][col] = true;
      if (pathIndex === path.length - 1) {
        hasPath = true;
      } else {
        hasPath = hasPathCore(matrix, rows, cols, row - 1, col, path, pathIndex + 1, visited) ||
                  hasPathCore(matrix, rows, cols, row + 1, col, path, pathIndex + 1, visited) ||
                  hasPathCore(matrix, rows, cols, row, col - 1, path, pathIndex + 1, visited) ||
                  hasPathCore(matrix, rows, cols, row, col + 1, path, pathIndex + 1, visited);

        if (!hasPath) {
          visited[row][col] = false;
        }
      }
    }
  }
  return hasPath;
}

function hasPath(matrix, rows, cols, path)
{
    // write code here
    if (path.length <= 0) {
      return true;
    }
    var visited = [];
    var temp = [];
    var i, j;
    for (i = 0; i < rows; i++) {
      temp = [];
      for (j = 0; j < cols; j++) {
        temp.push(false);
      }
      visited.push(temp);
    }
    for (i = 0; i < rows; i++) {
      for (j = 0; j < cols; j++) {
        if (hasPathCore(matrix, rows, cols, i, j, path, 0, visited)) {
          return true;
        }
      }
    }
    return false;
}

66、机器人的运动范围

题目描述

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路

回溯法

代码
function movingCount(threshold, rows, cols)
{
    // write code here
    var count=0;
    if(threshold<1||rows<1||cols<1){
        return count;
    }
    var visited=[];
    for(var i=0; i<rows; i++){
        visited[i]=[];
        for(var j=0; j<cols; j++){
            visited[i][j]=false;
        }
    }
    count = movingCountSum(threshold,0,0,rows,cols,visited);
    return count;
}
function movingCountSum(threshold,m,n,rows,cols,visited){
    var count = 0;
    if(m>=0&&m<rows&&n>=0&&n<cols&&!visited[m][n]&&getSum(m,n)<=threshold){
        visited[m][n]=true;
        count = 1+movingCountSum(threshold,m,n-1,rows,cols,visited)+
                movingCountSum(threshold,m,n+1,rows,cols,visited)+
                movingCountSum(threshold,m-1,n,rows,cols,visited)+
                movingCountSum(threshold,m+1,n,rows,cols,visited);
    }
    return count;
}
function getSum(m,n){
    var str = [].concat(m,n).join('');
    var sum=0;
    for(var i=0; i<str.length; i++){
        sum+=Number(str[i]);
    }
    return sum;
}

结言:看到这里,说明你得到初步胜利了,但是战争还是没有胜利,这些算法还是要反复刷,增强自己对代码的感觉!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值