第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;
}
}