5,二叉树【p6-p7】

二叉树

  • 5.1二叉树
    • 5.1.1例1:用递归和非递归两种方式实现二叉树的先序、中序、后序遍历
      • 5.1.1.1递归序的先序、中序、后序遍历
        • 先序遍历:
        • 中序遍历:
        • 后序遍历:
      • 5.1.1.2非递归序的先序、中序、后序遍历
        • 先序遍历:
        • 中序遍历:
        • 后序遍历:
    • 5.1.2例2:如何直观的打印一颗二叉树
    • 5.1.3例3:如何完成二叉树的宽度优先遍历
      • 5.1.3.1求一颗二叉树的宽度
        • 5.1.3.1.1方法1队列哈希表方法
        • 5.1.3.1.2方法2队列方法
    • 5.1.4例4:二叉树的相关概念及其实现判断
      • 5.1.4.1判断一颗二叉树是否是搜索二叉树(BST)?
      • 5.1.4.2判断二叉树是否是完全二叉树(CBT)?
      • 5.1.4.3判断一颗二叉树是否是满二叉树?
      • 5.1.4.4判断一棵二叉树是否是平衡二叉树?
      • ※5.1.4.5判断是搜索二叉树、满二叉树、平衡二叉树的递归套路(可以解决一切树形DP问题)
        • ※判断是否是平衡二叉树?
        • ※判断是否是搜索二叉树?
        • ※判断是否是满二叉树?
    • 5.1.5例5:最低公共祖先节点
      • 5.1.5.1方法1
      • 5.1.5.2方法2
    • 5.1.6例6:在二叉树中找到一个节点的后继节点
    • 5.1.7例7:二叉树的序列化和反序列化
    • 5.1.8例8:折纸问题

※5.1.4.4.1

5.1二叉树

class Node<V>{
V value;
Node left;
Node right;
}

用递归和非递归两种方式实现二叉树的先序、中序、后序遍历
如何直观的打印一颗二叉树
如何完成二叉树的宽度优先遍历(常见题目:求一颗二叉树的宽度)

5.1.1例1:用递归和非递归两种方式实现二叉树的先序、中序、后序遍历

递归序是通过递归方法生成的序列,而非递归序则是通过循环和栈的辅助实现的序列。两者在序列化顺序上是一致的,都是按照 “根结点、左子树、右子树” 的顺序遍历二叉树。

5.1.1.1递归序的先序、中序、后序遍历

5,二叉树【p6-p7】_第1张图片

先序遍历:

头左右
对于所有子树来说,先打印头节点,再打印左子树上的所有节点,再打印右树上的所有节点(对于每一个子树都是先打印左,后打印右)
1,2,4,5,3,6,7
第一次碰到的就打印,不是就不打印
5,二叉树【p6-p7】_第2张图片

中序遍历:

左头右
先打印左树,再打印头,再打印右树
4,2,5,1,6,3,7
利用递归序,第二次再打印,不是第二次什么也不做

后序遍历:

左右头
先打印左树,再打印右树,再打印头
4,5,2,6,7,3,1
利用递归序,第三次再打印,不是第三次什么也不做

#include 

using namespace std;

// 定义二叉树的结点
struct Node {
    int data;
    Node* left;
    Node* right;
};

// 创建一个新的二叉树结点
Node* createNode(int data) {
    Node* newNode = new Node();
    if (newNode == nullptr) {
        cout << "内存分配失败!" << endl;
        return nullptr;
    }
    newNode->data = data;
    newNode->left = nullptr;
    newNode->right = nullptr;
    return newNode;
}

// 先序遍历
void preorderTraversal(Node* root) {
    if (root == nullptr)
        return;
    cout << root->data << " ";
    preorderTraversal(root->left);
    preorderTraversal(root->right);
}

// 中序遍历
void inorderTraversal(Node* root) {
    if (root == nullptr)
        return;
    inorderTraversal(root->left);
    cout << root->data << " ";
    inorderTraversal(root->right);
}

// 后序遍历
void postorderTraversal(Node* root) {
    if (root == nullptr)
        return;
    postorderTraversal(root->left);
    postorderTraversal(root->right);
    cout << root->data << " ";
}

int main() {
    // 创建二叉树
    Node* root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    root->right->left = createNode(6);
    root->right->right = createNode(7);

    // 输出先序遍历
    cout << "先序遍历结果:" << endl;
    preorderTraversal(root);

    cout << endl;

    // 输出中序遍历
    cout << "中序遍历结果:" << endl;
    inorderTraversal(root);

    cout << endl;

    // 输出后序遍历
    cout << "后序遍历结果:" << endl;
    postorderTraversal(root);

    cout << endl;

    return 0;
}

5.1.1.2非递归序的先序、中序、后序遍历

先序遍历:

头左右
1,2,4,5,3,6,7
每次把头节点放入栈里,从栈中弹出一个节点cur,打印(处理)cur,先右边放入栈中,再左边放入栈中,从头(从栈中弹出一个节点cur)开始周而复始
先放头1节点,弹出1打印
放入右节点3,放入左节点2,弹出2打印
先放入5节点,再放入4节点,弹出4打印,
先放右再放左,都没有所以什么也不做,弹出5打印
先放右再放左,都没有所以什么也不做,弹出3打印
先放右节点7,再放左节点6,弹出6打印
先放右再放左,都没有所以什么也不做,弹出7打印
5,二叉树【p6-p7】_第3张图片

中序遍历:

左头右
每棵树左边界进栈,依次弹出节点的过程中打印,对弹出节点的右树循环周而复始
1,2,4进栈
弹出4打印4,没有右树
弹出2,打印2,有右树5,压栈5
弹出5,打印5,没有右树
弹出1,打印1,有右树,压栈3,6
弹出6,打印6,没有右树
弹出3,打印3,有右树,压栈7
弹出7,打印7
4,2,5,1,6,3,7

放入栈的顺序是从头到左,弹出栈的顺序是左头,之后取右树再次先左再头……
5,二叉树【p6-p7】_第4张图片

5,二叉树【p6-p7】_第5张图片

后序遍历:

左右头
放入到栈中是头右左,
从收栈弹出反转变成了左右头
4,5,2,6,7,3,1
5,二叉树【p6-p7】_第6张图片

弹,cur
cur放入收栈
先左再右
循环

先压栈头1节点,弹出1节点压栈到收栈
压栈2,压栈3,弹出3,3压栈到收栈
压栈6,压栈7,弹出7,7压栈到收栈
压栈左右为空,弹出6,6压栈到收栈
压栈左右为空,弹出2,2压栈到收栈
压栈4,压栈5,弹出5,5压栈到收栈
压栈左右为空,弹出4,4压栈到收栈

弹出收栈4,5,2,6,7,3,1
5,二叉树【p6-p7】_第7张图片

#include 
#include 

using namespace std;

// 定义二叉树的结点
struct Node {
    int data;
    Node* left;
    Node* right;
};

// 创建一个新的二叉树结点
Node* createNode(int data) {
    Node* newNode = new Node();
    if (newNode == nullptr) {
        cout << "内存分配失败!" << endl;
        return nullptr;
    }
    newNode->data = data;
    newNode->left = nullptr;
    newNode->right = nullptr;
    return newNode;
}

// 非递归先序遍历
void iterativePreorderTraversal(Node* root) {
    if (root == nullptr)
        return;
    stack<Node*> st;
    st.push(root);
    while (!st.empty()) {
        Node* curr = st.top();
        st.pop();
        cout << curr->data << " ";
        if (curr->right)
            st.push(curr->right);
        if (curr->left)
            st.push(curr->left);
    }
}

// 非递归中序遍历
void iterativeInorderTraversal(Node* root) {
    if (root == nullptr)
        return;
    stack<Node*> st;
    Node* curr = root;
    while (curr != nullptr || !st.empty()) {
        while (curr != nullptr) {
            st.push(curr);
            curr = curr->left;
        }
        curr = st.top();
        st.pop();
        cout << curr->data << " ";
        curr = curr->right;
    }
}

// 非递归后序遍历
void iterativePostorderTraversal(Node* root) {
    if (root == nullptr)
        return;
    stack<Node*> st1, st2;
    st1.push(root);
    while (!st1.empty()) {
        Node* curr = st1.top();
        st1.pop();
        st2.push(curr);
        if (curr->left)
            st1.push(curr->left);
        if (curr->right)
            st1.push(curr->right);
    }
    while (!st2.empty()) {
        Node* curr = st2.top();
        st2.pop();
        cout << curr->data << " ";
    }
}

int main() {
    // 创建二叉树
    Node* root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    root->right->left = createNode(6);
    root->right->right = createNode(7);

    // 输出非递归先序遍历
    cout << "非递归先序遍历结果:" << endl;
    iterativePreorderTraversal(root);

    cout << endl;

    // 输出非递归中序遍历
    cout << "非递归中序遍历结果:" << endl;
    iterativeInorderTraversal(root);

    cout << endl;

    // 输出非递归后序遍历
    cout << "非递归后序遍历结果:" << endl;
    iterativePostorderTraversal(root);

    cout << endl;

    return 0;
}


5.1.2例2:如何直观的打印一颗二叉树

5,二叉树【p6-p7】_第8张图片
5,二叉树【p6-p7】_第9张图片
如第三个树
5,二叉树【p6-p7】_第10张图片
5,二叉树【p6-p7】_第11张图片

5.1.3例3:如何完成二叉树的宽度优先遍历

先序遍历就是深度优先遍历

宽度遍历用队列(广度优先遍历),头进尾出(先进先出)弹出就打印

广度优先遍历代码

#include 
#include 

using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 宽度优先搜索遍历函数
void BFS(TreeNode* root) {
    if (root == nullptr) return;

    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        
        cout << node->val << " ";  // 打印出队节点的值

        if (node->left) {
            q.push(node->left);
        }
        if (node->right) {
            q.push(node->right);
        }
    }
}

int main() {
    // 构建二叉树
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    root->right->left = new TreeNode(6);
    root->right->right = new TreeNode(7);

    cout << "BFS traversal: ";
    BFS(root);

    return 0;
}

深度优先遍历代码

void DFS(TreeNode* node) {
    if (node == nullptr)
        return;

    cout << node->val << " ";  // 打印当前节点的值

    DFS(node->left);  // 递归遍历左子树
    DFS(node->right);  // 递归遍历右子树
}

5,二叉树【p6-p7】_第12张图片

5.1.3.1求一颗二叉树的宽度

5.1.3.1.1方法1队列哈希表方法

需要知道哪一层的节点个数,准备一张表,记录任何一个点在第几层

当前在哪一层 curLevel
当前层发现几个节点 curLevelNodes
所有层中那一层节点数最多 max
5,二叉树【p6-p7】_第13张图片
5,二叉树【p6-p7】_第14张图片

5.1.3.1.2方法2队列方法

NodeCurend=1
Nodenextend=null
int Curlevel=0

让1节点进队列
弹出1节点先让左孩子进栈,看Nodenextend是否为空,如果为空,把Nodenextend设置为进栈的节点2
再让右孩子3进栈,Nodenextend=3,int Curlevel=1
当前节点是不是当前层最后一个节点,是max=1,让NodeCurend拷贝Nodenextend,NodeCurend=3,使Nodenextend=null,让int Curlevel=0

弹出2节点,让2节点的左孩子进栈,再让右孩子4进栈,但是左孩子是nullptr,,所以4进栈,int Curlevel=1
弹出3节点,让3节点的左孩子5进栈,再让右孩子6进栈,Nodenextend=6(当前层谁最后进栈谁是Nodenextend),int Curlevel=2,当前层后面不会进了max=2,NodeCurend拷贝Nodenextend,NodeCurend=6,使Nodenextend=null,让int Curlevel=0

弹出4节点,让4节点的左孩子进栈,再让右孩子进栈,但是左右孩子都是nullptr,int Curlevel=1
弹出5节点,让5节点的左孩子7进栈,再让右孩子进栈,但是右孩子是nullptr,Nodenextend=7,int Curlevel=2
弹出6节点,让6节点的左孩子进栈,再让右孩子8进栈,但是左孩子是nullptr,Nodenextend=8,int Curlevel=3,6节点为本层的结束max=3,NodeCurend拷贝Nodenextend,NodeCurend=8,使Nodenextend=null,让int Curlevel=0

……
5,二叉树【p6-p7】_第15张图片

5.1.4例4:二叉树的相关概念及其实现判断

5.1.4.1判断一颗二叉树是否是搜索二叉树(BST)?

搜索二叉树(Binary Search Tree,简称BST)是一种具有特殊性质的二叉树。它满足以下定义:

每个节点都包含一个键(key)和一个相关联的值(value)。
对于任意节点,其左子树中的所有键的值都小于该节点的键的值。
对于任意节点,其右子树中的所有键的值都大于该节点的键的值。
左子树和右子树都是搜索二叉树。
也就是说,对于搜索二叉树中的任意节点,其左子树中的所有节点的键都小于该节点的键,而右子树中的所有节点的键都大于该节点的键。这个特点使得搜索二叉树有很好的查找和排序性能,在许多应用中被广泛使用。
5,二叉树【p6-p7】_第16张图片

中序遍历

使用中序遍历,如果输出结果一直在升序,那它一定是搜索二叉树

#include 
int preValue = std::numeric_limits<int>::min();

bool checkBST(Node* head)
{
    if (head == nullptr)
    {
        return true;
    }
    bool isLeftBST = checkBST(head->left);
    if (!isLeftBST)//发现左树不是搜索二叉树就直接返回false
    {
        return false;
    }
    if (head->data <= preValue)//和上次搜索到值相比较,小于等于就返回false
    {
        return false;
    }
    else//大于就使preValue等于当前值,下次循环就可以判断了
    {
        preValue = head->data;
    }
    return checkBST(head->right);
}

5,二叉树【p6-p7】_第17张图片

次一点的方法

5,二叉树【p6-p7】_第18张图片

非递归方式

5,二叉树【p6-p7】_第19张图片

5.1.4.2判断二叉树是否是完全二叉树(CBT)?

完全二叉树(Complete Binary Tree)是一种特殊的二叉树结构(除了最后一层都是满的,最后一层即便不满,也是从左到右依次满的)

(1)任一节点,一旦有右无左直接输出false
(2)在(1)不违规条件下,如果遇到左右两个孩子不双全的情况,接下来遇到的所有节点都必须是叶节点
5,二叉树【p6-p7】_第20张图片

5,二叉树【p6-p7】_第21张图片
5,二叉树【p6-p7】_第22张图片

5.1.4.3判断一颗二叉树是否是满二叉树?

麻烦做法:
先求二叉树最大深度L,再求二叉树节点个数N
最大深度L和节点个数N满足:N=2L-1
如果满足此关系必定是满二叉树,如果是满二叉树必定满足此关系

5.1.4.4判断一棵二叉树是否是平衡二叉树?

平衡二叉树:对于任一子树来说,左树和右树的高度差不超过1

※5.1.4.5判断是搜索二叉树、满二叉树、平衡二叉树的递归套路(可以解决一切树形DP问题)

树形DP问题是面试题内最难的二叉树类型的问题了
都是基于怎么向左树要信息,怎么向右树要信息

※判断是否是平衡二叉树?

左树需要返回是否平衡,高度是多少,右树也相同

#include
class ReturnType
{
public:
    bool isBalanced;
    int height;
    ReturnType(bool isB, int hei)//构造函数
    {
        isBalanced = isB;
        height = hei;
    }
};

ReturnType* process(Node* x)
{
    if (!x) return new ReturnType(true, 0);//x为空的时候返回哪两个值(是否是平衡树true,高度0)
    ReturnType leftData = *process(x->left);
    ReturnType rightData = *process(x->right);
    int height = max(leftData.height, rightData.height) + 1;//左树和右树高度较大的一个加1
    bool isBalanced = leftData.isBalanced && rightData.isBalanced && abs(leftData.height - rightData.height) < 2;
    //左树是平衡树,右树是平衡树,左树和右树的差的绝对值小于2
    
    ReturnType* ans = new ReturnType(isBalanced, height);//最后返回的值(以x为头的是否是平衡二叉树,高度)
    return ans;
}

bool isBalanced(Node* head)
{
    return process(head)->isBalanced;
}

5,二叉树【p6-p7】_第23张图片

※判断是否是搜索二叉树?

需要左树是搜索二叉树,左树最大值max
需要右树是搜索二叉树,右树最小值min
左树最大值max < x(头节点)
右树最小值min > x(头节点)
现在需求不一样,但是必须每一个节点的需求是一样的才是递归

//是否是搜索二叉树?
class ReturnData
{
public:
    bool isBST;//根据需求要三个信息
    int min;
    int max;
    ReturnData(bool is,int mi,int ma)//构造函数
    {
        isBST = is;
        min = mi;
        max = ma;
    }
};
ReturnData* process(Node* x)
{
    if (!x)return nullptr;
    ReturnData* leftData = process(x->left);//默认左树给我一个信息
    ReturnData* rightData = process(x->right);//默认右树给我一个信息
    int Min = x->data;
    int Max = x->data;
    if (leftData!=nullptr)//如果左边得到的信息不为空,则有东西
    {
        Min = std::min(Min, leftData->min);//如果左树不为空,则左树值和x(或当前值)比大小,取最小值,找到左树最小值
        Max = std::max(Max, leftData->max);//如果左树不为空,则左树值和x(或当前值)比大小,取最大值,找到左树最大值
    }
    if (rightData != nullptr)
    {
        Min = std::min(Min, rightData->min);//如果右树不为空,则左树值和x(或当前值)比大小,取最小值,找到右树最小值
        Max = std::max(Max, rightData->max);//如果右树不为空,则左树值和x(或当前值)比大小,取最大值,找到右树最大值
    }
    bool isBST = true;//整棵树书否是搜索二叉树,默认是
    //左边不是搜索二叉树了,返回false。左边最大值大于x了,返回false
    if (leftData != nullptr && (!leftData->isBST || leftData->max >= x->data))
    {
        isBST = false;
    }
    //右边不是搜索二叉树了,返回false。右边最小值小于x了,返回false
    if (rightData != nullptr && (!rightData->isBST || x->data >= rightData->min))
    {
        isBST = false;
    }
    return new ReturnData(isBST, Min, Max);//需求给了三个信息,返回三个值
}
bool isBST(Node* x)
{
    return process(x)->isBST;
}

5,二叉树【p6-p7】_第24张图片
5,二叉树【p6-p7】_第25张图片

※判断是否是满二叉树?

需知整棵树高度和节点个数

//是否是满二叉树?
class Info
{
public:
    int height;
    int nodes;
    Info(int h,int n)
    {
        height = h;
        nodes = n;
    }
};
 Info* process(Node* x)
{
     if (!x)return new Info(0, 0);
     Info* leftData = process(x->left);
     Info* rightData = process(x->right);
     int height = max(leftData->height, rightData->height) + 1;
     int nodes = leftData->nodes + rightData->nodes + 1;
     return new Info(height, nodes);
};
 bool isF(Node* head)
 {
     if (!head)return true;
     Info* data = process(head);
     return data->nodes == (1 << data->height - 1);//相当于2^L-1
 }

5,二叉树【p6-p7】_第26张图片

5.1.5例5:最低公共祖先节点

给定两个二叉树的节点node1和node2找到他们的最低公共祖先节点
5,二叉树【p6-p7】_第27张图片
往上走,哪一个点是最初汇聚的点,第一个汇聚的点就是最低公共祖先

如E和F的最低公共祖先为E
如D和I的最低公共祖先为A

5.1.5.1方法1

往上遍历过程生成一个链,用容器记录链,在另一节点向上遍历时走到容器记录过的节点即为最低公共祖先
需要二叉树头,两个节点

#include
#include
#include
#include 

using namespace std;

struct Node {
    int value;
    Node* left;
    Node* right;

    Node(int x) : value(x), left(nullptr), right(nullptr) {}
};

void process(Node* head, map<Node*, Node*>* fatherMap) {
    if (!head) return;
    fatherMap->insert(make_pair(head->left, head));
    fatherMap->insert(make_pair(head->right, head));
    process(head->left, fatherMap);//记录左儿子和父节点
    process(head->right, fatherMap);//记录右儿子和父节点
}

//o1和o2一定属于head为头的树
//返回o1和o2的最低公共祖先
Node* lca(Node* head, Node* o1, Node* o2) {
    auto* fatherMap = new map<Node*, Node*>();
    fatherMap->insert(make_pair(head, head));//设置head的父节点自己
    process(head, fatherMap);
    auto* set1 = new set<Node*>();//记录下o1往上整条链的节点
    set1->insert(o1);//先放入o1
    Node* cur = o1;//当前节点来到o1位置

    //o1经过的节点都放入set1中
    while (cur != fatherMap->find(cur)->second) {
        set1->insert(cur);
        cur = fatherMap->find(cur)->second;
    }
    set1->insert(head);
    cur = o2;
    //o2经过的节点都放入set1中,并且每经过一个点,都看在不在set中
    while (cur != fatherMap->find(cur)->second) {
        if (set1->find(cur) != set1->end())
            return cur;
        else {
            cur = fatherMap->find(cur)->second;
        }
    }
    return head;
}

int main() {
    Node* head = new Node(8);
    head->left = new Node(6);
    head->right = new Node(10);
    head->left->left = new Node(4);
    Node* o1 = head->left->right = new Node(7);
    Node* o2 = head->left->left->left = new Node(1);
    head->left->left->right = new Node(5);
    head->right->left = new Node(9);
    head->right->right = new Node(11);
    cout << lca(head, o1, o2)->value << endl;
}

5.1.5.2方法2

情况1:o1是o2的最低公共祖先(LCA),或o2是o1的最低公共祖先
情况2:o1和o2不互为最低公共祖先(LCA)

Node* lowestAncestor(Node* head, Node* o1, Node* o2)
{
    if (head == nullptr || head == o1 || head == o2)
    {
        return head;
    }
    Node* left = lowestAncestor(head->left, o1, o2);
    Node* right = lowestAncestor(head->right, o1, o2);
    if (left != nullptr && right != nullptr)//左树右树上都不为空就返回头部
    {
        return head;
    }
    return left != nullptr ? left : right;//两颗树并不都有返回值,谁不为空返回谁
}

在一个4层的完全二叉树中,o1,o2都在右树中,代码会怎么在左树中运行的状况

在一个4层的完全二叉树中,o1和o2都在右树中,那么代码将按照以下方式在左树中运行:

运行到第1行,此时head参数指向根节点。

运行到第3行,由于head不为空且不等于o1和o2,不会进入if语句,继续执行下一行。

运行到第7行,递归调用lowestAncestor函数传入head->left,即根节点的左子节点,在左树中进行递归。

递归调用的时候,继续从第1行开始执行,将左子节点作为新的head参数。

运行到第3行,由于head不为空且不等于o1和o2,不会进入if语句,继续执行下一行。

运行到第7行,继续递归调用lowestAncestor函数传入head->left,即左子节点的左子节点,在左树中继续递归。

重复步骤4-6,直到递归到最底层的叶子节点。

当递归到最底层叶子节点时,head为nullptr,因此第5行的条件判断为true,返回nullptr。

返回到上一层递归调用处,继续执行第6行。

运行到第6行,递归调用lowestAncestor函数传入head->right,即左子节点的右子节点,在左树中继续递归。

重复步骤4-10,直到递归到包含o1和o2的子树。

当递归到包含o1和o2的子树时,第3行的条件判断为true,返回包含o1和o2的子树的根节点。

因此,在这种情况下,代码将在左树中执行直到找到包含o1和o2的子树的根节点,并返回该节点。

当到达最深层时会返回到上一层的递归调用处继续执行下一行代码。这是递归的特性。在这种情况下,当递归到达最底层叶子节点时,递归调用将返回到上一层递归调用处,继续执行下一行代码。

5.1.6例6:在二叉树中找到一个节点的后继节点

先在有一种新的二叉树节点类型如下

public class Node{
    public int value;
    public Node left;
    public Node right;
    public Node parent;
    public Node(int val){
    value=val;
    }
}

该结构比普通二叉树节点结构多了一个指向父节点的parent指针
假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节点的parent指向null。
只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数

在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。

5,二叉树【p6-p7】_第28张图片
题意解析:正常来将需要中序排列才能找到后继节点,但是此时的时间复杂度为O(N),当题中给出了指向父节点的指针,那么,如果两个节点之间真实距离为k的话能不能让时间复杂度变为O(k)

情况1:x有右树的时候,它的后继节点为他的右树上的最左节点

情况2:x无右树的时候,
看它的父节点,是不是它的父节点的左孩子,不是,
看它的父节点,是不是它的父节点的左孩子,不是,
看它的父节点,是不是它的父节点的左孩子,不是,
看它的父节点,是不是它的父节点的左孩子,是,
它的后继节点为此节点的父

因为对此节点来说,x是其左子树最右的节点
5,二叉树【p6-p7】_第29张图片
情况3:没有后继,当处于整个二叉树的最右侧最后一个节点是没有后继的

#include 

struct Node {
    int val;
    Node* left;
    Node* right;
    Node* parent;
    Node (int val):val(val), left(nullptr), right(nullptr),parent(nullptr) {}
};

Node* getMostLeftNode(Node* node) {
    if (node == nullptr) {
        return node;
    }
    while (node->left != nullptr) {
        node = node->left;
    }
    return node;
}

Node* getNextNode(Node* node) {
    if (node == nullptr) {
        return node;
    }
    if (node->right != nullptr) {   // if node has right tree, we find the leftest node
        return getMostLeftNode(node->right);
    } else {
        Node* parent = node->parent;
        while (parent != nullptr && parent->left != node) {
            node = parent;
            parent = node->parent;
        }
        return parent;
    }
}

int main()
{
    Node* head = new Node(6);
    head->parent = nullptr;
    head->left = new Node(3);
    head->left->parent = head;
    head->left->left = new Node(1);
    head->left->left->parent = head->left;
    head->left->left->right = new Node(2);
    head->left->left->right->parent = head->left->left;
    head->left->right = new Node(4);
    head->left->right->parent = head->left;
    head->left->right->right = new Node(5);
    head->left->right->right->parent = head->left->right;
    head->right = new Node(9);
    head->right->parent = head;
    head->right->left = new Node(8);
    head->right->left->parent = head->right;
    head->right->left->left = new Node(7);
    head->right->left->left->parent = head->right->left;
    head->right->right = new Node(10);
    head->right->right->parent = head->right;

    Node* test1 = head->left->left;
    std::cout << test1->val <<  " next node: " <<  getNextNode(test1)->val << std::endl;
    Node* test2 = head->left->left->right;
    std::cout << test2->val <<  " next node: " <<  getNextNode(test2)->val << std::endl;
    Node* test3 = head->right->right;
    std::cout << test3->val <<  " next node: " <<  getNextNode(test3)<< std::endl;
    return 0;
}


5,二叉树【p6-p7】_第30张图片

5.1.7例7:二叉树的序列化和反序列化

就是内存里的一棵树如何变成字符串形式,又如何从字符串形式变成内存里的树
如何判断一颗二叉树是不是另一棵二叉树的子树?
由内存变为字符串叫序列化,由字符串还原为内存结构叫反序列化
5,二叉树【p6-p7】_第31张图片
先序、中序、后序、按层等方式序列化都可以,以先序方式举例
例:
看图中左侧树
_ # _表示为空
来到头节点1:1
左孩子为空:1 _ # _
右孩子为1:1 _ # _ 1 _
右->左孩子为1:1 _ # _ 1 _ 1 _
右->左->左孩子为空:1 _ # _ 1 _ 1 _ # _
右->左->右孩子为空:1 _ # _ 1 _ 1 _ # _ # _
右->右孩子为空:1 _ # _ 1 _ 1 _ # _ # _ # _

看图中右侧树
来到头节点1:1
左孩子为1:1 _ 1 _
左->左孩子为空:1 _ 1 _ # _
左->右孩子为1:1 _ 1 _ # _ 1 _
左->右->左孩子为1:1 _ 1 _ # _ 1 _ # _
左->右->右孩子为1:1 _ 1 _ # _ 1 _ # _ # _
右孩子为空:1 _ 1 _ # _ 1 _ # _ # _ # _

以符号还原可以还原
1,#,1,1,#,#,#,
根据先序遍历
先1为头:#,1,1,#,#,#,
左孩子为空:1,1,#,#,#,
右孩子为1:1,#,#,#,
右->左孩子为1:#,#,#,
右->左->左孩子为空:#,#,
右->左->右孩子为空:#,
右->右孩子为空:

前序遍历序列化代码

5,二叉树【p6-p7】_第32张图片

前序遍历反序列化代码

5,二叉树【p6-p7】_第33张图片

5.1.8例8:折纸问题

请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。
此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。
如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次。请从上到下打印所有折痕的方向。
例如:N=1时,打印:down N=2时,打印:down down up

折纸发现,会在折痕上方出现凹折痕,在下方出现凸折痕
5,二叉树【p6-p7】_第34张图片
5,二叉树【p6-p7】_第35张图片
只用了N个空间

你可能感兴趣的:(左神LeetCode500道,算法)