第60题
题目描述:
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己的函数来解析回这个二叉树。
算法分析:
一般解决二叉树的问题都可以使用递归。序列化的时候比较简单,但是要注意使用"#"表示空结点,有助于后面逆序列化的操作。逆序列化的时候我们要对字符串进行分割,分割成字符数组。逆序列化的过程类似序列化的过程。
代码如下:
public class Solution {
//序列化二叉树
//数与数之间以","间隔,便于反序列化的时候分割
int index;
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) {
if(str==null){
return null;
}
index = -1;
String[] strArray = str.split(",");
return DeserializeStr(strArray);
}
public TreeNode DeserializeStr(String[] strArray){
index++;
TreeNode treeNode = null;
//没到最终结点
if(!strArray[index].equals("#")){
treeNode = new TreeNode(Integer.parseInt(strArray[index]));
treeNode.left = DeserializeStr(strArray);
treeNode.right = DeserializeStr(strArray);
}
return treeNode;
}
}
第61题
题目描述:
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
思路分析:
二叉搜索树满足根节点左子树的结点值全部小于根节点,根节点右子树的值全部大于根节点。所以当对二叉搜索树按中序序列遍历时,得到的刚好是从小到大的序列。
代码如下:
public class Solution {
//二叉搜索树按中序遍历顺序打印即为从小到大打印一棵树
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot == null || k<1){
return null;
}
int index = 0;
//借助栈实现二叉树中序的非递归遍历
Stack<TreeNode> stack =new Stack<TreeNode>();
while(pRoot!=null || !stack.isEmpty()){
while(pRoot!=null){
stack.push(pRoot);
pRoot = pRoot.left;
}
pRoot = stack.pop();
index ++;
if(index == k){
return pRoot;
}
pRoot = pRoot.right;
}
return null;
}
}
第62题
题目描述:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路分析:
代码如下:
public class Solution {
//从小到大排序
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
//从大到小排序
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>((o1,o2)->{
return o2 - o1;
});
int count = 0;
public void Insert(Integer num) {
//为偶数
if(count%2==0){
maxHeap.offer(num);
minHeap.offer(maxHeap.poll());
}else{
//为奇数
minHeap.offer(num);
maxHeap.offer(minHeap.poll());
}
count++;
}
public Double GetMedian() {
if(count%2==0){
return new Double(minHeap.peek()+maxHeap.peek())/2;
}
return new Double(minHeap.peek());
}
}
第63题
题目描述:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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是否已不在窗口之内,不在了,直接移出队列队列的第一个元素是滑动窗口中的最大值。
代码如下:
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
//用于存储最后的结果
ArrayList<Integer> result = new ArrayList<>();
if (num == null) {
return result;
}
if (num.length < size || size < 1) {
return result;
}
//用做双向链表,存储的是元素的下标
LinkedList<Integer> indexDeque = new LinkedList<>();
for(int i=0;i<num.length;i++){
//当新进的元素大于数组中最后一个元素(最小值),删除最小值
while(!indexDeque.isEmpty()&&num[i]>num[indexDeque.getLast()]){
indexDeque.removeLast();
}
indexDeque.addLast(i);
//判断队列首部是否过期
if(indexDeque.getFirst() == i - size){
indexDeque.removeFirst();
}
//从第三个值开始往result添加元素
if(i>=size-1){
result.add(num[indexDeque.getFirst()]);
}
}
return result;
}
}