二叉樹相關練習題(C++)

一、二叉樹
1、用遞歸方法實現二叉樹的先序、中序、後序遍歷
class TreeToSequence {
public:     
     void preOrder(TreeNode* root,vector<int> &pre) {
         if (!root)
             return;
         pre.push_back(root->val);
         preOrder(root->left,pre);
         preOrder(root->right,pre);      
    }
 
    void inOrder(TreeNode* root,vector<int> &in) {
        if (!root)
            return;
        inOrder(root->left,in);
        in.push_back(root->val);
        inOrder(root->right,in);
    }
 
    void postOrder(TreeNode* root,vector<int> &post) {
        if (!root)
            return;
        postOrder(root->left,post);
        postOrder(root->right,post);
        post.push_back(root->val);
    }
 
    vector<vector<int> > convert(TreeNode* root) {
        vector<vector<int> > vRecur;
        vector<int> pre,in,post;
 
        preOrder(root,pre);
        inOrder(root,in);
        postOrder(root,post);
         
        vRecur.push_back(pre);
        vRecur.push_back(in);
        vRecur.push_back(post);
        return vRecur;
    }
};

2、用非遞歸方法實現二叉樹的先序、中序、後序遍歷
class TreeToSequence {
public
:
    vector<int> preOrder(TreeNode* root) {
        vector<int> pre;
        stack<TreeNode*> s;
        s.push(root);
        while (!s.empty()) {
            TreeNode* temp = s.top();
            pre.push_back(temp->val);
            s.pop();            
            if (temp->right)
                s.push(temp->right);
            if (temp->left)
                s.push(temp->left);
        }
        return pre;
    }

    vector<int> inOrder(TreeNode* root) {
        vector<int> in;
        stack<TreeNode*> s;
        TreeNode* temp = root;

        while (temp || !s.empty()){            
            while (temp) {
                s.push(temp);
                temp = temp->left;
            }
            temp = s.top();
            s.pop();
            in.push_back(temp->val);
            temp = temp->right;
        }
        return in;
    }

    vector<int> postOrder(TreeNode* root) {
        vector<int> post;
        stack<TreeNode*> s;
        TreeNode *temp = root,*previsited = NULL;

        while (temp || !s.empty()){            
            while (temp) {
                s.push(temp);
                temp = temp->left;
            }
            temp = s.top();
            if (!temp->right || temp->right == previsited) {
                post.push_back(temp->val);
                s.pop();
                previsited = temp;
                temp = NULL;
            }
            else
 
                temp = temp->right;          
        }
        return post;
    }

    vector<vector<int> > convert(TreeNode* root) {

        vector<vector<int> > vNonRecur;
        if (!root)
            return vNonRecur;
        vNonRecur.push_back(preOrder(root));
        vNonRecur.push_back(inOrder(root));
        vNonRecur.push_back(postOrder(root));
        return vNonRecur;
    }
};

3、按照層次打印二叉樹,且不同層的數據要打印在不同行。可把不同層的數據存在不同的數組中模擬打印過程。
解法:①二叉樹的層序遍歷要用到隊列。隊頭結點出隊則接着將其左右子節點入隊。
           ②不同層打印在不同行就要考慮在合適的節點處換行。定義兩個指針curLast和nexLast分別記錄正在打印的行的最右結點和下一行的最右結點。nexLast跟蹤入隊結點,則當打印結點是當前層最右結點curLast時緊接着的入隊結點也是下一行的最右結點;若當前打印的結點爲curLast則換行,接着將下一行最右結點指針nexLast賦值給curLast。

class TreePrinter {
public
:
    vector<vector<int> > printTree(TreeNode* root) {
        vector<vector<int> > res;
        vector<int> temp;
        queue<TreeNode*> q;
        TreeNode *curLast = root,*nexLast = NULL,*cur;
        q.push(root);

        while (!q.empty()) {
            cur = q.front();
            temp.push_back(cur->val);
            q.pop();                        
            if (cur->left) {
                q.push(cur->left);
                nexLast = cur->left;    
            }
            if (cur->right) {
                q.push(cur->right);
                nexLast = cur->right;
            }
            if (cur == curLast) {
                res.push_back(temp);
                temp.clear();
                curLast = nexLast;
            }        
        }
        return res;
    }
};

4、二叉樹的先序序列化
解法:用遞歸先序遍歷更簡單,遇到空則在字符串str後追加#!,非空則追加‘數值!’。
注意:int型數據要先轉換成字符串類型才能追加到字符串末尾,用stringstream可以實現轉換。
class TreeToString {
public
:
    void preSerial(TreeNode* root,string &str) {
        if (!root) {
            str += "#!";
            return;
        }            
        str += int2string(root->val) + '!'; 
        preSerial(root->left,str);
        preSerial(root->right,str);
    }

    string int2string(int val) {
        stringstream ss;
        ss << val;
        return ss.str();
    }

    string toString(TreeNode* root) {
        string str;
        preSerial(root,str);
        return str;
    }
};


5、二叉樹先序反序列化
解法:①先將字符串序列轉換成字符數組的形式。②遍歷字符數組,遇到不是#的元素,則新建結點,並用剩下的元素構造這個結點的左子樹;遇到#則不新建結點,並且當前正在構建的分支結束,用剩下的元素構造當前分支的父節點的右子樹。

6、判斷二叉樹是否爲平衡二叉樹
平衡二叉樹:任何結點的左右子樹高度之差都不大於1.
解法:利用二叉樹的後序遍歷,統計左右子樹的高度,同時通過一個全局的bool變量記錄遍歷過程中是否出現非平衡二叉樹,一旦bool被置爲false則說明有子樹不是平衡二叉樹,直接返回,最後返回這個bool變量即可。
class CheckBalance {
public:
    int postVisit(TreeNode* root,bool &isBalance ) {
        // 若當前結點空,則直接返回0,即當前子樹的高度不增加
        if (!root)
            return 0;  
        // 若前結點不空,則統計其左子樹的高度 
        int nL = postVisit(root->left,isBalance ); 
        // 如果在遍歷當前節點的左子樹的過程中出現非平衡二叉樹,此時不再遍歷右子樹,直接返回
        if (!isBalance )
            return nL; 
        // 若當前結點不空且左子樹是平衡二叉樹,則遍歷右子樹,統計其高度
        int nR = postVisit(root->right,isBalance );
        // 如果在遍歷右子樹的過程中出現非AVL,此時不再判斷當前結點爲根的子樹是否是平衡二叉樹,直接返回
        if (!isBalance )
            return nR;
        // 如果當前節點的左右子樹都是平衡二叉樹,則根據左右子樹的高度判斷當前結點爲根的子樹是否是平衡二叉樹 
         if (abs(nL-nR) > 1)
             isBalance false;
        // 不管當前結點爲根的子樹是不是平衡二叉樹,都應該使子樹的高度增加1 
         return 1+max(nL,nR); 
     }
     bool check(TreeNode* root) {
         bool isBalance = true;       
         int height = postVisit(root,isBalance );
         return isBalance ;
     }
};

7、判斷二叉樹是不是二叉搜索樹
解法:二叉樹的中序遍歷,若得到的遍歷序列是遞增的則原二叉樹是二叉搜索樹。
bool checkSearch(TreeNode* root) {
        stack<TreeNode*> s;
        TreeNode *cur = root,*last =NULL;
         while(cur || !s.empty()) {
            while(cur) {
                  s.push(cur);
                  cur = cur->left;   
            }
            cur = s.top();
            if(!last)
                last = cur;
            if(cur->val >= last->val) {
                s.pop();
                cur = cur->right;
                last = cur;
            }
           else
               return false;            
        }
       return true;
}

8、判斷二叉樹是不是完全二叉樹
解法:利用層序遍歷。如果當前結點左孩子空右孩子不空,則肯定不是完全二叉樹;如果左右孩子都不空,則正常遍歷;如果左孩子不空右孩子空,則在完全二叉樹中這個左孩子一定是最後一層最右的結點,並且這種情況只可能發生一次,用n來記錄這種情況發生的次數,n>1則返回false。
class CheckCompletion {
public:
    bool chk(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);
        int n = 0;
        while (!q.empty()) {
            TreeNode* temp = q.front();
            q.pop();
            if (!temp->left && temp->right)
                return false;
            if (temp->left && temp->right) {
                q.push(temp->left);
                q.push(temp->right);           
            }
            if (temp->left && !temp->right) {
                if (temp->left->left || temp->left->right)
                    return false;
                n++;
                if (n > 1)
                    return false;
                q.push(temp->left);              
            }               
        }
        return true;
    }
};


9、摺紙問題
描述:把紙條豎着放在桌上,然後從紙條的下邊向上對摺,壓出摺痕後再展開。此時有1條摺痕,突起的方向指向紙條的背面,這條摺痕叫做“下”摺痕 ;突起的方向指向紙條正面的摺痕叫做“上”摺痕。如果每次都從下邊向上邊 對摺,對摺N次。請從上到下計算出所有摺痕的方向。
解法:摺痕方向的分佈其實是一棵滿二叉樹。根節點是0,所有結點的左右孩子都分別爲0,1。0代表“下”摺痕,1代表“上”摺痕。可以用遞歸算法來實現。也可以用非遞歸方式實現。
遞歸解法:
class FoldPaper {
public:
    vector<string> foldPaper(int n) {
        vector<string> ret;
        foldPrint("down", ret, n);
        return ret;
    }
    void foldPrint(string fold, vector<string>& ret, int remain){
        if (remain == 0)
            return;
        foldPrint("down", ret, remain - 1);
        ret.push_back(fold);
        foldPrint("up", ret, remain - 1);
    }
};
非遞歸解法:
class FoldPaper {
public:     
    vector<string> foldPaper(int n) {
        string sLeft[] = {"down","down","up"};
        string sRight[] = {"down","up","up"};
        vector<string> lS(sLeft,sLeft+3);
        vector<string> rS(sRight,sRight+3);         
        vector<string> last,odd,even = lS;
        odd.push_back("down");                
        if (n == 1) { 
            return odd;
        }
        if (n == 2) {
            return even;
        }         
        int i;
        if (n%2== 1) {
            i = 3;
            last = odd;
        }             
        else {
            i = 4;
            last = even;
        }             
        vector<string> res;
        vector<string>::iterator iter;
        for (;i <= n;) {
            iter = last.begin();            
            while (iter < last.end()) {
                res.insert(res.end(),lS.begin(),lS.end());
                res.push_back(*iter);
                res.insert(res.end(),rS.begin(),rS.end());
                if (iter < last.end()-1)
                    res.push_back(*(iter+1));
                iter = iter+2;
            }             
            last.swap(res);
            res.clear();
            i = i+2;
        }
        return last;         
    }
};

10、一棵二叉樹原本是搜索二叉樹,但是其中有兩個節點調換了位置,使得這棵二叉樹不再是搜索二叉樹,請找到這兩個錯誤節點並返回他們的值。小的值在前。
解法:二叉樹的中序遍歷可以檢查出樹是不是搜索樹。用last指針跟蹤上一個遍歷的結點就可以比較遍歷過程中是否出現降序。若調換了兩個結點則最多會出現兩次降序,若只有第一次,則降序的兩個結點就是調換的結點,若有兩次降序則第一次較大的結點和第二次較小的結點是調換的兩個結點。

class FindErrorNode {
public:
    vector<int> findError(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> res;
        TreeNode *cur = root,*last =NULL;
         while(cur || !s.empty()) {             
                while (cur) {
                s.push(cur);
                cur = cur->left;   
            }
            cur = s.top();
            s.pop();
            if(last && cur->val < last->val) {                
                if (res.empty()) {
                    res.push_back(cur->val);
                    res.push_back(last->val);
                }
                else {                    
                    res.erase(res.begin());
                    res.insert(res.begin(),cur->val);
                    break;
                }                            
            } 
            last = cur;
            cur = cur->right;                       
        }     
           return res;
    }
};

11、對於給定的一棵二叉樹,求整棵樹上節點間的最大距離。
最大距離的定義:從一個節點走到(可以向上走可以向下走)另一個節點需要經過的結點數(包括這兩個結點)。
解法:一棵樹的節點間最大距離只可能是一下三種情況。①左子樹中的最大距離;②右子樹中的最大距離;③左子樹中距離根節點最遠的結點和右子樹中距離根節點最遠的結點之間的距離。
         採用二叉樹的後序遞歸遍歷可是實現。在遞歸的每一層都求得以當前結點爲根節點的樹中的最大距離和這棵樹中距離根節點最遠的結點距離根節點的距離。

classLongestDistance {
public:     
    vector<int> postVisit(TreeNode* root) {
        vector<int> resL,resR,res;
        if (!root) { // 如果當前節點爲空,則以它爲根節點的樹中最大距離和最遠距離都爲0
            res.push_back(0);
            res.push_back(0);
            return res;
        }   
// 若不空則先後處理左右子樹,返回左右子樹的最大距離和最遠距離           
        resL = postVisit(root->left);
        resR = postVisit(root->right);
// 計算以當前結點爲根節點的樹的最大距離和最遠距離        
        int max1 = resL[0],max2;
        if (resR[0] > max1)
            max1 = resR[0];
        if (resL[1]+resR[1]+1> max1)
            max1 = resL[1]+resR[1]+1;
        max2 = resL[1]>resR[1]?resL[1]+1:resR[1]+1;
        res.push_back(max1);
        res.push_back(max2);
        return res;
    }
     
    int findLongest(TreeNode* root) {
        vector<int> max = postVisit(root);
        return max[0];
    }
};


12、尋找一顆二叉樹中節點數最多的搜索樹的根節點
解法一:只有兩種情況。①當前結點的左子樹的最大搜索樹以當前結點的左孩子爲根節點,右子樹的最大搜索樹以右孩子爲根節點,且左子樹的最大值小於根節點,右子樹的最小值大於根節點,則當前結點就是以其爲根節點的子樹的最大搜索樹,結點個數爲左右子樹中最大搜索樹結點個數之和加1,最大值爲右子樹的最大值,最小值爲左子樹的最小值;②第①種情況不滿足,則左右子樹中結點數最多的最大搜索樹爲當前的最大搜索樹,存在四種情況,如下代碼所示。

classMaxSubtree {
public:
    typedef struct {
        vector<int> num;
        TreeNode* rMax;
    } Dataset;
 
    Dataset postVisit(TreeNode* root) {
        Dataset datal,datar,data;
        if(!root) {
            data.num.insert(data.num.begin(),3,0);
            data.rMax = NULL;
            returndata;
        }         
        datal = postVisit(root->left);
        datar = postVisit(root->right);
         
        if(root->left && root->right) {
            if(datal.rMax == root->left &&
                datar.rMax == root->right &&
                datal.num[2] < root->val &&
                datar.num[1] > root->val) {
             
                data.rMax = root;               
                data.num.push_back(datal.num[0]+datar.num[0]+1);
                data.num.push_back(datal.num[1]<datar.num[1]?datal.num[1]:datar.num[1]);
                data.num.push_back(datal.num[2]<datar.num[2]?datar.num[2]:datal.num[2]);
            }
            else{
                if(datal.num[0]>datar.num[0])
                    data = datal;
                elseif(datal.num[0]<datar.num[0])
                    data = datar;
                else
                    data = datal.rMax->val > datar.rMax->val?datal:datar;                               
            }
        }              
        elseif(!root->left && !root->right) {
            data.rMax = root;
            data.num.push_back(datal.num[0]+datar.num[0]+1);
            data.num.push_back(root->val);
            data.num.push_back(root->val);
        }
        elseif(!root->left) {
            if(datar.num[1] > root->val && datar.rMax == root->right) {
                data.rMax = root;
                data.num.push_back(datal.num[0]+datar.num[0]+1);
                data.num.push_back(root->val);
                data.num.push_back(datar.num[2]);
            }               
            else
                data = datar;
        }
        else{
            if(datal.num[2] < root->val && datal.rMax == root->left) {
                data.rMax = root;
                data.num.push_back(datal.num[0]+datar.num[0]+1);
                data.num.push_back(datal.num[1]);
                data.num.push_back(root->val);
            }               
            else
                 data = datal;
        }
       returndata;
    }
     
    TreeNode* getMax(TreeNode* root) {
        Dataset resData = postVisit(root);
        returnresData.rMax;
    }
};


解法2:

classMaxSubtree {
public:
    bool chk[550];
    voidget(TreeNode* rt,int*mn,int*mx,int*sz,TreeNode *&ret,int&msz){
        intv = rt->val;
        mn[v] = 10101010; mx[v] = 0;
        chk[v] = false;
        intlsz = 0,rsz = 0,lmx = 0,rmx = 0,lmn = 10101010,rmn = 10101010;
        bool cl = true,cr = true;
        if(rt->left) get(rt->left,mn,mx,sz,ret,msz),lsz = sz[rt->left->val],lmn = mn[rt->left->val],lmx = mx[rt->left->val],cl = chk[rt->left->val];
        if(rt->right) get(rt->right,mn,mx,sz,ret,msz),rsz = sz[rt->right->val],rmn = mn[rt->right->val],rmx = mx[rt->right->val],cr = chk[rt->right->val];
        if(lsz != -1&& rsz != -1){
            if(cl && cr && lmx < v && v < rmn){
                inttmp = lsz + rsz + 1;
                chk[v] = true;
                if(tmp > msz || (tmp == msz && ret->val < v)){
                    ret = rt; msz = tmp;
                }
            }
            sz[v] = lsz + rsz + 1;
            mx[v] = max(v,lmx);
            mn[v] = min(v,lmn);
        }
    }
    TreeNode* getMax(TreeNode* root) {
        TreeNode* ret = NULL;
        intmn[550],mx[550],sz[550],msz = 0;
        get(root,mn,mx,sz,ret,msz);
        returnret;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章