二叉樹 c++


樹 非空樹

  • 有一個(root)根節點r
  • 其余節點可分為m個互不相交的有限集(子樹)T1....Tm

具有n個節點的樹,具有(n-1)條連接(指針域),需要構成結構體,盡可能減少空間域的浪費,使用兒子兄弟結構體,每個結構體包含 數據 兒子 及 兒子的兄弟

typedef struct Tree{
    ElemType data;
    Tree* First_Child;   ## 兒子
    Tree* Next_Sibling;   ## 兄弟
}
/* 二叉樹結構體 */
typedef int Elementtype;

typedef struct TreeNode{
    Elementtype data;
    TreeNode* left;
    TreeNode* right;
}TreeNode,*PTREE;

  • 第i層最多可以有2^(i-1) 每個節點可以延伸2子節點 222...*2
  • 深度為k的二叉樹 最大節點數 2^k -1
  • 非空二叉樹,n0 = n2 + 1 邊數證明:
1. 節點數-1 = 邊長數
2. n0+n1+n2-1=0*n0+1*n1+2*n2

主要操作:判空 遍歷 創建 刪除 查找 平衡

  • 先序 根-左-右 (遞歸 or 堆棧)
  • 中序 左-根-右
  • 后序 左-右-根 根的遍歷次序
  • 層次遍歷 從上到下 從左到右 (隊列)

順序存儲結構 注意 左右孩子和父節點的索引關系

根據兩個遍歷確定一棵樹

根據先序/中序 或者 后序/中序 判斷二叉樹

  • 先序: a b c d e f g h i j
  • 中序: c b e d a h g i j f

我們知道先序遍歷 第一個元素為根(a),所以利用將中序遍歷分成左右子樹(cbed)(hgijf),再找根節點(b),分成(c)(ed)兩個子樹,逐漸遞歸:

構建二叉樹

參考二叉樹的遞歸遍歷,可以同樣適用遞歸進行二叉樹的構建,先申請根節點的空間,然后賦值,然后分別遞歸建立其左子樹和右子樹


//輸入 abc***de**fg***  建立先序遍歷為abcdefg的普通樹
//需要修改指向 樹結構的 指針,所以形參為 指針的引用(指針的指針)
void CreatTree(PTREE& Root){
    char a=0;
    a = getchar();
    if(a=='*'){
        Root = NULL;
    }
    else{
        Root = (TreeNode *)malloc(sizeof(TreeNode));
        if(Root==NULL)
            printf("Failed");
        else{
            Root->data = a;
            CreatTree(Root->left);
            CreatTree(Root->right);
        }
    }
}

二叉樹的遍歷

二叉樹的遍歷分為 先序 中序 后序遍歷,可以使用遞歸遍歷 和 非遞歸遍歷

遞歸遍歷


//先序**
void fsearch(PTREE Root){
    if (Root == NULL)  return;
    else{
        printf("%d  ",Root->data);
        fsearch(Root->left);
        fsearch(Root->right);
    }
}

// 中序
void msearch(PTREE Root){
    if (Root == NULL)  return;
    else{
        msearch(Root->left);
        printf("%d  ",Root->data);
        msearch(Root->right);
    }
}

// 后序
void psearch(PTREE Root){
    if (Root == NULL)  return;
    else{
        psearch(Root->left);
        psearch(Root->right);
        printf("%d  ",Root->data);
    }
}

非遞歸遍歷

需要借助棧(先進后去)的特性實現非遞歸

  1. 先序(根左右)

void f_search(PTREE Root){
    std::stack<PTREE> my_s;
    PTREE cur=Root;

    while(cur!=NULL || !my_s.empty()){

        while(cur!=NULL){
            printf("%d ",cur->val);
            my_s.push(cur);
            cur = cur->left;
        }

        if(!my_s.empty()){
            cur = my_s.top();
            my_s.pop();
        }
        cur = cur->right;
    }
}

  1. 中序(左根右)
void m_search(PTREE Root){
    std::stack<PTREE> my_s;
    PTREE cur=Root;

    while(cur!=NULL || !my_s.empty()){
    
        while(cur!=NULL){
            my_s.push(cur);
            cur = cur->left;
        }

        cur = my_s.top();
        my_s.pop();
        printf("%d ",cur->val);
        cur = cur->right;
    }
}

  1. 后序(左右根)
void d_search(PTREE Root){
    std::stack<PTREE> my_s;
    PTREE cur=Root,Pre= nullptr;
    
    while(cur || !my_s.empty()){
        while(cur)
        {
            my_s.push(cur);
            cur = cur->left;
        }

        PTREE top_node = my_s.top();
        if( Pre == top_node->right || top_node->right==NULL  ){
            printf("%d ",top_node->val);
            my_s.pop();
            Pre = top_node;
        }
        else
            cur = top_node->right;
    }
}

實現2

void d_search3(PTREE Root){
    std::stack<PTREE> my_s,my_s2;

    PTREE cur = Root;
    my_s.push(Root);
    while(!my_s.empty())
    {
        cur = my_s.top();
        my_s.pop();
        my_s2.push(cur);
        if(cur->left) my_s.push(cur->left);
        if(cur->right) my_s.push(cur->right);
    }
    while(!my_s2.empty())
    {
        cur = my_s2.top();
        my_s2.pop();
        std::cout << cur->val << " ";
    }
}


二叉樹的插入


void Insert_Node(PTREE& Root,ElemType a){

    if(Root==NULL){
        Root = (PTREE)malloc(sizeof(TreeNode));
        if(!Root)
            printf("malloc failed");
        else{
            Root->val = a;
            Root->left = NULL;
            Root->right = NULL;
        }
    }else{
        if(a>Root->val){
            Insert_Node(Root->right,a);
        }else if(a<Root->val){
            Insert_Node(Root->left,a);
        }else{
            printf("dup node");
        }
    }
}

二叉樹的刪除

void Free_Tree(PTREE& Root){

    if(Root == nullptr ) return ;

    Free_Tree(Root->left);
    Free_Tree(Root->right);
    free(Root);
    Root = nullptr;
}

普通二叉樹的查找


int Find_Node(PTREE Root,int target){
    if(Root==NULL) return -1;

    if(target == Root->val) 
       return target;
    
    if(target > Root->val)
        return Find_Node(Root->right,target);
    else if(target < Root->val){
        return Find_Node(Root->left,target);
    }
}

104. Maximum Depth of Binary Tree

Tree高度延伸的一類題目
最基礎的遞歸,先遞歸到底,當Leaf Node的左右兩個Children Node都分別觸及Base Case,也就是None的時候,向上返回。然后之后對應當前node,左右兩邊的遞歸都操作結束以后,返回的過程中對左右高度進行對比,取兩個中間最大值,然后這里記住要加1,也就是當前的層數。


class Solution(object):
    def maxDepth_gd(self, root):
        if not root: return 0

        left = self.maxDepth(root.left)
        right = self.maxDepth(root.right)
        return max(left, right) + 1
        

110. Balanced Binary Tree

有了104的基礎,我們在延伸下看看110這道題,其實就是基於高度計算,然后判斷一下。

但由於嵌套的Recursion調用,整體的時間復雜度是:O(nlogn) , 在每一層調用get_height的平均時間復雜度是O(N),然后基於二叉樹的性質,調用了的高度是logn,所以n * logn 的時間復雜。

時間復雜度為什么是nlogn搞不清楚的看 時間復雜度圖解

class Solution(object):
    def isBalanced(self, root):
        if not root: return True
        left = self.get_height(root.left)
        right = self.get_height(root.right)
        if abs(left - right) > 1: 
            return False  
        return self.isBalanced(root.left) and self.isBalanced(root.right)

        
    def get_height(self, root):
        if not root: return 0
        left = self.get_height(root.left)
        right = self.get_height(root.right)
        return max(left, right) + 1

上面這種Brute Froce的方法,整棵樹有很多冗余無意義的遍歷,其實我們在處理完get_height這個高度的時候,我們完全可以在檢查每個節點高度並且返回的同時,記錄左右差是否已經超過1,只要有一個節點超過1,那么直接返回False即可,因此我們只需要在外圍設立一個全球變量記錄True和False,在調用get_height的時候,內置代碼里加入對左右高度的判定即可,代碼如下

時間復雜度: O(N)

Recursive Rules:

索取:Node的左孩子是不是全部是Balanced,Node的右孩子是不是全部是Balanced的,返回:如果都是Balanced的,返回True,不然返回False

class Solution(object):
    def isBalanced(self, root):
        self.flag = False
        self.getHeight(root)
        return not self.flag
        
    
    def getHeight(self, root):
        if not root: return 0
        left = self.getHeight(root.left)
        right = self.getHeight(root.right)
        if abs(left - right) > 1: 
            self.flag = True
        return max(left, right) + 1

最后Leetcode上有一種-1的方法,其實就是上面這種方法的一種延伸。如果左右兩邊出現了高度差高於1的情況,直接返回-1,這個-1怎么來的?因為高度不可能為負數,-1其實就是一種True/False的表達。

那么在實現上,我們只要對get_height每次返回前做一個判定即可,具體實現看下方:

時間復雜度: O(N)

class Solution(object):
    def isBalanced(self, root):
        height = self.get_height(root)
        return height != -1

        
    def get_height(self, root):
        if not root: return 0
        left = self.get_height(root.left)
        right = self.get_height(root.right)
        if left == -1 or right == -1 : return -1          
        if abs(left - right) > 1:  return -1
        return max(left, right) + 1

111. Minimum Depth of Binary Tree

返回葉子 (無左右孩子的節點) 節點的最小深度

int minDepth(TreeNode* root) {
        if(root==NULL) return 0;
        int dep =1;
        queue<TreeNode*> q_cur,q_next;
        q_cur.push(root);
        
        while(!q_cur.empty()){
                TreeNode* a = q_cur.front();
                q_cur.pop();
               
               if(a->left==NULL && a->right==NULL) 
                    return dep;
               if(a->left!=NULL)
                   q_next.push(a->left);
               if(a->right!=NULL)
                   q_next.push(a->right);
            
                if(q_cur.empty()){
                    swap(q_cur,q_next);
                    dep+=1;
                    
                }
            }
             return dep;
    }

# 或者使用一個隊列  每一次層次遍歷 都提前計算出當前層的size   既當前層的元素數量

//         while(!q.empty()){
//             int size = q.size();
//             while(size>0){
//                 TreeNode* a = q.front();
//                 q.pop();
               
//                if(a->left==NULL && a->right==NULL) 
//                     return dep;
//                if(a->left!=NULL)
//                    q.push(a->left);
//                if(a->right!=NULL)
//                    q.push(a->right);
//                size--;
//             }
            
//             dep+=1;
//         }

112. Path Sum 路徑數值和

普通的dfs 遞歸

bool hasPathSum(TreeNode* root, int sum) {
       if(root == NULL) return false;
        
       if(root->left == NULL && root->right == NULL && sum-root->val==0)  return true;
       
       return hasPathSum(root->left,(sum-root->val)) || hasPathSum(root->right,(sum-root->val));   
    }

利用棧實現的非遞歸dfs

bool hasPathSum(TreeNode* root, int sum) {
        if(root==NULL) return false;
        
        stack<TreeNode*> s;
        stack<int> s_n;
        
        s.push(root);
        s_n.push(root->val);
        
        while (!s.empty()){
            TreeNode* a = s.top();
            int b = s_n.top();
            s.pop(); s_n.pop();
            if(a->left==NULL && a->right==NULL && b==sum)
                return true;
            
            if(a->left!=NULL){
                s.push(a->left);
                s_n.push(b+a->left->val);
            }
            
            if(a->right!=NULL){
                s.push(a->right);
                s_n.push(b+a->right->val);
            }
            
        }
        
      return false;  
    }

輸出Tree的路徑value 257. Binary Tree Paths

遞歸dfs 實現

 void dfs(TreeNode* root,string str,vector<string>& res){
        
        if(root->left==NULL && root->right==NULL){
            res.push_back(str);
        }
        if(root->left!=NULL){
            dfs(root->left,str+"->"+to_string(root->left->val),res);
        }
        if(root->right!=NULL){
            dfs(root->right,str+"->"+to_string(root->right->val),res);
        }     
    }
    
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        if(root == NULL) return res;
        string str = to_string(root->val);
        dfs(root,str,res);
        return res;
        
    }

使用Stack 進行的非遞歸遍歷

    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        if(root == NULL) return res;
        stack<TreeNode*> s;
        stack<string> str_s;
        s.push(root);
        str_s.push(to_string(root->val));
        string str;
        while(!s.empty()){
            TreeNode* a = s.top();
            str = str_s.top();
            s.pop(); str_s.pop();
            
            if(a->left==NULL && a->right==NULL)
                res.push_back(str);
            
            
            if(a->left!=NULL){
                s.push(a->left);
                str_s.push(str+"->"+to_string(a->left->val));
            }
            if(a->right!=NULL){
                s.push(a->right);
                str_s.push(str+"->"+to_string(a->right->val));
            }
            
            
        }
        reverse(res.begin(),res.end());
        return res;

翻轉二叉樹 226. Invert Binary Tree

dfs 遍歷每一個節點 進行交換

    TreeNode* invertTree(TreeNode* root) 
    {
        dfs_invert(root);
        return root;
    }
    void dfs_invert(TreeNode* root)
    {
        if(root == NULL)  return;
        
        invertTree(root -> left);
        invertTree(root -> right);
        // swap after the left subtree and right subtree has been done
        swap(root -> left, root -> right);
    }

queue_BFS

   TreeNode* invertTree(TreeNode* root) 
    {
         if(!root) return NULL;
            queue<TreeNode*> q;
            q.push(root);
            while(!q.empty()) {
                TreeNode* node = q.front();
                q.pop();
                #只需要交換 非葉子節點的左右孩子即可
                if (node->left != NULL || node->right != NULL) {
                    TreeNode* temp = node->left;
                    node->left = node->right;
                    node->right = temp;
                }
                if (node->left)
                    q.push(node->left);
                if (node->right)
                    q.push(node->right);
            }
        return root;
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM