Dongxing's Wiki Dongxing's Wiki
首页
  • 剑指 Offer
  • LeetCode
  • 算法与数据结构
  • Python 语言
  • Web 开发
  • Hive
  • Elastic Search
  • 机器学习
  • NLP
  • 检索技术
  • 数据分析
  • 经验笔记
  • Linux 配置
  • 博客进化记
  • 杂谈
GitHub (opens new window)
首页
  • 剑指 Offer
  • LeetCode
  • 算法与数据结构
  • Python 语言
  • Web 开发
  • Hive
  • Elastic Search
  • 机器学习
  • NLP
  • 检索技术
  • 数据分析
  • 经验笔记
  • Linux 配置
  • 博客进化记
  • 杂谈
GitHub (opens new window)
  • 剑指offer

    • 剑指 Offer - python 题解
    • 剑指Offer(1)
    • 剑指Offer(2)
    • 剑指Offer(3)
    • 剑指Offer(4)
    • 剑指Offer(5)
    • 剑指Offer(6)
    • 剑指Offer(7)
    • 剑指Offer(8)
    • 剑指Offer(9)
    • 剑指Offer(10)
    • 剑指Offer(11)
    • 剑指Offer(12)
    • 剑指Offer(13)
    • 剑指Offer(14)
      • 二叉树的下一个结点
        • 问题
        • 思路
        • 代码
      • 对称的二叉树
        • 问题
        • 思路
        • 代码
      • 按之字形打印二叉树
        • 问题
        • 思路
        • 代码
      • 把二叉树打印成多行
        • 问题
        • 思路与代码
      • 序列化二叉树
        • 问题
        • 思路
        • 代码
      • 二叉搜索树的第k个节点
        • 问题
        • 思路
        • 代码
      • 数据流中的中位数
        • 问题
        • 思路
        • 代码
    • 剑指Offer(15)
  • 剑指Offer
  • 剑指offer
anthony
2018-10-07
目录

剑指Offer(14)

更新:此题解 markdown 源文件已放到 Github https://github.com/bytetopia/offer-coding-interviews-python (opens new window),欢迎前去给个小星星鸭!


# 二叉树的下一个结点

# 问题

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

# 思路

以这个图为例来分析一下。

  • 如果当前节点有右子树,那么中序的下一个节点就是右子树的最左节点。如图中的b,中序的下一个是h。只需沿着右子树,一直往左下找就行。
  • 如果没有右子树
    • 如果它是父节点的左子节点,那么中序的下一个就是它的父节点,如图中的f,下一个是c
    • 如果是父节点的右子节点,就需要往上找父节点,直到找到一个节点x,x是它的父节点的左子节点,这样的话x的父节点就是要找的节点。例如图中i,沿着往上找到b是a的左子节点,那么a就是i中序的下一个。如果找不到,例如图中g,a已经没有父节点,所以g没有下一个中序节点。

# 代码

按照上面思路来写就好,注意不要弄混这些父节点和子节点的关系之类

# -*- coding:utf-8 -*-
# class TreeLinkNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#         self.next = None
class Solution:
    def GetNext(self, pNode):
        if not pNode:
            return None
        if pNode.right:
            node = pNode.right
            while node.left:
                node = node.left
            return node
        else:
            if pNode.next and pNode.next.left == pNode:
                return pNode.next
            else:
                node = pNode.next
                while node:
                    if node.next and node.next.left == node:
                        return node.next
                    else:
                        node = node.next
                return node
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 对称的二叉树

# 问题

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

# 思路

想象一下如果人来做比较,应该如何进行。首先判断根节点的左右,都是6,相等;再判断两个6为根的两棵树,先判断左边的左子树为5,右边的右子树为5,相等;左边没有左子树和右子树了,判断左边的右子树为7,右边的左子树为7,相等;右边也没有左子树和右子树了;……

其实更严格来说,是把根节点的左右两棵树,左边以前序遍历来遍历,右边对应地以一种对称的前序遍历方式,找到和左边对称位置的节点,来比较是否对称。可采用一种递归的方法。

# 代码

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def isSymmetrical(self, pRoot):
        if not pRoot:
            return True
        else:
            return self.check(pRoot.left, pRoot.right)
    
    def check(self, left, right):
        if (not left) and (not right):
            return True
        elif not(left and right):
            return False
        if left.val != right.val:
            return False
        return self.check(left.left, right.right) and self.check(right.left, left.right)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 按之字形打印二叉树

# 问题

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

# 思路

类似层级遍历二叉树 (opens new window) 的做法,只不过现在需要根据层的奇偶来改变加入的顺序,不能只简简单单用一个队列来解决了。

先来分析一下如图,第0层,先a,然后将a的右c-左b加入下一层;第1层,依次输出c,b,但注意的是却需要先将b的左d-右e加入下一层,才能将c的左f右g加入下一层。也就是说,在这一层时,打印的顺序 和 遍历节点并将节点孩子加入下一层 的顺序,是相反着的。这点特别要注意。

然后就可以循环着来做了,这里使用了两个list交替存储奇数层和偶数层。

# 代码

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Print(self, pRoot):
        if not pRoot:
            return []
        result = []
        stack = [[pRoot], []]
        current = 0
        while stack[0] or stack[1]:
            if current == 0:
				# 当前偶数下标层,先按list中顺序打印元素
                result.append([p.val for p in stack[0]])
				# 然后逆序遍历,并将每个元素的 右-左 先后加入下一层
                for i in range(len(stack[0])-1, -1, -1):
                    p = stack[0][i]
                    if p.right:
                        stack[1].append(p.right)
                    if p.left:
                        stack[1].append(p.left)
                stack[0] = []
            else:
				# 当前奇数下标层,先按list顺序打印元素
                result.append([p.val for p in stack[1]])
				# 然后逆序遍历,并将每个元素的 左-右 先后加入下一层
                for i in range(len(stack[1])-1, -1, -1):
                    p = stack[1][i]
                    if p.left:
                        stack[0].append(p.left)
                    if p.right:
                        stack[0].append(p.right)
                stack[1] = []
            current = 1 - current
        return result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 把二叉树打印成多行

# 问题

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

# 思路与代码

依然是层级遍历,但这次要注意区分每一层,因为需要换行。 我写的思路(插入特殊的节点,作为换行标记):

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        if not pRoot:
            return []
        sep = TreeNode(-1)
        result = []
        queue = [pRoot, sep]
        temp = []
        while queue:
            current = queue.pop(0)
            if current != sep:
                temp.append(current.val)
                if current.left:
                    queue.append(current.left)
                if current.right:
                    queue.append(current.right)
            else:
                result.append(temp)
                temp = []
                if queue:
                    queue.append(sep)
        return result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

如果不插入特殊标记,其实很简单只需要在每一行开始时计算一下queue的长度,就是这一行的节点数目了。

class Solution:
    # 返回二维列表[[1,2],[4,5]]
    def Print(self, pRoot):
        if not pRoot:
            return []
        result = []
        queue = [pRoot]
        while queue:
            temp = []
            length = len(queue)
            for i in range(length):
                current = queue.pop(0)
                temp.append(current.val)
                if current.left:
                    queue.append(current.left)
                if current.right:
                    queue.append(current.right)
            result.append(temp)
        return result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 序列化二叉树

# 问题

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

# 思路

递归前序遍历二叉树,对每个节点的孩子如果遇到空就以#来代替,最终得到一个字符串。 读取字符串,当前字符作为根,下一个字符作为当前的左孩子,依此递归进行前序遍历,恢复树结构。

# 代码

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.flag = -1
         
    def Serialize(self, root):
        # write code here
        if not root:
            return '#,'
        return str(root.val)+','+self.Serialize(root.left)+self.Serialize(root.right)
         
    def Deserialize(self, s):
        # write code here
        self.flag += 1
        l = s.split(',')
         
        if self.flag >= len(s):
            return None
        root = None
         
        if l[self.flag] != '#':
            root = TreeNode(int(l[self.flag]))
            root.left = self.Deserialize(s)
            root.right = self.Deserialize(s)
        return root
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 二叉搜索树的第k个节点

# 问题

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

# 思路

二叉搜索树,左子树节点均小于根,右子树节点值均大于根,按中序遍历,找到中序的第k个节点,就是第k小的节点了。

# 代码

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def __init__(self):
        self.current = 0
        
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        if not pRoot:
            return None
        left_val = self.KthNode(pRoot.left, k)
        if left_val:
            return left_val
        self.current += 1
        if self.current == k:
            return pRoot
        right_val = self.KthNode(pRoot.right, k)
        if right_val:
            return right_val
        return None
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 数据流中的中位数

# 问题

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

# 思路

最直接的就是存着所有数字,加入一个新的之后重新排序取中间;或者加入新数字时就直接加入到指定位置上,比如用链表之类的存储。

另外可采用堆的思路:

1.维护一个大顶堆,一个小顶堆,且保证两点:
	1)小顶堆里的全大于 大顶堆里的;
	2)2个堆个数的差值小于等于1
2.当insert的数字个数为奇数时:使小顶堆个数比大顶堆多1;
  当insert的数字个数为偶数时,使大顶堆个数跟小顶堆个数一样。
3.那么当总数字个数为奇数时,中位数就是小顶堆堆头;
  当总数字个数为偶数时,中位数就是 2个堆堆头平均数
1
2
3
4
5
6
7

# 代码

在python heapq的基础上又稍微封装了一下堆的相关判断,这个题还是蛮繁杂的,后面有机会再补一下堆的相关知识吧。

# -*- coding:utf-8 -*-
import heapq

class Heap:
    # tp是一个标记,默认1为小根堆,type为-1则为大根堆,大根堆仍然用小根堆存储,但对数值*-1
    def __init__(self, tp=1):
        self.data = []
        self.tp = tp
        
    def __len__(self):
        return len(self.data)
    
    def insert(self, num):
        heapq.heappush(self.data, num * self.tp)
    
    def pop(self):
        return heapq.heappop(self.data) * self.tp
    
    def get_top(self):
        return heapq.nsmallest(1, self.data)[0] * self.tp

class Solution:
    def __init__(self):
        self.total = 0
        self.min_heap = Heap()
        self.max_heap = Heap(tp=-1)
    
    def Insert(self, num):
        self.total += 1
        if (self.total == 1) or (num >= self.min_heap.get_top()):
            self.min_heap.insert(num)
        else:
            self.max_heap.insert(num)
        while len(self.max_heap) > len(self.min_heap):
                self.min_heap.insert(self.max_heap.pop())
        while (len(self.min_heap) - len(self.max_heap)) > (self.total % 2):
                self.max_heap.insert(self.min_heap.pop())
        
    def GetMedian(self, wtf):
        if self.total == 0:
            return None
        if self.total % 2 == 0:
            return (self.min_heap.get_top() + self.max_heap.get_top()) / 2.0
        else:
            return self.min_heap.get_top()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
上次更新: 2022/11/11, 2:11:00
剑指Offer(13)
剑指Offer(15)

← 剑指Offer(13) 剑指Offer(15)→

Theme by Vdoing | Copyright © 2017-2023 anthony 京ICP备17072417-3
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式