Tree 小結(jié)

以下是數(shù)據(jù)結(jié)構(gòu)部分的主要知識點的思維導圖

tree_introduce.png

在這段時間基本上刷的都是跟二叉樹有關(guān)的題目九默,所以下面主要針對二叉樹部分進行總結(jié)逃魄。
  二叉樹的各種操作基本上離不開基本的幾個點,遍歷伍俘、搜索癌瘾、節(jié)點插入和刪除饵溅。利用這幾個操作互相結(jié)合就能解決大部分問題,當然前提是先理解它們的意義和熟悉操作代碼的編寫咬荷。下面分別對這些操作進行代碼的實現(xiàn)與分析轻掩。

二叉樹操作.png

假設(shè)樹的結(jié)構(gòu)體實現(xiàn)如下:

 Definition for a binary tree node.
  struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };

遍歷 (前序唇牧、中序、后序)

以中序遍歷為例(其他兩種情況大同小異)腔召,假設(shè)現(xiàn)在需要對一個根節(jié)點為root(不為空)的樹進行遍歷將節(jié)點的值按順序放到vector中扮惦,這種情況下同常有遞歸和非遞歸兩種實現(xiàn)方式,實現(xiàn)代碼如下:

  • 遞歸實現(xiàn)
class Solution {
public:
    vector<int> traverse(TreeNode* root, vector<int>& vec) {
         traverse(root->left);
         vec.push_back(root->val);
         traverse(root->right);
         return vec;
   }
};
  • 使用棧實現(xiàn)h

1.遍歷后樹會發(fā)生變化的實現(xiàn)方式

class Solution {
public:
    vector<int> traverse(TreeNode* root, vector<int>& vec) {
           stack<TreeNode*> s;
           s.push(root);
           while(!s.empty()){
             TreeNode* node = s.top();
              if(node ->left){
                  s.push(node ->left);
                  node ->left = NULL;//防止重復遍歷
              }else{
                  s.pop();
                  vec->push(node->val);
                   if(node->right){
                     s.push(node->right);
                   }   
              } 
          }
       }
       return vec;
   }
};

2.遍歷后原樹不會發(fā)生變化(使用unordered_map)

class Solution {
public:
    vector<int> traverse(TreeNode* root, vector<int>& vec) {
           unordered_map<TreeNode*, bool> map;
           stack<TreeNode*> s;
           s.push(root);
           while(!s.empty()){
             TreeNode* node = s.top();
              if(node ->left && !map[node]){
                  s.push(node ->left);
                  map[node->left] = true;
              }else{
                  s.pop();
                  vec->push(node->val);
                   if(node->right){
                     s.push(node->right);
                   }   
              } 
          }
       }
       return vec;
   }
};

3.遍歷后原樹不會發(fā)生變化(不使用unordered_map)

class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> vector;
        stack<TreeNode *> stack;
        TreeNode *pCurrent = root;

        while(!stack.empty() || pCurrent)
        {
            if(pCurrent)
            {
                //同一根節(jié)點下的左右節(jié)點客峭,左節(jié)點比右節(jié)點先出棧(即左節(jié)點后進棧,其實最后所有進棧的節(jié)點都可以視左一個樹中的中間節(jié)點氧卧。)
                stack.push(pCurrent);
                pCurrent = pCurrent->left;//若左節(jié)點為空則沙绝,該節(jié)點可以等同為中間節(jié)點,相對于其右節(jié)點先出棧(后進棧)
            }//節(jié)點為空就出棧
            else
            {//當左子節(jié)點或右子節(jié)點沒有左子節(jié)點時 改節(jié)點出棧
                TreeNode *pNode = stack.top();
                vector.push_back(pNode->val);
                stack.pop();
                pCurrent = pNode->right;
            }
        }
        return vector;
    }
};

由于篇幅所限前序遍歷和后序遍歷的代碼就不貼了星著,可去這個地方查看:
https://github.com/kid1943/leetcode/tree/master/tree/Binary%20Tree%20Preorder%20Traversal

https://github.com/kid1943/leetcode/tree/master/tree/Binary%20Tree%20Postorder%20Traversal

逐層遍歷

  • 自上而下
class Solution {
public:
    vector<int> traverse(TreeNode* root) {
        queue<TreeNode*> q;
        queue<int> level;
        
        q.push(root);
        level.push(0);
        
        vector<int> vec;
        int m = -1;
        int result;
        while(q.size()){
          TreeNode* sr = q.front();
          result = sr->val;
          int l = level.front();
          level.pop();
          q.pop();  
          
          if(sr->left){
              q.push(sr->left);
              level.push(l+1);
          }
          
          if(sr->right){
              q.push(sr->right);
              level.push(l+1);
          }
        }
        return level;
    }
};

節(jié)點的刪除

要刪除二叉搜索樹中的某個節(jié)點p需要考慮三種情況:
1)p有兩顆非空子樹
2)p是樹葉
3)p是只有一顆非空子樹

刪除p節(jié)點的思路
1)要刪除的節(jié)點p具有兩顆非空子樹虚循。先將該節(jié)點的元素替換為它的左子樹的最大元素或右子樹的最小元素样傍。
2)要刪除的節(jié)點是葉子節(jié)點 。處理的方法是釋放該節(jié)點空間茎刚,若是根節(jié)點撤逢,則令根為NULL。
3 ) 要刪除的節(jié)點p只有一顆子樹初狰。如果p沒有節(jié)點(即p是根節(jié)點)互例,則p的唯一子樹的根節(jié)點成為新的搜索樹的根節(jié)點。如果p有父節(jié)點pp俊马,則修改pp的指針域肩杈,使得它指向p的唯一孩子,然后釋放節(jié)點p扩然。

  • 以下是我實現(xiàn)的代碼
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        root = deleteNodeitem(root, key);
        return root; 
    }

    TreeNode* deleteNodeitem(TreeNode* root, int key){
        TreeNode* p = root;//the keynode to delete
        TreeNode* pp = root;//parentNode of the keynode
        TreeNode* s;//the node to replace the keynode
        TreeNode* ps;//parent node of replacenode

        //findout the keynode p and its parant node pp
        while((p != NULL)&&(p->val != key)){
            pp = p;
            if(key > p->val){
               p = p->right;    
            }else if(key < p->val){
               p = p->left;
            }
        }
        //can not find the keynode
        if(p == NULL){
            return root;
         }

        //state1 node p have 2 nonull childnode
        if((p->left!=NULL)&&(p->right!=NULL)){
            //find the biggest node in the right tree
            s = p->right;
            ps = p;
            while(s->left != NULL){
                ps = s;
                s = s->left;
            }
           //初始化一個要替換刪除節(jié)點的節(jié)點
           TreeNode q = {s->val};
           q.left = p->left; 
           q.right = p->right;
           //將刪除節(jié)點指向新構(gòu)造出來的節(jié)點q
           if(pp->left == p){
                pp->left = &q;
            }else if(pp->right == p){
                pp->right = &q;
            }else if(p == root){//要刪除的節(jié)點就是根節(jié)點
                pp->val = q.val;
            }

            //make the s node to delete 找出節(jié)點s所對應的父節(jié)點
            if(ps != p){
                pp = ps;
            }else if((p == ps)&&(p!=root)){//當s的父節(jié)點即為p節(jié)點時
                pp = &q;
            }
            p = s;//it must one left child of p if p has child
        }

        //statue2 node p have at most one nonull childnode
       //在只有一個非空子樹的情況下界睁,只需要將刪除子樹的非空子樹將其替換就可以了
        TreeNode* pc = p;//the child of p , if it has. 
        if(p->left != NULL){
            pc = p->left;
        }else if(p->right != NULL){
            pc = p->right;
        }else{
            pc = NULL;//p has no child
        }

        if(pp->left == p){
            pp->left = pc;
        }else if(pp->right == p){
            pp->right = pc;
        }else{
            root = pc;//根節(jié)點就是需要刪除的節(jié)點
        }
        return root;
    }
};
  • 利用遞歸
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (!root) return nullptr;
        if (root->val == key) {
            if (!root->right) {
                TreeNode* left = root->left;
                delete root;
                return left;
            }
            else {
                TreeNode* right = root->right;
                //找出右子樹的最小節(jié)點  
                while (right->left)
                    right = right->left;
                swap(root->val, right->val);    
            }
        }
       //利用遞歸找出左右子樹中要刪除的節(jié)點翻斟,
       //并利用該節(jié)點的右子樹中的左子樹的最小節(jié)點替換掉要被刪除的節(jié)點
        root->left = deleteNode(root->left, key);
        root->right = deleteNode(root->right, key);
        return root;
    }
};

題目思路整理

  • Find Bottom Left Tree Value 逐層遍歷
  • Find Largest Value in Each Tree Row 逐層遍歷或前序遍歷
  • Find Largest Value in Each Tree Row 利用中序遍歷可以得到由小到大的集合
  • Maximum Depth of Binary Tree 后序遍歷或逐層遍歷
  • Most Frequent Subtree Sum 中序遍歷,計算每個節(jié)點的值及其左右子樹的值之和
  • Invert Binary Tree 后序遍歷
  • Binary Tree Tilt 后序遍歷求和
  • Sum of Left Leaves 前序遍歷嘹履,找出左子葉
  • Same Tree 后序或前序遍歷節(jié)點進行比較
  • Binary Tree Inorder Traversal 中序遍歷的實現(xiàn)

未完待續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末债热,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窒篱,更是在濱河造成了極大的恐慌,老刑警劉巖墙杯,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異荐绝,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門召夹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人监憎,你說我怎么就攤上這事【ɡ” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵类少,是天一觀的道長。 經(jīng)常有香客問我硫狞,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任财忽,我火速辦了婚禮泣侮,結(jié)果婚禮上即彪,老公的妹妹穿的比我還像新娘活尊。我一直安慰自己,他們只是感情好酬凳,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宁仔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翎苫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天攘蔽,我揣著相機與錄音,去河邊找鬼满俗。 笑死,一個胖子當著我的面吹牛唆垃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痘儡,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渐尿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起砖茸,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脯倚,失蹤者是張志新(化名)和其女友劉穎渔彰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恍涂,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年尼夺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淤堵。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡顷扩,死狀恐怖拐邪,靈堂內(nèi)的尸體忽然破棺而出隘截,到底是詐尸還是另有隱情扎阶,我是刑警寧澤婶芭,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站犀农,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呵哨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一孟害、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纹坐,春花似錦舞丛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吨凑。三九已至户辱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糙臼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工必逆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人揽乱。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像凰棉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子撒犀,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容