[Java]剑指offer61-66题_day13

61序列化二叉树

题目描述

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

思路:

对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的节点不为空时,在转化val所得的字符之后添加一个’,‘作为分割,对于空节点则使用’#'代替
对于反序列化:按照前序顺序,递归的使用字符串的字符创建一个二叉树

package com.matajie;

/**
 * 61序列化二叉树
 * 题目描述
 *
 * 请实现两个函数,分别用来序列化和反序列化二叉树
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class Serialize {
    public int index = -1;
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }

    }
    String Serialize(TreeNode root) {
         StringBuffer sb = new StringBuffer();
         if(root == null){
             sb.append("#,");
         return sb.toString();
         }
    sb.append(root.val+",");
    sb.append(Serialize(root.left));
    sb.append(Serialize(root.right));
    return sb.toString();
    }
    TreeNode Deserialize(String str) {
         index++;
         int len = str.length();
    String[] strr =  str.split(",");
    TreeNode node = null;
    if(!strr[index].equals("#")){
        node = new TreeNode(Integer.valueOf(strr[index]));
        node.left = Deserialize(str);
        node.right = Deserialize(str);
    }
    return node;
    }
}

62.二叉搜索树的第K个节点

题目描述

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

思路:

二叉搜索树按照中序遍历的顺序打印出来正好就是排序号的顺序,所以按照中序遍历顺序找到第K个节点就是结果

package com.matajie;

/**
 * 62.二叉搜索树的第K个节点
 * 题目描述
 *
 * 给定一棵二叉搜索树,请找出其中的第k小的结点。
 * 例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。
 *
 * #
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class KthNode {
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }

    }
         int index = 0;//放在方法里每一次都重置
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot != null){
            TreeNode node = KthNode(pRoot.left,k);
            if(node != null)
                return node;
            index++;
            if(index == k)
                return pRoot;
            node = KthNode(pRoot.right,k);
            if(pRoot != null)
                return node;
        }
        return null;
    }
}

解释: if(pRoot != null)这行代码
如果没有这行代码,那么pRoot就是返回给上一级的父节点的,而不是递归结束的条件了,有了这行代码,一旦返回了pRoot,那么node就不会为空了,就一直一层一层的递归出去到结束了.
例如{8,6,5,7},K=1答案应该时是5,如果不加的话,
开始pRoot = 8,node = KthNode(6,1)
继续pRoot = 6,node = KthNode(5,1)
pRoot返回null
这时向下执行index = k =1了,返回5给pRoot = 6递归时候的node,这时回到pRoot = 8了.往后面调右孩子的时候为空而把5给覆盖了,有了这句话虽然为空,但不把null返回,而是继续返回5

63.数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

思路:

用Java集合priorityQueue来设置一个小顶堆和大顶堆.因为要求是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排序,小顶堆存较大的数,从小到大排序.显然中位数就是大顶堆的根节点和小顶堆的根节点的和的平均数.

  1. 小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值.
  2. 当数目为偶数时,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中.
  3. 当数目为奇数时,将这个值插入小顶堆中,再将小顶堆中根节点(即最小值)插入到大顶堆中.
  4. 取中位数时如果当前个数为偶数,显然是取小顶堆和大顶堆根节点的平均值,如果当前个数为奇数,显然是取小顶堆的根节点.

例如:[5,2,3,4,1,6,7,0,8]

  1. 5先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,min = [5],max = [无],avg = [5.00].
  2. 5先进入小顶堆,然后将大顶堆中最小值放入大顶堆中,min = [5],max = [2],avg = [3.50].
  3. 5先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,min = [3.5],max = [2],avg = [3.00].
  4. 5先进入小顶堆,然后将大顶堆中最小值放入大顶堆中,min = [4,5],max = [3,2],avg = [3.50].
  5. 5先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,min = [3,45],max=[2,1],avg = [3.00].
package com.matajie;

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class Insert {
    private PriorityQueue<Integer> minHeap = new PriorityQueue();
    private PriorityQueue<Integer> maxHeap = new PriorityQueue(15, new Comparator<Integer>() {
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });
    int count = 0;//记录偶数个还是奇数个
    public void Insert(Integer num) {
       if(count % 2 == 0){
           maxHeap.offer(num);
           int max = maxHeap.poll();
           minHeap.offer(max);
       }else {
           minHeap.offer(num);
           int min = minHeap.poll();
       maxHeap.offer(min);
       }
       count++;
    }

    public Double GetMedian() {
if(count % 2 == 0)
   return new Double(minHeap.peek()+maxHeap.peek())/2;//偶数个求平均
 else {
     return new Double (minHeap.peek());//奇数个取小顶堆即可
 }
    }

}

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]}。

思路:

滑动窗口应当是队列,但为了得到滑动窗口的最大值,队列序可以从两端删除元素,因此使用双端队列.
对新来的元素K,将其与双端队列中的元素相比较.

  1. 前面比K小的,直接移出队列(因为不可能成为后面滑动窗口的最大值了)
  2. 前面比K大的X,比较两者下标判断X是否已经不在窗口之内,不在了直接移出队列,队列第一个位置保存当前窗口的最大值.
package com.matajie;

import java.util.ArrayDeque;
import java.util.ArrayList;

/**
 * 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]}。
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class MaxInWindows {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
     ArrayList<Integer> res = new ArrayList<>();
     if(size == 0) return res;
     int begin;
     ArrayDeque<Integer> q = new ArrayDeque<>();//双端队列保存的是下标
     for(int i = 0;i<num.length;i++){
         begin = i - size + 1;//begin用来保存当前窗口的第一个值在原始数组中的下标,这样写的目的是为了少些一些判断边界条件.
         if(q.isEmpty()){
             q.add(i);
         }else if(begin > q.peekFirst())
             q.pollFirst();
         while ((!q.isEmpty() && num[q.peekLast()] <= num[i]))
             q.pollLast();//返回队列第一个元素,但不删除这个元素
         q.add(i);
         if(begin >= 0)
             res.add(num[q.peekFirst()]);
     }
     return res;
    }
}

65.矩阵中的路径

题目描述

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

思路:

用一个状态数组保存之前访问过的字符,然后再分别按上下左右递归

package com.matajie;

/**
 * 65.矩阵中的路径
 * 题目描述
 *
 * 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
 * 路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
 * 如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。
 * 例如      a b c e      s f c s      a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,
 * 但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class HasPath {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
      int flag[] = new int[matrix.length];
      for(int i = 0 ;i<rows;i++){
          for(int j = 0;j<cols;j++){
              if(helper(matrix,rows,cols,i,j,str,0,flag))//回溯法:循环遍历二维数组,找到七点等于str第一个元素的值,再递归判断四周是否又符合条件的
                  return true;
          }
      }
      return false;
    }
                           //初始矩阵     矩阵行数  矩阵列数 索引行坐标i,索引纵坐标j,   k:字符串索引初始为0,即先判断字符串的第一位  
    private boolean helper(char[] matrix,int rows,int cols,int i ,int j, char[] str,int k,int[] flag){
        int index = i * cols + j;//i是行数,每行有cols个数,所以i*cols,第j列就+j,,即根据i和j计算匹配的第一个元素转为一维数组的位置
        if(i < 0 || i >= rows || j <0 || j >= cols || matrix[index] != str[k] || flag[index] == 1)//递归终止条件
            return false;
        if( k == str.length-1)//若k已经到达str末尾,说明之前的已经匹配成功,直接返回true即可
            return true;
        flag[index] = 1;//要走的位置置为1,表示已经走过
        //如果4个判断条件全部为false,说明不应该来到index寻找路径,也就是我们不走index这个位置了,回到index之前的位置,所以flag[index] = 0,
        //表示我们暂时不走index了,但是以后是可能走的,如果不重设flag[index] = 0,以后也走不了了
        //递归寻找,每次找到了就给k+1,找不到还原
        if(helper(matrix,rows,cols,i-1,j,str,k+1,flag)//
                ||helper(matrix,rows,cols,i+1,j,str,k+1,flag)
                ||helper(matrix,rows,cols,i,j-1,str,k+1,flag)
                ||helper(matrix,rows,cols,i,j+1,str,k+1,flag)){
            return true;
        }
        flag[index] = 0;//条件不符合,还原为未访问过的标记.
        return false;
    }
}

66.机器人的运动范围

题目描述

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

思路:回溯法

当机器人准备进入坐标为(i,j)的格子时,通过检查坐标的数位和来判断机器人是否能够进入,如果能进,再判断它能否进入四个相邻的格子(i,j-1),(i-1,j),(i,j+1)和(i+1,j).

package com.matajie;

/**
 * 66.机器人的运动范围
 * 题目描述
 *
 *
 * 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,
 * 每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
 * 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
 * 但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class MovingCount {
    public int movingCount(int threshold, int rows, int cols)
    {
      int flag[][] = new int[rows][cols];//记录是否已经走过
        return helper(0,0,rows,cols,flag,threshold);
    }
    private int helper(int i ,int j,int rows,int cols,int[][] flag,int threshold){
        //判断flag[i][j],如果它为1,则说明已经走过,就会返回0
        if(i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j) > threshold||flag[i][j] == 1){
            return 0;
        }
        flag[i][j] = 1;
        return helper(i-1,j,rows,cols,flag,threshold)
                + helper(i+1,j,rows,cols,flag,threshold)
                + helper(i,j-1,rows,cols,flag,threshold)
                + helper(i,j+1,rows,cols,flag,threshold)
                +1;
    }
    private int numSum(int i){
        int sum = 0;
        do {
            sum += i%10;
        }while ((i = i/10) > 0);
        return sum;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值