剑指offer_13

剑指offer_13(最后6题)来源:牛客网

1. 序列化二叉树

题目:

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

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

参考代码:

/*
    算法思想:根据前序遍历规则完成序列化与反序列化。所谓序列化指的是遍历二叉树为字符串;所谓反序列化指的是依据字符串重新构造成二叉树。
    依据前序遍历序列来序列化二叉树,因为前序遍历序列是从根结点开始的。当在遍历二叉树时碰到Null指针时,这些Null指针被序列化为一个特殊的字符“#”。
    另外,结点之间的数值用逗号隔开。
*/
public class Solution {
    int index = -1;   //计数变量
  
    String Serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        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();
        //if(index >= len){
        //    return null;
       // }
        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;
  }
}


//其它
import java.util.*;
//采用层序遍历,不需要将转化为完全二叉树的简单方法
public class Solution {
    String Serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root != null)
            queue.add(root);
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(node != null){
                queue.offer(node.left);
                queue.offer(node.right);
                sb.append(node.val + ",");
            }else{
                sb.append("#" + ",");
            }
        }
        if(sb.length() != 0)
            sb.deleteCharAt(sb.length()-1);
        return sb.toString();
  }
    TreeNode Deserialize(String str) {
       TreeNode head = null;
        if(str == null || str.length() == 0)
            return head;
        String[] nodes = str.split(",");
        TreeNode[] treeNodes = new TreeNode[nodes.length];
        for(int i=0; i<nodes.length; i++){
            if(!nodes[i].equals("#"))
                treeNodes[i] = new TreeNode(Integer.valueOf(nodes[i]));
        }
        for(int i=0, j=1; j<treeNodes.length; i++){
            if(treeNodes[i] != null){
                treeNodes[i].left = treeNodes[j++];
                treeNodes[i].right = treeNodes[j++];
            }
        }
        return treeNodes[0];
  }
}
 
//前序遍历
public class Solution {
    String Serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        getSerializeString(root, sb);
        if(sb.length() != 0)
            sb.deleteCharAt(sb.length()-1);
        return sb.toString();
      }
    getSerializeString(TreeNode root, StringBuilder sb){
        if(root == null)
            sb.append("#,");
        else{
            sb.append(root.val + ",");
            getSerializeString(root.left, sb);
            getSerializeString(root.right, sb);
        }
    }
    TreeNode Deserialize(String str) {
       if(str == null || str.length() == 0 || str.length() ==1)
            return null;
        String[] nodes = str.split(",");
        TreeNode[] treeNodes = new TreeNode[nodes.length];
        for(int i=0; i<nodes.length; i++){
            if(!nodes[i].equals("#"))
                treeNodes[i] = new TreeNode(Integer.valueOf(nodes[i]));
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(treeNodes[0]);
        int i = 1;
        while(treeNodes[i] != null){
            stack.peek().left = treeNodes[i];
            stack.push(treeNodes[i++]);
        }
        while(!stack.isEmpty()){
            stack.pop().right = treeNodes[++i];
            if(treeNodes[i] != null){
                stack.push(treeNodes[i++]);
                while(treeNodes[i] != null){
                    stack.peek().left = treeNodes[i];
                    stack.push(treeNodes[i++]);
                }
            }
        }
        return treeNodes[0];
      }
}

2. 二叉搜索树的第k个结点

题目:

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

参考代码:

//我的做法
import java.util.*;
public class Solution {
    ArrayList<TreeNode> list = new ArrayList<>();
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(k<1)
            return null;
        MidSearch(pRoot);
        if(k>list.size())
            return null;
        return list.get(k-1);
    }
    
    void MidSearch(TreeNode pRoot){
        if(pRoot != null){
            MidSearch(pRoot.left);
            list.add(pRoot);
            MidSearch(pRoot.right);
        }
    }
}


//思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。
//     所以,按照中序遍历顺序找到第k个结点就是结果。
public class Solution {
   int index = 0; //计数器
    TreeNode KthNode(TreeNode root, int k)
    {
        if(root != null){ //中序遍历寻找第k个
            TreeNode node = KthNode(root.left,k);
            if(node != null)
                return node;
            index ++;
            if(index == k)
                return root;
            node = KthNode(root.right,k);
            if(node != null)
                return node;
        }
        return null;
    }
}


//非递归中序遍历
TreeNode KthNode(TreeNode root, int k){
        if(root==null||k==0)
            return null;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        int count = 0;
        TreeNode node = root;
        do{
            if(node!=null){
                stack.push(node);
                node = node.left;
            }else{
                node = stack.pop();
                count++;
                if(count==k)
                    return node;
                node = node.right;
            }
        }while(node!=null||!stack.isEmpty());
        return null;
    }

3. 数据流中的中位数

题目:

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

参考代码:

【java】这里讨论两种方法:
一:代码复杂:减少不必要插入,提高效率
二:代码大大简化:可能有不必要插入,效率有所降低
==============思路解析=================================
思考:如何得到一个数据流中的中位数?
如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
一:代码复杂:
* 分析:对于海量数据和流的数据,用最大堆和最小堆来管理
* 我们希望 数据分为[]|[]两个部分,细化一点
[最大堆 |   左边最大 leftMax] 
右边最小rightMin | 最小堆]


* 定义一个规则:保证左边和右边个数相差不大于1,且左边小于右边
* 1.数据是偶数的时候 insert的数据进入 [右边,最小堆]*  1.1当插入的数字cur > leftMax时,直接插入到[右边,最小堆]*  1.2当插入的数字cur < leftMax时,为了保证左边小于右边,
*      先把cur插入[最大堆|左边最大leftMax]中,
*      然后把leftMax放入[右边最小rightMin|最小堆]*      就可以保证: 左边 < 右边
* 2.数据是奇数的时候 insert的数据进入 [左边,最大堆]*      2.1当插入的数字cur < rightMin时,直接插入到[左边,最小堆]*      2.2当插入的数字cur > rightMin时,为了保证左边小于右边,
*      先把cur插入[右边最小rightMin|最小堆]中,
*      然后把rightMin放入[最大堆|左边最大leftMax]*      就可以保证: 左边 < 右边
* 最后:
* 如果是偶数:中位数mid= (leftMax+right)/2
* 如果是奇数:中位数mid= leftMax 因为先插入到左边,再插入到右边,为奇数时,中位数就是mid
//降序就是最大堆,lambda表达式了解下
    private static PriorityQueue<Integer> leftHeap = new PriorityQueue<>((x, y) -> y - x);
    //升序就是最小堆
    private static PriorityQueue<Integer> rightHeap = new PriorityQueue<>();
    //当前是奇数还是偶数
    private static boolean isOdd = true;
    public static void Insert(Integer num) {
        if(isOdd) {//数据是奇数的时候 insert的数据进入 [左边,最大堆]中
            if(leftHeap.isEmpty()) {
                leftHeap.add(num);
            }
            else {//这个时候rightHeap一定不是null,就不讨论了。考虑鲁棒性可以讨论
                int rightMin = rightHeap.peek();
                if(num <= rightMin) {//直接插入到[左边,最小堆]中
                    leftHeap.add(num);
                }else {
                    rightHeap.add(num);//先把cur插入[右边最小rightMin|最小堆]中
                    leftHeap.add(rightHeap.poll());//然后把rightMin放入[最大堆|左边最大leftMax]中
                }
            }
        }else {//数据是偶数的时候 insert的数据进入 [右边,最小堆]中
            //这个时候leftHeap一定不是null,就不讨论了。考虑鲁棒性可以讨论
                int leftMax = leftHeap.peek();
                if(num >= leftMax) {//直接插入到[右边,最小堆]中
                    rightHeap.add(num);
                }else {
                    leftHeap.add(num);//先把cur插入[右边最小rightMin|最小堆]中,
                    rightHeap.add(leftHeap.poll());//然后把rightMin放入[最大堆|左边最大leftMax]中
                }
        }
        isOdd = !isOdd;//改变奇偶性
    }
 
    public static Double GetMedian() {
        if(leftHeap.isEmpty()) return 0.0;
        if(!isOdd)//这里插入num改变了奇偶性,这里是奇数的意思
            return leftHeap.peek() / 1.0;
        else
            return (leftHeap.peek() + rightHeap.peek()) / 2.0;
    }.简化代码,取消了判断过程,直接插入到对面的堆中,然后再转移到自己的堆中
* 但是!!!时间复杂度提高,每次都插入时间复杂度O(log n)这是很可怕的
* 定义一个规则:不要判断了
* 1.数据是偶数的时候 insert的数据进入 [右边,最小堆]*      先把cur插入[最大堆|左边最大leftMax]中,
*      然后把leftMax放入[右边最小rightMin|最小堆]*      就可以保证: 左边 < 右边
* 2.数据是奇数的时候 insert的数据进入 [左边,最大堆]*      先把cur插入[右边最小rightMin|最小堆]中,
*      然后把rightMin放入[最大堆|左边最大leftMax]*      就可以保证: 左边 < 右边
* 最后:
* 如果是偶数:中位数mid= (leftMax+right)/2
* 如果是奇数:中位数mid= leftMax
看看是不是简化了很多,哈哈!
链接:https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1
来源:牛客网

//降序就是最大堆,lambda表达式了解下
    private static PriorityQueue<Integer> leftHeap = new PriorityQueue<>((x, y) -> y - x);
    private static PriorityQueue<Integer> rightHeap = new PriorityQueue<>();
    private static boolean isOdd = true;
    public static void Insert(Integer num) {
        if(isOdd) {
            rightHeap.add(num);
            leftHeap.add(rightHeap.poll());
        }else {
            leftHeap.add(num);
            rightHeap.add(leftHeap.poll());
        }
        isOdd = !isOdd;
    }
    public static Double GetMedian() {
        if(leftHeap.isEmpty()) return 0.0;
        if(!isOdd)
            return leftHeap.peek() / 1.0;
        else
            return (leftHeap.peek() + rightHeap.peek()) / 2.0;
    }

4. 滑动窗口的最大值

题目:

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。

参考代码:

//我的做法
import java.util.*;
public class Solution {
    ArrayList<Integer> res = new ArrayList<>();
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        if(num==null || size<1)
            return res;
        for(int i=0;i<=num.length-size;i++){
            int max=num[i];
            for(int j=0;j<size;j++){
                if(num[i+j]>max)
                    max = num[i+j];
            }
            res.add(max);
        }
        return res;
    }
}


//其它
import java.util.*;
/**
用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次
1.判断当前最大值是否过期
2.新增加的值从队尾开始比较,把所有比他小的值丢掉
*/
public class Solution {
   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;
            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;
    }
}


//another
import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        if (num == null || num.length == 0 || size <= 0 || num.length < size) {
            return new ArrayList<Integer>();
        }
        ArrayList<Integer> result = new ArrayList<>();
        //双端队列,用来记录每个窗口的最大值下标
        LinkedList<Integer> qmax = new LinkedList<>();
        for (int i = 0; i < num.length; i++) {
            while (!qmax.isEmpty() && num[qmax.peekLast()] < num[i]) {
                qmax.pollLast();
            }
            qmax.addLast(i);
            //判断队首元素是否过期
            if (qmax.peekFirst() == i - size) {
                qmax.pollFirst();
            }
            //向result列表中加入元素
            if (i >= size - 1) {
                result.add(num[qmax.peekFirst()]);
            }
        }
        return result;
    }
}


//其它,deque s中存储的是num的下标
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> res;
        deque<int> s;
        for(unsigned int i=0;i<num.size();++i){
            while(s.size() && num[s.back()]<=num[i])//从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
                s.pop_back();
            while(s.size() && i-s.front()+1>size)//当当前窗口移出队首元素所在的位置,即队首元素坐标对应的num不在窗口中,需要弹出
                s.pop_front();
            s.push_back(i);//把每次滑动的num下标加入队列
            if(size&&i+1>=size)//当滑动窗口首地址i大于等于size时才开始写入窗口最大值
                res.push_back(num[s.front()]);
        }
        return res;
    }
};

5. 矩阵中的路径

题目:

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

参考代码:

public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        //标志位,初始化为false
        boolean[] flag = new boolean[matrix.length];
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                 //循环遍历二维数组,找到起点等于str第一个元素的值,再递归判断四周是否有符合条件的----回溯法
                 if(judge(matrix,i,j,rows,cols,flag,str,0)){
                     return true;
                 }
            }
        }
        return false;
    }
     
    //judge(初始矩阵,索引行坐标i,索引纵坐标j,矩阵行数,矩阵列数,待判断的字符串,字符串索引初始为0即先判断字符串的第一位)
    private boolean judge(char[] matrix,int i,int j,int rows,int cols,boolean[] flag,char[] str,int k){
        //先根据i和j计算匹配的第一个元素转为一维数组的位置
        int index = i*cols+j;
        //递归终止条件
        if(i<0 || j<0 || i>=rows || j>=cols || matrix[index] != str[k] || flag[index] == true)
            return false;
        //若k已经到达str末尾了,说明之前的都已经匹配成功了,直接返回true即可
        if(k == str.length-1)
            return true;
        //要走的第一个位置置为true,表示已经走过了
        flag[index] = true;
         
        //回溯,递归寻找,每次找到了就给k加一,找不到,还原
        if(judge(matrix,i-1,j,rows,cols,flag,str,k+1) ||
           judge(matrix,i+1,j,rows,cols,flag,str,k+1) ||
           judge(matrix,i,j-1,rows,cols,flag,str,k+1) ||
           judge(matrix,i,j+1,rows,cols,flag,str,k+1)  )
        {
            return true;
        }
        //走到这,说明这一条路不通,还原,再试其他的路径
        flag[index] = false;
        return false;
    }
}

6. 机器人的运动范围

题目:

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

参考代码:

核心思路:
1.(0,0)开始走,每成功走一步标记当前位置为true,然后从当前位置往四个方向探索,
返回1 + 4 个方向的探索值之和。
2.探索时,判断当前节点是否可达的标准为:
1)当前节点在矩阵内;
2)当前节点未被访问过;
3)当前节点满足limit限制。
public class Solution {
        public int movingCount(int threshold, int rows, int cols) {
        boolean[][] visited = new boolean[rows][cols];
        return countingSteps(threshold,rows,cols,0,0,visited);
    }
    public int countingSteps(int limit,int rows,int cols,int r,int c,boolean[][] visited){
        if (r < 0 || r >= rows || c < 0 || c >= cols
                || visited[r][c] || bitSum(r) + bitSum(c) > limit)  return 0;
        visited[r][c] = true;
        return countingSteps(limit,rows,cols,r - 1,c,visited)
                + countingSteps(limit,rows,cols,r,c - 1,visited)
                + countingSteps(limit,rows,cols,r + 1,c,visited)
                + countingSteps(limit,rows,cols,r,c + 1,visited)
                + 1;
    }
    public int bitSum(int t){
        int count = 0;
        while (t != 0){
            count += t % 10;
            t /= 10;
        }
        return  count;
    }
}


//python
//思路:将地图全部置1,遍历能够到达的点,将遍历的点置0并令计数+1.这个思路在找前后左右相连的点很有用,比如leetcode中的海岛个数问题/最大海岛问题都可以用这种方法来求解。
class Solution:
    def __init__(self):
        self.count = 0
 
    def movingCount(self, threshold, rows, cols):
        # write code here
        arr = [[1 for i in range(cols)] for j in range(rows)]
        self.findway(arr, 0, 0, threshold)
        return self.count
 
    def findway(self, arr, i, j, k):
        if i < 0 or j < 0 or i >= len(arr) or j >= len(arr[0]):
            return
        tmpi = list(map(int, list(str(i))))
        tmpj = list(map(int, list(str(j))))
        if sum(tmpi) + sum(tmpj) > k or arr[i][j] != 1:
            return
        arr[i][j] = 0
        self.count += 1
        self.findway(arr, i + 1, j, k)
        self.findway(arr, i - 1, j, k)
        self.findway(arr, i, j + 1, k)
        self.findway(arr, i, j - 1, k)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值