秋招备战-算法系列(二叉树篇)更新中。。。

数据结构:

四种主要的遍历思想为:
深度优先:
前序遍历:根结点 —> 左子树 —> 右子树
中序遍历:左子树—> 根结点 —> 右子树
后序遍历:左子树 —> 右子树 —> 根结点

广度优先:
层次遍历:只需按层次遍历即可

基础题

给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式)。

递归解法
/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/

class TreeToSequence {
public:
    void preOrder(TreeNode* root, vector<int>& v){
        if(root == NULL) return;
        v.push_back(root->val);
        preOrder(root->left, v);
        preOrder(root->right, v);
    }
    
    void midOrder(TreeNode* root, vector<int>& v){
        if(root == NULL) return;
        midOrder(root->left, v);
        v.push_back(root->val);
        midOrder(root->right, v);
    }
    
    void afterOrder(TreeNode* root, vector<int>& v){
        if(root == NULL) return;
        afterOrder(root->left, v);
        afterOrder(root->right, v);
        v.push_back(root->val);
    }
    
    vector<vector<int> > convert(TreeNode* root) {
        // write code here
        vector<vector<int>> result;
        vector<int> pre, mid, after;
        result.resize(3);
        preOrder(root, pre);
        midOrder(root, mid);
        afterOrder(root, after);
        result = {pre, mid, after};
        return result;
        
    }
};
非递归解法

思路:
二叉树的非递归解法需要借助栈来实现,具体三种遍历的方法如下
先序遍历:
s1:创建一个栈为s
s2:将二叉树的头节点head压入栈s中。
s3:将s中的头节点出栈,并且记为cur,如果cur的右节点不为空,将右节点入栈,如果cur的左节点不为空,左节点入栈
s4:重复s3直到栈为空时结束

头节点入栈
在这里插入图片描述
1为cur
打印序列:1
在这里插入图片描述
2为cur
打印序列:1 2
在这里插入图片描述
5为cur
打印序列:1 2 4 5
在这里插入图片描述
中序遍历:
s1:创建一个栈为s,head为cur
s2:将cur压入栈中,不断的使cur = cur.left,重复步骤s2。
s3:当cur为空时,弹出头节点记为node并打印,cur = node.right,重复步骤二,若cur的右节点为空,继续出栈记为node并打印
s4:当stack为空,cur为空,结束

cur = 4
在这里插入图片描述
cur = 4.left
出栈:4
在这里插入图片描述
cur = 4.right
出栈:4 2
在这里插入图片描述
cur = 2.right
在这里插入图片描述
cur = 5.right
出栈:4 2 5 1
cur = 1
在这里插入图片描述
后序遍历:
s1:创建一个栈为s,h为最近一次弹出的节点,c指向栈顶节点,h初始为head,c为null
s2:t指向栈顶节点,此时有三种情况
1.若c的左节点不为空,h不等于c的左节点和右节点,h的左节点入栈
2.若不满足条件1,c的右节点不为空,h不等于c的右孩子,h的右节点入栈
3.若不满足1,2条件,弹出c,h = t
3.重复s2,直到s为空
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
void pre(TreeNode* root, vector<int> &v){
    stack<TreeNode*> s;
    TreeNode* cur = NULL;
    s.push(root);
    while(!s.empty()){
        cur = s.top();
        v.push_back(cur->val);
        s.pop();
        if(cur->right != NULL) s.push(cur->right);
        if(cur->left != NULL) s.push(cur->left);
    }
}

void middle(TreeNode* root, vector<int> &v){
    stack<TreeNode*> s;
    TreeNode* cur = root;
    TreeNode* node = NULL;
    //s.push(root);
    while(!s.empty() || cur != NULL){
        if(cur != NULL){
            s.push(cur);
            cur = cur->left;
        }else{
            node = s.top();
            v.push_back(node->val);
            s.pop();
            cur = node->right;
        }
    }
}

void after(TreeNode* root, vector<int> &v){
    stack<TreeNode*> s;
    TreeNode* c = NULL;//top
    TreeNode* h = root;//pop node
    s.push(root);
    c = s.top();
    while(!s.empty()){
        if(c->left != NULL && h!=c->left && h!=c->right){
            s.push(c->left);
        }else if(c->right != NULL && h != c->right){
            s.push(c->right);
        }else{
            h = s.top();
            v.push_back(h->val);
            s.pop();
        }
        c = s.top();
    }
}

class TreeToSequence {
public:
    vector<vector<int> > convert(TreeNode* root) {
        vector<vector<int>> res;
        vector<int> temp;
        // write code here
        pre(root, temp);
        res.push_back(temp);
        temp.clear();
        
        middle(root, temp);
        res.push_back(temp);
        temp.clear();
        
        after(root, temp);
        res.push_back(temp);
        temp.clear();
        
        return res;
    }
};

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

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot == NULL) return 0;
        int left = TreeDepth(pRoot->left);
        int right = TreeDepth(pRoot->right);
        return (left > right)? left+1:right+1;
    }
};

有一棵二叉树,请设计一个算法判断这棵二叉树是否为平衡二叉树。

基础知识:
平衡二叉树:
任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过1

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/

class Solution {
public:
    int getDepth(TreeNode* root){
        if(root == NULL) return 0;
        int left = getDepth(root->left);
        int right = getDepth(root->right);
        return (left>right)? (left+1) : (right+1);
    }
    bool IsBalanced_Solution(TreeNode* pRoot) {
        if(pRoot == NULL) return true;
        int left = getDepth(pRoot->left);
        int right = getDepth(pRoot->right);
        if(abs(left-right) > 1){
            return false;
        }
        return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);;
    }
};

从上往下打印出二叉树的每个节点,同层节点从左至右打印。(广度优先遍历)

在这里插入图片描述
思路:
该题采用队列的方式来实现,将根节点0入队列
s1:当打印根节点0时,将0的两个节点1和2入队列,0出队列
s2:将队列最开始的元素1打印,出队列,将1的两个子节点3和4入队列
s3:不断重复s2,直到队列为空

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> res;
        queue<TreeNode*> q;
        TreeNode* cur;
        if(root == NULL) return res;
        q.push(root);
        while(q.size()){
            cur = q.front();
            res.push_back(cur->val);
            q.pop();
            if(cur->left){
                q.push(cur->left);
            }
            if(cur->right){
                q.push(cur->right);
            }
        }
        return res;
    }
};

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

在这里插入图片描述
思路:
这道题和上一道题类似,也是用队列的方法来实现的,思路在上一题的基础上再添加两个变量,nextLevel用来存储下一层的节点数,toBePrinted用来存储当前层未打印的节点数。
s1:将根节点a入队列,toBePrinted初始值为1,nextLevel为0
s2:当打印根节点a时,a出队列,toBePrinted减1,若根节点的左节点b存在,nextLevel自增1,右节点c同理,b,c入队列,当toBePrinted等于0时,本行打印结束,并且toBePrinted=nextLevel
s3:重复s2直到栈为空

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
        vector<vector<int>> Print(TreeNode* pRoot) {   
            vector<vector<int>> res;
            vector<int> temp;
            if(pRoot == nullptr) return res;
            
            queue<TreeNode*> q;
            q.push(pRoot);
            int nexlLevel = 0;
            int toBePrinted = 1;
            TreeNode* node;
            while(!q.empty()){
                node = q.front();
                q.pop();
                temp.push_back(node->val);
                if(node->left != nullptr){
                    q.push(node->left);
                    ++nexlLevel;
                }
                if(node->right != nullptr){
                    q.push(node->right);
                    ++nexlLevel;
                }
                toBePrinted--;
                if(toBePrinted == 0){
                    res.push_back(temp);
                    temp.clear();
                    toBePrinted = nexlLevel;
                    nexlLevel = 0;
                }
            }
            return res;
        }  
};

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

在这里插入图片描述
思路:
这一题使用栈的数据结构,从之前的两个问题可以知道,顺序打印每一个节点使用队列的数据结构,那么逆序打印使用的是栈的数据结构。
从图上可以知道:
当行数为奇数行1,3时先入栈左节点再入栈右节点,当行数为偶数时先入栈右节点再入栈左节点,这道题用两个栈分别存储当前层的节点的和下一层的节点

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> res;
        vector<int> temp;
        if(pRoot == nullptr) return res;
        stack<TreeNode*> levels[2];
        int current = 0;
        int next = 1;
        levels[current].push(pRoot);
        while(!levels[1].empty() || !levels[0].empty()){
            TreeNode* pNode = levels[current].top();
            temp.push_back(pNode->val);
            levels[current].pop();
            if(current == 0){
                if(pNode->left != nullptr) levels[next].push(pNode->left);
                if(pNode->right != nullptr) levels[next].push(pNode->right);
            }else{
                if(pNode->right != nullptr) levels[next].push(pNode->right);
                if(pNode->left != nullptr) levels[next].push(pNode->left);
            }
            if(levels[current].empty()){
                res.push_back(temp);
                temp.clear();
                current = 1-current;
                next = 1-next;
            }
        }
        return res;
    }
    
};

进阶题

和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路:
这道题可以采取二叉树递归的思路来解:二叉树的前序遍历中第一个节点是二叉树的根节点,二叉树的中序遍历中,根节点左边的节点是左子树,根节点右边的节点是右子树。
s1:求出二叉树的根节点
s2:找出二叉树的左子树以及右子树
s3:递归重复以上步骤

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int len = pre.size();
        if (len == 0){
            return NULL;
        }
        int root = 0;
        TreeNode* head = new TreeNode(pre[0]);
        vector<int> preLeft, inLeft, preRight, inRight;
        for (int i = 0; i < len; i ++){
            if (pre[0] == vin[i]){
                root = i;
                break;
            }
        }
        for (int i = 0; i < root; i++){
            inLeft.push_back(vin[i]);
            preLeft.push_back(pre[i+1]);
        }
        for (int i = root+1; i < len; i++){
            inRight.push_back(vin[i]);
            preRight.push_back(pre[i]);
        }
        head->left = reConstructBinaryTree(preLeft, inLeft);
        head->right = reConstructBinaryTree(preRight, inRight);
         
        return head;
    }
};

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

思路:
这题采用递归的思路
s1:在a树中找出与b树根节点相等的值
s2:判断a树中与b树根节点相等的子树的左右子树是否相等

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        bool result;
        if(pRoot1!= NULL && pRoot2!=NULL){
            if(pRoot1->val == pRoot2->val){
                result = HasSubtree2(pRoot1,pRoot2);
            }
            if(!result){
                result = HasSubtree(pRoot1->right, pRoot2);
            }
            if(!result){
                result = HasSubtree(pRoot1->left, pRoot2);
            }
        }
        return result;
    }
    bool HasSubtree2(TreeNode* pRoot1, TreeNode* pRoot2){
        if(pRoot2 == NULL){
            return true;
        }
        if(pRoot1 == NULL){
            return false;
        }
        if(pRoot1->val != pRoot2->val){
            return false;
        }
        return HasSubtree2(pRoot1->left, pRoot2->left) && HasSubtree2(pRoot1->right, pRoot2->right);
    }
};

操作给定的二叉树,将其变换为源二叉树的镜像。

在这里插入图片描述

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot==NULL) return;
        if(pRoot->left == NULL && pRoot->right == NULL) return;
        
        TreeNode *temp = pRoot->left;
        pRoot->left = pRoot->right;
        pRoot->right = temp;
        
        if(pRoot->left){
            Mirror(pRoot->left);
        }
        if(pRoot->right){
            Mirror(pRoot->right);
        }
    }
};

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

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    bool isSame(TreeNode* r1, TreeNode* r2){
        if(r1 == NULL && r2 == NULL) return true;
        if(r1 == NULL || r2 == NULL) return false;
        if(r1->val != r2->val) return false;
        return isSame(r1->left, r2->right) && isSame(r1->right, r2->left);
    }
    bool isSymmetrical(TreeNode* pRoot)
    {
        if (pRoot == NULL) return true;
        return isSame(pRoot->left, pRoot->right);
    }

};

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

在这里插入图片描述
思路:
关于二叉树中序遍历的下一个节点有三种情况:
1.如果一个节点有右子树,那么下一个节点为从右子树出发一直指向左节点的指针
例如:b的下一个节点为h
2.如果一个节点没有右子树,并且是父节点的左节点,下一个节点为父节点。例如:h下一个节点为e
3.如果一个节点既没有右子树,并且是父节点的右节点,这时就要沿着父指针一直向上遍历,直到遍历到的当前节点满足条件2,那么下一个节点为该节点的父节点。例如:i的下一个节点为a

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(pNode == nullptr){
            return nullptr;
        }
        TreeLinkNode* pNext = nullptr;
        if(pNode->right != nullptr){
            TreeLinkNode* pRight = pNode->right;;
            while(pRight->left != nullptr){
                pRight = pRight->left;
            }
            pNext = pRight;
        }
        else if(pNode->next != nullptr){
            TreeLinkNode* pCurrent = pNode;
            TreeLinkNode* pParent = pNode->next;
            while(pParent != nullptr && pCurrent == pParent->right){
                pCurrent = pParent;
                pParent = pParent->next;
            }
            pNext = pParent;
        }
        return pNext;
    }
};

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

基础知识:
二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,对于树中每个节点:
若其左子树存在,则其左子树中每个节点的值都不大于该节点值;
若其右子树存在,则其右子树中每个节点的值都不小于该节点值。
二叉树的后序遍历结果中,最后一个元素为二叉树的根节点
思路:
根据二叉树和二叉搜索树的基本性质,序列最后一个元素为根节点,之前的序列可以看作两个部分,比根节点小的序列为左子树,比根节点大的序列为右子树。

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.size() == 0) return false;
        return func(sequence, 0, sequence.size()-1);
    }
    bool func(vector<int> array, int begin, int end){
        int root = array[end];
        int i;
        for(i = begin; i < end; i++){
            if(array[i]>root) break;
        }
        int j = i;
        for(; j < end; j++){
            if(array[j]<root) return false;
        }
        bool left = true;
        if(begin<i-1) left = func(array, begin, i-1);
        bool right = true;
        if(i<end-1) right = func(array, i, end-1);
        return left && right;
    }
};

复杂题

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

思路:
该题使用递归的思想,当访问到某一节点时,若该节点不是叶节点,该节点下面的路径和等于输入的值减去这一节点的值。当该节点访问结束时,递归自动回归到它的父节点。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<vector<int> > buffer;
    vector<int> tmp;
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        if(root==NULL)
            return buffer;
        tmp.push_back(root->val);
        if((expectNumber-root->val)==0 && root->left==NULL && root->right==NULL)
            {
            buffer.push_back(tmp);
        }
        FindPath(root->left,expectNumber-root->val);
        FindPath(root->right,expectNumber-root->val);
        if(tmp.size()!=0)
            tmp.pop_back();
        return buffer;
    }
};

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void convert(TreeNode* root, TreeNode** lastNode){
        if(root == nullptr) return;
        TreeNode* pNode = root;
        if(root->left != nullptr){
            convert(root->left, lastNode);
        }
        pNode->left = *lastNode;
        if(*lastNode != nullptr){
            (*lastNode)->right = pNode;
        }
        *lastNode = pNode;
        if(root->right != nullptr){
            convert(root->right, lastNode);
        }
    }
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        TreeNode* lastNode = nullptr;
        convert(pRootOfTree, &lastNode);
        TreeNode* pHead = lastNode;
        while(pHead != nullptr && pHead->left != nullptr){
            pHead = pHead->left;
        }
        return pHead;
    }
};

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

思路:
二叉搜索树的中序遍历为节点从小到大排序的顺序,第k小的值为中序遍历的第k个节点

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(pRoot == nullptr || k == 0) return nullptr;
        return  findTarget(pRoot, k);
    }
    
    TreeNode* findTarget(TreeNode* pRoot, int &k){
        TreeNode* target = nullptr;
        if(pRoot->left != nullptr){
            target = findTarget(pRoot->left, k);
        }
        if(target == nullptr){
            if(k==1)
                target = pRoot;
            //遍历一次k减1
            k--;
        }
        if(target == nullptr && pRoot->right != nullptr){
            target = findTarget(pRoot->right, k);
        }
        return target;
    }
    
};

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

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。
序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值