剑指offer 树

二、树

需要知道中/前/后序的递归和遍历(借助栈),顺序遍历借助队列

层次遍历

1.初始化:一个队列queue q, 将root节点入队列q
2.如果队列不空,做如下操作:
3.弹出队列头,保存为node,将node的左右非空孩子加入队列
4.做2,3步骤,知道队列为空
不需要确定当前遍历到那一层的模板

void bfs() {
    vis[] = 0;
    queue<int> pq(start_val);
    while (!pq.empty()) {
        int cur = pq.front(); 
        pq.pop();
        for (遍历cur所有的相邻节点nex) {
            if (nex节点有效 && vis[nex]==0){
                vis[nex] = 1;
                pq.push(nex)
            }
        }
    }
}

如果需要确定遍历到哪一层:

void bfs() {
    int level = 0;
    vis[] = 0; // or set
    queue<int> pq(original_val);
    while (!pq.empty()) {
        int sz = pq.size();
        while (sz--) {
                int cur = pq.front(); pq.pop();
            for (遍历cur所有的相邻节点nex) {
                if (nex节点有效 && vis[nex] == 0) {
                    vis[nex] = 1;
                    pq.push(nex)
                }
            } // end for
        } // end inner while
        level++;
    } // end outer while
}

JZ55 二叉树的深度:

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度,根节点的深度视为 1 。

数据范围:节点的数量满足 0 \le n \le 1000≤n≤100 ,节点上的值满足 0 \le val \le 1000≤val≤100
进阶:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n)

假如输入的用例为{1,2,3,4,5,#,6,#,#,7},那么如下图:
剑指offer 树_第1张图片

1.递归取左右子树最大深度+1。
2.顺序遍历树,每遍历一层,count+1。

class Solution {
public:
    // 递归方法
    int Depth(TreeNode* node, int depth){
        if(node == nullptr){
            return 0;
        }
        int left = Depth(node->left, depth);
        int right = Depth(node->right, depth);
        return max(left, right) + 1;
    }
    int TreeDepth(TreeNode* pRoot) {
        if(pRoot == nullptr){
            return 0;
        }
        else{
            return Depth(pRoot, 0);
            // 顺序遍历方法
            queue<TreeNode*> q;
            q.push(pRoot);
            int level = 0;
            while(!q.empty()){
                int s = q.size();
                while(s--){
                    TreeNode* p = q.front();
                    q.pop();
                    if(p->left)
                        q.push(p->left);
                    if(p->right)
                        q.push(p->right);
                }
                level++;
            }
            return level;
        }
    }
};
class Solution:
    def TreeDepth(self , pRoot: TreeNode) -> int:
        def Depth(node, deep):
            if node == None:
                return deep
            left = Depth(node.left, deep)
            right = Depth(node.right, deep)
            return max(deep, left, right)+1
        if pRoot == None:
            return 0
        return Depth(pRoot, 0)```

JZ77 按之字形顺序打印二叉树:

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)

数据范围:0 \le n \le 15000≤n≤1500,树上每个节点的val满足 |val| <= 1500∣val∣<=1500
要求:空间复杂度:O(n)O(n),时间复杂度:O(n)O(n)
例如:
给定的二叉树是{1,2,3,#,#,4,5}
剑指offer 树_第2张图片
该二叉树之字形层序遍历的结果是[[1],[3,2],[4,5]]

每层顺序遍历,队列当前的长度是每层的个数,借助数组存取每层的元素。

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> result;
        if(!pRoot)
            return result;
        queue<TreeNode*> q;
        q.push(pRoot);
        int level = 0;
        while(!q.empty()){
            int size = q.size();
            vector<int> tmp;
            while(size--){
                TreeNode* p = q.front();
                tmp.push_back(p->val);
                q.pop();
                if(p->left)
                    q.push(p->left);
                if(p->right)
                    q.push(p->right);
            }
            if(level%2 == 0)
                result.push_back(tmp);
            else{
                reverse(tmp.begin(), tmp.end());
                result.push_back(tmp);
            }
            level++;
        }
        return result;
    }
};
class Solution:
    def Print(self , pRoot: TreeNode) -> List[List[int]]:
        if pRoot == None:
            return []
        tmp = []
        tmp.append(pRoot)
        level = 0
        result = []
        while len(tmp) != 0:
            size = len(tmp)
            tmp1 = []
            while size:
                a = tmp[0]
                tmp1.append(a.val)
                tmp.remove(tmp[0])
                if a.left:
                    tmp.append(a.left)
                if a.right:
                    tmp.append(a.right)
                size -= 1
            if level%2 == 0:
                result.append(tmp1)
            else:
                print(tmp1)
                result.append(tmp1[::-1])
            level += 1
        return result

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

给定一棵结点数为n 二叉搜索树,请找出其中的第 k 小的TreeNode结点值。
1.返回第k小的节点值即可
2.不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1
3.保证n个节点的值不一样

数据范围: 0 \le n \le10000≤n≤1000,0 \le k \le10000≤k≤1000,树上每个结点的值满足0 \le val \le 10000≤val≤1000
进阶:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

如输入{5,3,7,2,4,6,8},3时,二叉树{5,3,7,2,4,6,8}如下图所示
剑指offer 树_第3张图片
该二叉树所有节点按结点值升序排列后可得[2,3,4,5,6,7,8],所以第3个结点的结点值为4,故返回对应结点值为4的结点即可。

中序遍历取第k个值
1.中序递归,注意需在类中添加类变量以记录遍历的个数
2.中序非递归遍历,需借助栈(stack),先把节点的所有左子树放入栈中,取最后一个左子树开始遍历,每取一个结点类变量+1,不等于k则把右子树放入栈中,结点变为右子树,继续循环。

class Solution {
public:
    int KthNode(TreeNode* proot, int k) {
        // write code here
        stack<TreeNode*> q;
        if(proot==nullptr)
            return -1;
        q.push(proot);
        TreeNode* node = proot;
        int count = 0;
        while(!q.empty()){
            while(node->left){
                q.push(node->left);
                cout << node->left->val << '\n';
                node = node->left;
            }
            count++;
            cout << count << q.top()->val<<'\n';
            if(count == k){
                return q.top()->val;
            }
            TreeNode* p = q.top();
            q.pop();
            if(p->right){
                q.push(p->right);
                node = p->right;
            }
        }
        return -1;
    }
};
class Solution:
    def __init__(self):
        self.count = 0
        self.result = -1
    def KthNode(self , proot: TreeNode, k: int) -> int:
        def Kth(node, k):
            if node == None:
                return -1
            Kth(node.left, k)
            print(node.val, k, self.count)
            self.count += 1
            if self.count == k:
                self.result = node.val
            Kth(node.right, k)
        Kth(proot, k)
        return self.result

JZ7 重建二叉树

给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
剑指offer 树_第4张图片

递归的思想:前序的第一个为根节点,在中序中找到根节点,前部分是左子树,后部分是右子树;前序中从1到左子树长度+1是左子树,从左子树长度+1到最后是右子树,递归求左右子树。

C++使用数组的前后位置作为左右子树
剑指offer 树_第5张图片

class Solution {
public:
    TreeNode* dfs(vector<int> pre,vector<int> vin, int instart, int prestart, int inend){
        if(prestart > pre.size()-1 || instart > inend)
            return nullptr;
        TreeNode* root = new TreeNode(pre[prestart]);
        int index = 0;
        for(int i = instart; i <= inend; i++){
            if(vin[i] == root->val){
                index = i;
                break;
            }
        }
        root->left = dfs(pre, vin, instart, prestart+1, index-1);
        root->right = dfs(pre, vin, index+1, prestart+index-instart+1, inend);
        return root;
    }
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        return dfs(pre, vin, 0, 0, pre.size()-1);
    }
};
class Solution:
    def reConstructBinaryTree(self , pre: List[int], vin: List[int]) -> TreeNode:
        def re(pre, vin):
            if len(pre) == 0:
                return None
            root = TreeLinkNode(pre[0])
            vinroot = vin.index(pre[0])
            root.left = re(pre[1:vinroot+1], vin[:vinroot])
            root.right = re(pre[vinroot+1:], vin[vinroot+1:])
            return root
        return re(pre, vin)

JZ26 树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的子结构)
假如给定A为{8,8,7,9,2,#,#,#,#,4,7},B为{8,9,2},2个树的结构如下,可以看出B是A的子结构
剑指offer 树_第6张图片
先构造函数判断两个树是否相同,之后把A树从头遍历和B判断是否相同。

class Solution {
public:
    bool same(TreeNode* pRoot1, TreeNode* pRoot2){
        if(pRoot1 == nullptr & pRoot2 == nullptr)
            return true;
        else if(pRoot1 == nullptr)
            return false;
        else if(pRoot2 == nullptr)
            return true;
        if(pRoot1->val == pRoot2->val)
            return same(pRoot1->left, pRoot2->left) & same(pRoot1->right, pRoot2->right);
        else{
            return false;
        }
    }
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
        if(pRoot1 == nullptr)
            return false;
        else if(pRoot2 == nullptr)
            return false;
        else if(same(pRoot1, pRoot2))
            return true;
        else
            return HasSubtree(pRoot1->left, pRoot2) or HasSubtree(pRoot1->right, pRoot2);
    }
};
class Solution:
    def HasSubtree(self , pRoot1: TreeNode, pRoot2: TreeNode) -> bool:
        if pRoot1 == None:
            return False
        if pRoot2 == None:
            return False
        def subtree(pRoot1, pRoot2):
            if pRoot1 == None:
                return False
            if same(pRoot1, pRoot2):
                return True
            return s(pRoot1.left, pRoot2) or s(pRoot1.right, pRoot2)
        def same(pRoot1, pRoot2):
            if pRoot1 == None and pRoot2 == None:
                return True
            elif pRoot1 == None:
                return False
            elif pRoot2 == None:
                return True
            if pRoot1.val != pRoot2.val:
                return False
            else:
                return same(pRoot1.left, pRoot2.left) and same(pRoot1.right, pRoot2.right)
        return subtree(pRoot1, pRoot2)

JZ27 二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。
数据范围:二叉树的节点数 0 \le n \le 10000≤n≤1000 , 二叉树每个节点的值 0\le val \le 10000≤val≤1000
要求: 空间复杂度 O(n)O(n) 。本题也有原地操作,即空间复杂度 O(1)O(1) 的解法,时间复杂度 O(n)O(n)

比如:
源二叉树
剑指offer 树_第7张图片镜像二叉树
剑指offer 树_第8张图片
其实就是遍历一遍树,把每个结点的左右子树互换一下,可以递归遍历也可以用栈或队列遍历。
C++ 是用栈遍历

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * @param pRoot TreeNode类 
     * @return TreeNode类
     */
    TreeNode* Mirror(TreeNode* pRoot) {
        if(pRoot == nullptr)
            return pRoot;
        stack<TreeNode*> q;
        q.push(pRoot);
        while(!q.empty()){
            TreeNode* p = q.top();
            q.pop();
            if(p->left){
                q.push(p->left);
            }
            if(p->right){
                q.push(p->right);
            }
            TreeNode* tmp = p->left;
            p->left = p->right;
            p->right = tmp;
        }
        return pRoot;
        // write code here
    }
};

Python是用递归遍历

class Solution:
    def Mirror(self , pRoot: TreeNode) -> TreeNode:
        def mir(node):
            if node == None:
                return node
            p = node.left
            node.left = node.right
            node.right = p
            mir(node.left)
            mir(node.right)
        mir(pRoot)
        return pRoot

JZ32 从上往下打印二叉树

不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印。例如输入{8,6,10,#,#,2,1},如以下图中的示例二叉树,则依次打印8,6,10,2,1(空节点不打印,跳过),请你将打印的结果存放到一个数组里面,返回。剑指offer 树_第9张图片
就是借助队列顺序遍历树

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);
        vector<int> result;
        if(root==nullptr)
            return result;
        while(!q.empty()){
            TreeNode* p = q.front();
            q.pop();
            if(p->left)
                q.push(p->left);
            if(p->right)
                q.push(p->right);
            result.push_back(p->val);
        }
        return result;
    }
};
class Solution:
    def PrintFromTopToBottom(self , root: TreeNode) -> List[int]:
        if root == None:
            return []
        result = []
        tmp = []
        tmp.append(root)
        while len(tmp):
            q = tmp[0]
            tmp.remove(tmp[0])
            if q.left:
                tmp.append(q.left)
            if q.right:
                tmp.append(q.right)
            result.append(q.val)
        return result

JZ33 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回 true ,否则返回 false 。假设输入的数组的任意两个数字都互不相同。

数据范围: 节点数量 0≤n≤1000,节点上的值满足1≤val≤105,保证节点上的值各不相同
要求:空间复杂度 O(n),时间时间复杂度 O(n2)
提示:
1.二叉搜索树是指父亲节点大于左子树中的全部节点,但是小于右子树中的全部节点的树。
2.该题我们约定空树不是二叉搜索树
3.后序遍历是指按照 “左子树-右子树-根节点” 的顺序遍历
4.参考下面的二叉搜索树
剑指offer 树_第10张图片
1.递归:
列表最后一个元素为根,找到第一个大于根节点的值作为参考。前半部分为左子树,后半部分为右子树,递归判断左右子树是否正确。若左子树全小于根,右子树全大于根,返回true;若左右子树个数小于1,返回true。
2.借用栈:
(1) 实际上二叉树的中序遍历和后序遍历对应一种栈的压入和弹出序列,对后序遍历序列从小到大排列就得到中序遍历序列
(2)我们得到中序遍历后,将其作为入栈序列,检查后序遍历序列是不是一个合法的出栈序列即可
3.逆序后序遍历+辅助栈

C++:借助栈

class Solution {
public:
    bool verify(vector<int> inorder, vector<int> sequence){
        stack<int> stk;
        int n = inorder.size();
        int i = 0, j = 0;
        while(i < n){
            stk.push(inorder[i]);
            while(!stk.empty() && stk.top() == sequence[j]){
                j++;
                stk.pop();
            }
            i++;
        }
        if(j == n){
            return true;
        }
        return false;
    }
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.size()==0)
            return false;
        vector<int> inorder(sequence);
        sort(inorder.begin(), inorder.end());
        return verify(inorder, sequence);
    }
};

Python:递归

class Solution:
    def VerifySquenceOfBST(self , sequence: List[int]) -> bool:
        if len(sequence) == 0:
            return False
        def verift(seq):
            if len(seq) <= 1:
                return True
            root = seq[-1]
            index = len(seq)-1
            for i in range(len(seq)):
                if seq[i] > root:
                    index = i
                    break
            for i in range(index, len(seq)):
                if seq[i] < root:
                    return False
            return verift(seq[:index]) and verift(seq[index:len(seq)-1])
        return verift(sequence)

JZ82 二叉树中和为某一值的路径(一)

给定一个二叉树root和一个值 sum ,判断是否有从根节点到叶子节点的节点值之和等于 sum 的路径。
1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点
2.叶子节点是指没有子节点的节点
3.路径只能从父节点到子节点,不能从子节点到父节点
4.总节点数目为n
例如:
给出如下的二叉树,sum=22,剑指offer 树_第11张图片
返回true,因为存在一条路径 5→4→11→2的节点值之和为 22
数据范围:
1.树上的节点数满足 0≤n≤10000
2.每 个节点的值都满足∣val∣≤1000
要求:空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(树的高度),时间复杂度 O(n)

1.递归:sum+=根节点,判断当前sum是否相等,相等且为叶子节点则返回True,否则返回左结点或右节点的结果。
2.顺序遍历:用Pair类记录结点和对应curSum值,若curSum==sum且为叶子节点则返回true,否则取下一个结点。

C++:顺序遍历

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
    class Pair{
        public:
        TreeNode* node;
        int curSum;
        Pair(TreeNode* node, int curSum){
            this->node = node;
            this->curSum = curSum;
        }
    };
    bool hasPathSum(TreeNode* root, int sum) {
        if(root == nullptr)
            return false;
        queue<Pair*> q;
        Pair* rootpair = new Pair(root, root->val);
        q.push(rootpair);
        while(!q.empty()){
            Pair* p = q.front();
            q.pop();
            if(p->node->left){
                Pair* cur = new Pair(p->node->left, p->curSum+p->node->left->val);
                q.push(cur);
            }
            if(p->node->right){
                Pair* cur = new Pair(p->node->right, p->curSum+p->node->right->val);
                q.push(cur);
            }
            if(p->node->left == nullptr && p->node->right == nullptr && p->curSum == sum){
                return true;
            }
        }
        return false;
        // write code here
    }
};

Pytho:递归

class Solution:
    def hasPathSum(self , root: TreeNode, sum: int) -> bool:
        def path(root, su, sum):
            if root == None:
                return False
            su += root.val
            print(su)
            if su == sum and root.left == None and root.right == None:
                return True
            else:
                return path(root.left, su, sum) or path(root.right, su, sum)
        return path(root, 0, sum)

JZ34 二叉树中和为某一值的路径(二)

输入一颗二叉树的根节点root和一个整数expectNumber,找出二叉树中结点值的和为expectNumber的所有路径。
1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点
2.叶子节点是指没有子节点的节点
3.路径只能从父节点到子节点,不能从子节点到父节点
4.总节点数目为n

如二叉树root为{10,5,12,4,7},expectNumber为22
剑指offer 树_第12张图片
则合法路径有[[10,5,7],[10,12]]

1.递归:记录cursum和curlist,需要注意的是再左右都取完后需删除根节点(最后元素),需用全局数组记录结果。
2.顺序遍历获取结果。
C++:顺序遍历

class Solution {
public:
    vector<vector<int>> result;
    class Pair{
        public:
        TreeNode* node;
        int curSum;
        vector<int> curlist;
        Pair(TreeNode* node, int curSum, vector<int> curlist){
            this->node = node;
            this->curSum = curSum;
            this->curlist = curlist;
        }
    };
    vector<vector<int>> FindPath(TreeNode* root,int expectNumber) {
        if(root == nullptr)
            return result;
        vector<int> curlist;
        curlist.push_back(root->val);
        Pair* rootp = new Pair(root, root->val, curlist);
        queue<Pair*> q;
        q.push(rootp);
        while(!q.empty()){
            Pair* p = q.front();
            q.pop();
            cout << q.size() << p->curSum << endl;
            if(p->node->left==nullptr && p->node->right==nullptr && p->curSum==expectNumber){
                result.push_back(p->curlist);
            }
            else{
                if(p->node->left){
                    vector<int> tmp = p->curlist;
                    tmp.push_back(p->node->left->val);
                    Pair* cur = new Pair(p->node->left, p->curSum+p->node->left->val, tmp);
                    q.push(cur);
                }
                if(p->node->right){
                    vector<int> tmp = p->curlist;
                    tmp.push_back(p->node->right->val);
                    Pair* cur = new Pair(p->node->right, p->curSum+p->node->right->val, tmp);
                    q.push(cur);
                }
            }
            
        }
        return result;
    }
};

Python:递归

class Solution:
    def __init__(self):
        self.result = []
    def FindPath(self , root: TreeNode, target: int) -> List[List[int]]:
        def path(root, curlist, cursum):
            if root == None:
                return False
            cursum += root.val
            curlist.append(root.val)
            if cursum == target and root.left == None and root.right == None:
                self.result.append(curlist.copy())
            if root.left:
                path(root.left, curlist, cursum)
            if root.right:
                path(root.right, curlist, cursum)
            curlist.pop()
        path(root, [], 0)
        return self.result

JZ84 二叉树中和为某一值的路径(三)

给定一个二叉树root和一个整数值 sum ,求该树有多少路径的的节点值之和等于 sum 。
1.该题路径定义不需要从根节点开始,也不需要在叶子节点结束,但是一定是从父亲节点往下到孩子节点
2.总节点数目为n
3.保证最后返回的路径个数在整形范围内(即路径个数小于231-1)

数据范围:
0<=n<=10000<=n<=1000
-109 <=节点值<= 109

假如二叉树root为{1,2,3,4,5,4,3,#,#,-1},sum=6,那么总共如下所示,有3条路径符合要求
剑指offer 树_第13张图片
递归:先找从根节点的函数,然后从根节点开始递归遍历。

class Solution {
public:
    int result = 0;
    int Find(TreeNode* root, int cursum, int sum){
        if(root == nullptr){
            return 0;
        }
        cursum += root->val;
        if(cursum == sum){
            result++;
        }
        Find(root->left, cursum, sum);
        Find(root->right, cursum, sum);
        return 0;
    }
    int FindPath(TreeNode* root, int sum) {
        if(root == nullptr){
            return result;
        }
        Find(root, 0, sum);
        FindPath(root->left, sum);
        FindPath(root->right, sum);
        return result;
        // write code here
    }
};
class Solution:
    def __init__(self):
        self.result = 0
    def FindPath(self , root: TreeNode, sum: int) -> int:
        def path(root, cursum):
            if root == None:
                return False
            cursum += root.val
            if cursum == sum:
                self.result += 1
            path(root.left, cursum)
            path(root.right, cursum)
        def Find(node):
            if node == None:
                return False
            path(node, 0)
            Find(node.right)
            Find(node.left)
        Find(root)
        return self.result

JZ36 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示
剑指offer 树_第14张图片
数据范围:输入二叉树的节点数0≤n≤1000,二叉树中每个节点的值0≤val≤1000
要求:空间复杂度O(1)(即在原树上操作),时间复杂度 O(n)
注意:
1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继
2.返回链表中的第一个节点的指针
3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构
4.你不用输出双向链表,程序会根据你的返回值自动打印输出

1.中序遍历+数组,先中序遍历记录顺序,再把数组变成双向链表,但空间复杂度为n。
2.中序遍历优化:对于二叉树进行中序遍历,在遍历的同时调整结点之间的指针,使之成为双向链表
(1) 特殊情况,二叉树为空,则直接返回 null
(2) 创建 保留上一个结点 pre,返回链表结点 root
(3) 递归遍历左子树;root = pRootOfTree
(4) 遍历当前结点,并修改为双向链表 pRootOfTree.left=pre; pre.right=pRootOfTree;
(5) 更新 pre = pRootOfTree
(6) 递归遍历右子树
(7) 递归结束返回 root

class Solution {
public:
    TreeNode* pre;
    TreeNode* root;
    TreeNode* Convert(TreeNode* pRootOfTree) {
        if(pRootOfTree == nullptr)
            return pRootOfTree;
        Convert(pRootOfTree->left);
        if(root == nullptr)
            root = pRootOfTree;
        if(pre!=nullptr){
            pre->right = pRootOfTree;
            pRootOfTree->left = pre;
        }
        pre = pRootOfTree;
        Convert(pRootOfTree->right);
        return root;
    }
};
class Solution:
    def __init__(self):
        self.pre = None
        self.root = None
    def Convert(self , pRootOfTree ):
        if pRootOfTree == None:
            return pRootOfTree
        def inorder(pRoot):
            if pRoot == None:
                return None
            inorder(pRoot.left)
            if self.root == None:
                self.root = pRoot
            if self.pre:
                self.pre.right = pRoot
                pRoot.left = self.pre
            self.pre = pRoot
            inorder(pRoot.right)
        inorder(pRootOfTree)
        return self.root

JZ79 判断是不是平衡二叉树

输入一棵节点数为 n 二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
样例解释:剑指offer 树_第15张图片
样例二叉树如图,为一颗平衡二叉树
注:我们约定空树是平衡二叉树。

数据范围:0≤n≤100,树上节点的val值满足 0≤n≤1000
要求:空间复杂度O(1),时间复杂度 O(n)

递归:deep()函数计算深度,从头判断是否是平衡二叉树,返回结果。可在求深度的时候判断二叉树。

C++:过程比较简洁。

class Solution {
public:
    bool is =true;
    int Deepth(TreeNode* pRoot){
        if(pRoot == nullptr)
            return 0;
        int l = Deepth(pRoot->left);
        if(l== -1)
            return -1;
        int r = Deepth(pRoot->right);
        if(r==-1)
            return -1;
        if(abs(l-r)>1){
            is = false;
            return -1;
        }
        return max(l, r)+1;
    }
    bool IsBalanced_Solution(TreeNode* pRoot) {
        Deepth(pRoot);
        return is;
    }
};
class Solution:
    def IsBalanced_Solution(self , pRoot: TreeNode) -> bool:
        if pRoot == None:
            return True
        def deepth(root, depth):
            if root == None:
                return depth
            depth += 1
            return max(deepth(root.left, depth), deepth(root.right, depth))
        def isBalande(pRoot):
            if pRoot.left == None and pRoot.right == None:
                return True
            if pRoot.left and pRoot.right:
                if abs(deepth(pRoot.left, 0)-deepth(pRoot.right, 0)) < 2:
                    print(abs(deepth(pRoot.left, 0)-deepth(pRoot.right, 0)))
                    return isBalande(pRoot.left) and isBalande(pRoot.right)
                else:
                    return False
            elif pRoot.left and deepth(pRoot.left, 0) <2:
                return isBalande(pRoot.left)
            elif pRoot.right and deepth(pRoot.right, 0) <2:
                return isBalande(pRoot.right)
            else:
                return False
        return isBalande(pRoot)

JZ8 二叉树的下一个结点

给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针。下图为一棵有9个节点的二叉树。树中从父节点指向子节点的指针用实线表示,从子节点指向父节点的用虚线表示
剑指offer 树_第16张图片
示例:
输入:{8,6,10,5,7,9,11},8
返回:9
解析:这个组装传入的子树根节点,其实就是整颗树,中序遍历{5,6,7,8,9,10,11},根节点8的下一个节点就是9,应该返回{9,10,11},后台只打印子树的下一个节点,所以只会打印9,如下图,其实都有指向左右孩子的指针,还有指向父节点的指针,下图没有画出来剑指offer 树_第17张图片
数据范围:节点数满足 0 \1≤n≤50 ,节点上的值满足 1≤val≤100

要求:空间复杂度 O(1) ,时间复杂度 O(n)
剑指offer 树_第18张图片

解:仔细观察,可以把中序(DBHEIAFCG)下一结点归为几种类型:
1、有右子树,下一结点是右子树中的最左结点,例如 B,下一结点是 H
2、无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点,例如 H,下一结点是 E
3、无右子树,且结点是该结点父结点的右子树,则一直沿着父结点追朔,直到找到某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点。例如 I,下一结点是 A;例如 G,并没有符合情况的结点,所以 G 没有下一结点

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        if(pNode == nullptr)
            return nullptr;
        if(pNode->right){
            TreeLinkNode* p = pNode->right;
            while(p->left){
                p = p->left;
            }
            return p;
        }
        while(pNode->next){
            if(pNode->next->left == pNode)
                return pNode->next;
            pNode = pNode->next;
        }
        return nullptr;
    }
};
class Solution:
    def GetNext(self, pNode):
        if pNode == None:
            return None
        if pNode.right:
            p = pNode.right
            while p.left:
                p = p.left
            return p
        while pNode.next:
            if pNode.next.left == pNode:
                return pNode.next
            pNode = pNode.next
        return None

JZ28 对称的二叉树

给定一棵二叉树,判断其是否是自身的镜像(即:是否对称)
例如: 下面这棵二叉树是对称的
剑指offer 树_第19张图片
下面这棵二叉树不对称。
剑指offer 树_第20张图片
数据范围:节点数满足 0≤n≤1000,节点上的值满足 ∣val∣≤1000
要求:空间复杂度 O(n),时间复杂度 O(n)
备注:
你可以用递归和迭代两种方法解决这个问题

1.递归:以子树为单位进行对称检查。满足对称二叉树需满足,左结点==右结点,左结点的左结点=右结点的右结点,左结点的右结点=右结点的左结点。给定两个结点判断是否是对称。
2.顺序遍历,以层为单位检查,看每一层是否对称,注:空结点也需要记录,否则判断错误。

C++:顺序遍历

class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot) {
        if(pRoot== nullptr)
            return true;
        queue<TreeNode*> q;
        q.push(pRoot);
        int INF = 0x3f3f3f3f;
        TreeNode* empty = new TreeNode(INF);
        while(!q.empty()){
            int size = q.size();
            vector<int> a;
            while(size--){
                TreeNode* p = q.front();
                q.pop();
                a.push_back(p->val);
                if(p!=empty){
                    if(p->left)
                        q.push(p->left);
                    else
                        q.push(empty);
                    if(p->right)
                        q.push(p->right);
                    else
                        q.push(empty);
                }
            }
            for(int i = 0,j = a.size()-1; i<(a.size()/2+1); i++,j--){
                cout << a[i] << ' ' << a[j] << ' ';
                if(a[i] != a[j])
                    return false;
            }
            cout << endl;
        }
        return true;
    }
};

Python:递归

class Solution:
    def isSymmetrical(self , pRoot: TreeNode) -> bool:
        if pRoot == None:
            return True
        def issame(root1, root2):
            if root1 == None and root2 == None:
                return True
            if root1 == None or root2 == None:
                return False
            return root1.val == root2.val and issame(root1.left, root2.right) and issame(root2.left, root1.right)
        return issame(pRoot, pRoot)

JZ78 把二叉树打印成多行

给定一个节点数为 n 二叉树,要求从上到下按层打印二叉树的 val 值,同一层结点从左至右输出,每一层输出一行,将输出的结果存放到一个二维数组中返回。
例如:
给定的二叉树是{1,2,3,#,#,4,5}
剑指offer 树_第21张图片
该二叉树多行打印层序遍历的结果是[[1],[2,3],[4,5]]
数据范围:二叉树的节点数 0≤n≤1000,0≤val≤1000
要求:空间复杂度 O(n),时间复杂度 O(n)

顺序遍历

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> result;
        if(pRoot==nullptr)
            return result;
        queue<TreeNode*> q;
        q.push(pRoot);
        while(!q.empty()){
            int size = q.size();
            vector<int> tmp;
            while(size--){
                TreeNode* p = q.front();
                q.pop();
                tmp.push_back(p->val);
                if(p->left)
                    q.push(p->left);
                if(p->right)
                    q.push(p->right);
            }
            result.push_back(tmp);
        }
        return result;
    }
};
class Solution:
    def Print(self , pRoot: TreeNode) -> List[List[int]]:
        if pRoot == None:
            return []
        result = []
        queue = []
        queue.append(pRoot)
        while(len(queue)):
            size = len(queue)
            tmp = []
            while(size):
                p = queue[0]
                queue.remove(queue[0])
                tmp.append(p.val)
                if p.left:
                    queue.append(p.left)
                if p.right:
                    queue.append(p.right)
                size -= 1
            result.append(tmp)
        return result

JZ68 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
1.对于该题的最近的公共祖先定义:对于有根树T的两个节点p、q,最近公共祖先LCA(T,p,q)表示一个节点x,满足x是p和q的祖先且x的深度尽可能大。在这里,一个节点也可以是它自己的祖先.
2.二叉搜索树是若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值
3.所有节点的值都是唯一的。
4.p、q 为不同节点且均存在于给定的二叉搜索树中。
数据范围:3<=节点总数<=10000;0<=节点值<=10000
如果给定以下搜索二叉树: {7,1,12,0,4,11,14,#,#,3,5},如下图:剑指offer 树_第22张图片
二叉搜索树的一个特征左子树<根<右子树,若根的值在p,q之间返回根的值,若p,q小于根递归到左子树上搜索,p,q大于根在右子树上搜索。若根和p,q某个值相等返回根的值。

class Solution {
public:
    int lowestCommonAncestor(TreeNode* root, int p, int q) {
        if(p>q){
            int tmp = p;
            p = q;
            q = tmp;
        }
        if(p == root->val || q == root->val)
            return root->val;
        if(p < root->val && root->val < q)
            return root->val;
        else if(q < root->val)
            return lowestCommonAncestor(root->left, p, q);
        else
            return lowestCommonAncestor(root->right, p, q)
        // write code here
    }
};
class Solution:
    def lowestCommonAncestor(self , root: TreeNode, p: int, q: int) -> int:
        if p > q:
            tmp = p
            p = q
            q = tmp
        def lowe(root, p, q):
            if p == root.val or q == root.val:
                return root.val
            if p < root.val and q > root.val:
                return root.val
            elif q < root.val:
                return lowe(root.left, p, q)
            else:
                return lowe(root.right, p, q)
        return lowe(root, p, q)

JZ86 在二叉树中找到两个节点的最近公共祖先

给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
数据范围:树上节点数满足 1≤n≤105 , 节点值val满足区间 [0,n)
要求:时间复杂度 O(n)
注:本题保证二叉树中每个节点的val值均不相同。
如当输入{3,5,1,6,2,0,8,#,#,7,4},5,1时,二叉树{3,5,1,6,2,0,8,#,#,7,4}如下图所示:
剑指offer 树_第23张图片
所以节点值为5和节点值为1的节点的最近公共祖先节点的节点值为3,所以对应的输出为3。
节点本身可以视为自己的祖先

1.递归:对于节点o1,o2只有三种情况,o1,o2分别位于左右子树;o1 = root, 且 o2 位于root的左子树/右子树中;o2 = root, 且 o1 位于root的左子树/右子树中。若root为空或等于o1或o2返回root,再看是否在左右子树上,若右子树返回空说明在左子树上,返回左子树的值,左子树为空同理。若左右子树都不为空则说明一左一右返回根。
2.顺序遍历:(1)顺序遍历用map记录结点及其父结点直到找到o1和o2.(2)得到o1的所有父结点(3)找o2的父结点看是否在o1的父结点中,在就返回该结点。

C++:顺序遍历

// 注意map和set的用法
class Solution {
public:
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        //记录遍历到的每个节点的父节点。
        map<int, int> parent;
        queue<TreeNode*> q;
        parent.insert(pair<int, int>(root->val, 0x3f3f3f3f)); //根节点没有父节点,给他默认一个值
        q.push(root);
        //直到两个节点都找到为止。
        while(parent.find(o1)==parent.end() || parent.find(o2)==parent.end()){
            TreeNode* p = q.front();
            q.pop();
            if(p->left){
                //左子节点不为空,记录下他的父节点
                parent.insert(pair<int, int>(p->left->val, p->val));
                q.push(p->left);
            }
            if(p->right){
                 parent.insert(pair<int, int>(p->right->val, p->val));
                q.push(p->right);
            }
        }
        //记录下o1和他的祖先节点,从o1节点开始一直到根节点。
        set<int> o1_parent;
        while(parent.find(o1) != parent.end()){
            o1_parent.insert(o1);
            o1 = parent.find(o1)->second;
        }
        //查看o1和他的祖先节点是否包含o2节点,如果不包含再看是否包含o2的父节点
        while(o1_parent.find(o2) == o1_parent.end()){
            o2 = parent.find(o2)->second;
        }
        return o2;
    }
};

Python:递归

class Solution:
    def lowestCommonAncestor(self , root: TreeNode, o1: int, o2: int) -> int:
        def lowe(root, o1, o2):
            if root == None or root.val == o1 or root.val == o2:
                return root.val
            # 如果left为空,说明这两个节点在root结点的右子树上,我们只需要返回右子树查找的结果即可
            left = lowe(root.left, o1, o2)
            right = lowe(root.right, o1, o2)
            if left==None:
                return right
            if right==None:
                return left
            # 如果left和right都不为空,说明这两个节点一个在root的左子树上一个在root的右子树上,
            # 我们只需要返回cur结点即可。
            return root.val
        return lowe(root, o1, o2)

JZ37 序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。
二叉树的序列化(Serialize)是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树等遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#)
二叉树的反序列化(Deserialize)是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
例如,可以根据层序遍历的方案序列化,如下图:剑指offer 树_第24张图片
层序序列化(即用函数Serialize转化)如上的二叉树转为"{1,2,3,#,#,6,7}",再能够调用反序列化(Deserialize)将"{1,2,3,#,#,6,7}"构造成如上的二叉树。
当然你也可以根据满二叉树结点位置的标号规律来序列化,还可以根据先序遍历和中序遍历的结果来序列化。不对序列化之后的字符串进行约束,所以欢迎各种奇思妙想。
数据范围:节点数n≤100,树上每个节点的值满足0≤val≤150
要求:序列化和反序列化都是空间复杂度 O(n),时间复杂度 O(n)
层序遍历:序列化就是记录每层的打印,空结点为#。反序列化是把顺序遍历变回树,第一个值为根结点,字符串当前位置的下两个值为左右子结点,把左右结点放入队列中,字符串位置后移两位是下个队列结点的左右结点,循环。
先序遍历:借用递归,用先序遍历将二叉树结构序列化为一个字符串,空节点用 # 来表示。反序列化时用 “队列” 来存储所有节点(根据先序遍历顺序存储),判断当前出队元素是否为 # ,如果是则为空节点,如果不是则新建树节点,并依次处理该节点的左子树和右子树。
后序遍历:用后续遍历将二叉树结构序列化为一个字符串,空节点用 # 来表示。栈的出队顺序为 先进后出,即在序列化时先添加的左子树、右子树和根节点,在经过栈存储数据后,就会变成跟前序遍历使用队列一样的构建顺序。反序列化时用 “栈” 来存储所有节点(根据后序遍历顺序存储),判断当前出队元素是否为 # ,如果是则为空节点,如果不是则新建树节点,并依次处理该节点的左子树和右子树。

C++:先序遍历

class Solution {
public:
    void inorder(TreeNode* root, string& ret){
        if(root==nullptr){
            ret += '#';
            return;
        }
        ret += to_string(root->val) + '!';
        inorder(root->left, ret);
        inorder(root->right, ret);
    }
    char* Serialize(TreeNode *root) {   
        string ret;
        inorder(root, ret);
        char* ans = new char[ret.size()+1];
        strcpy(ans, ret.c_str());
        cout << ret << endl;
        return ans;
    }
    TreeNode* des(char* str, int& p){
        if(str[p] == '#'){
            p++;
            return nullptr;
        }
        int val = 0;
        while(str[p] != '!'){
            val = val*10 + str[p] -'0';
            p++;
        }
        p++;
        TreeNode* root = new TreeNode(val);
        root->left = des(str, p);
        root->right = des(str, p);
        return root;
    }
    TreeNode* Deserialize(char *str) {
        int p = 0;
        return des(str, p);
    }
};

Python:层序遍历

class Solution:
    def Serialize(self, root):
        if root == None:
            return ''
        queue = []
        queue.append(root)
        str_queue = ''
        while len(queue):
            p = queue[0]
            queue.remove(queue[0])
            if p == None:
                str_queue += '#,'
                continue
            str_queue += str(p.val) + ','
            queue.append(p.left)
            queue.append(p.right)
        print(str_queue)
        return str_queue
        # write code here
    def Deserialize(self, s):
        if s == '':
            return None
        s = s.split(',')[:-1]
        root = TreeNode(int(s[0]))
        queue = []
        queue.append(root)
        k = 1
        while k < len(s):
            p = queue[0]
            queue.remove(p)
            if p == None:
                continue
            if s[k] == '#':
                p.left = None
            else:
                p.left = TreeNode(int(s[k]))
            if s[k+1] == '#':
                p.right = None
            else:
                p.right = TreeNode(int(s[k+1]))
            queue.append(p.left)
            queue.append(p.right)
            k += 2
        return root

你可能感兴趣的:(python,c++)