剑指offer系列之树(p61-63)

34 篇文章 0 订阅
15 篇文章 0 订阅

61. 二叉树的序列化和反序列化

题目描述

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

思路

利用先序遍历的思想来实现序列化和反序列化。序列化就是将树转化为字符串保存起来,相邻结点之间用,分隔,空结点使用#表示;反序列化就是根据字符串还原二叉树。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.index = -1
        
    def Serialize(self, root):
        # write code here
        string = []
        def preOrder(p):
            if p == None:
                string.append('#,')
            else:
                string.append(str(p.val) + ',')
                preOrder(p.left)
                preOrder(p.right)
        preOrder(root)
        string = ''.join(string)
        return string
    
    def Deserialize(self, s):
        # write code here
        self.index += 1
        sp = s.split(',')
        node = None
        if sp[self.index] != '#':
            node = TreeNode(int(sp[self.index]))
            node.left = self.Deserialize(s)
            node.right = self.Deserialize(s)
        return node

简化了一点:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.index = -1
        
    def Serialize(self, root):
        # write code here
        if root == None:
            return '#,'
        return str(root.val) + ',' + self.Serialize(root.left) + self.Serialize(root.right)
    
    def Deserialize(self, s):
        # write code here
        self.index += 1
        sp = s.split(',')
        node = None
        if sp[self.index] != '#':
            node = TreeNode(int(sp[self.index]))
            node.left = self.Deserialize(s)
            node.right = self.Deserialize(s)
        return node

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

题目描述

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

思路

思路一:中序遍历的递归算法

利用一个列表保存中序遍历的结果,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( l o g h + n ) , h O(logh+n),h O(logh+n),h为树的深度.

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        # write code here
        res = []
        def InOrder(root):
            if not root:
                return None
            InOrder(root.left)
            res.append(root)
            InOrder(root.right)
        InOrder(pRoot)
        return res[k - 1] if 0 < k <= len(res) else None 
思路二:中序遍历的非递归算法

利用中序遍历的非递归算法。时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( h ) , h O(h),h O(h),h为树的深度。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        # write code here
        count = 0
        if k <= count:
            return None
        p, stack = pRoot, []
        while p or len(stack) > 0:
            while p:
                stack.append(p)
                p = p.left
            tmp = stack.pop(-1)
            count += 1
            if count == k:
                return tmp
            p = tmp.right
        return None        

63. 数据流中的中位数

题目描述

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

思路

思路一

每次插入时都把数据流调整为有序的,因此插入操作就相当于向有序数组中插入一个数,时间复杂度为 O ( n ) O(n) O(n);取中位数时时间复杂度为 O ( 1 ) O(1) O(1)。注意这里牛客网有个bug,GetMedian函数必须多加一个任意参数才能通过。

class Solution:
    def __init__(self):
        self.data = []
    def Insert(self, num):
        if len(self.data) == 0:
            self.data.append(num)
        else:
            i = 0
            # 找到num应该被插入的位置
            while i < len(self.data) and self.data[i] < num:
                i += 1
            # num大于现存所有值
            if i == len(self.data):
                self.data.append(num)
            else:
                self.data.append(0)
                for j in range(len(self.data) - 2, i - 1, -1):
                    self.data[j + 1] = self.data[j]
                self.data[i] = num
    def GetMedian(self, damn):                   
        length = len(self.data)                 
        if length%2 == 1:                      
            return self.data[length//2]       
        else:                                  
            return (self.data[length//2] + self.data[length//2-1])/2.0    
思路二

该思路相当于把有序数组分成两个部分,左边一部分使用大根堆存储,右边一部分用小根堆存储。只要能保证两个堆大小相差不超过1,就很容易利用两个堆获得中位数,此时两个堆的堆顶值就为中间的值。具体操作如下。
插入操作:
首先判断大根堆是否为空:
如果为空则直接插入;
若不为空,则使用堆顶元素和待插入元素比较大小,若前者更大,则说明待插元素应该是属于左半部分的,即应该插入大根堆;若后者更大,则说明应该插入到右半部分,即小根堆。每次插入后都进行堆调整操作。
插入操作完毕后,需要调整两个堆长度差不超过1,如果长度差超过了1则将更长的堆的堆顶元素弹出并压入到较短的堆中。这个步骤主要是为了方便取中位数。
取中位数:
根据两个堆的长度差情况取出中位数。注意这里取堆顶元素直接使用heap[0]即可,不能使用heappop,这样会导致数据流序列变化。
除此之外,需要注意的是,python的堆默认为小根堆,因此对于大根堆的插入,要把数字乘以 − 1 -1 1,这样才能保证堆顶元素的相反数是最大值。

# -*- coding:utf-8 -*-
import heapq
class Solution:
    def __init__(self):
        self.small_heap = []
        self.big_heap = []
        
    def Insert(self, num):
        # write code here
        if len(self.big_heap) == 0:
            self.big_heap.append(-1 * num)
        elif num < self.big_heap[0] * -1:
            heapq.heappush(self.big_heap, -1 * num)
        else:
            heapq.heappush(self.small_heap, num)
        length_diff = len(self.big_heap) - len(self.small_heap)
        if length_diff > 1:
            tmp = heapq.heappop(self.big_heap)
            heapq.heappush(self.small_heap, -1 * tmp)
        elif length_diff < -1:
            tmp = heapq.heappop(self.small_heap)
            heapq.heappush(self.big_heap, -1 * tmp)
            
    def GetMedian(self, m):
        # write code here
        length_diff = len(self.big_heap) - len(self.small_heap)
        if length_diff > 0:
            return -1 * self.big_heap[0]
        elif length_diff == 0:
            return (-1 * self.big_heap[0] + self.small_heap[0]) / 2.0
        else:
            return self.small_heap[0]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值