二叉樹學習筆記

二叉樹

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

二叉樹的序列化及反序列化

序列化二叉樹

//==================================================
// 序列化二叉樹
//==================================================
void serializeBTree(TreeNode* pRoot, ostream& out, char mark, char separator) {
    if(NULL == pRoot) {
        out << mark << separator;
        return;
    }
    out << pRoot->val << separator;
    serializeBTree(pRoot->left, out, mark, separator);
    serializeBTree(pRoot->right, out, mark, separator);
}
/* 序列化二叉樹
 * pRoot:要序列化的二叉樹
 * mark:葉子節點下的 NULL 指針標記符(默認爲 #)
 * separator:分隔符(默認爲空格)
 */
string Serialize(TreeNode *pRoot, char mark, char separator) {//【序列化二叉樹】
    ostringstream os;
    if(NULL != pRoot) {
        serializeBTree(pRoot, os, mark, separator);
    }
    return os.str();
}

反序列化二叉樹

//==================================================
// 反序列化二叉樹
//==================================================
// 使用流
bool readStream(istream& in, int& num, char mark, char separator) {
    if(' ' == separator) {// 空格分割的情況
        string str;
        in >> str;
        if("#" == str)
            return false;
        num = atoi(str.c_str());
        return true;
    } else {// 其他字符分割的情況
        char ch;
        string s = "";
        in >> ch;
        while(ch != separator) {
            s += ch;
            in >> ch;
        }
        if(s[0] != mark) {
            num = atoi(s.c_str());
            return true;
        }
        return false;
    }
}
void deserializeBTree(TreeNode* &pRoot, istream& in, char mark, char separator) {// 使用流
    int num;
    if(readStream(in, num, mark, separator)) {
        pRoot = new TreeNode(num);
        deserializeBTree(pRoot->left, in, mark, separator);
        deserializeBTree(pRoot->right, in, mark, separator);
    }
}
// 使用 string
bool readString(string& str, int& num, char mark, char separator) {
    string::size_type index = str.find_first_of(separator);
    if(string::npos != index) {
        string s = str.substr(0, index);
        str = str.substr(index+1);
        if(s[0] != mark) {
            num = atoi(s.c_str());
            return true;
        }
    }
    return false;
}
void deserializeBTree(TreeNode* &pRoot, string& str, char mark, char separator) {// 使用 string
    int num;
    if(readString(str, num, mark, separator)) {
        pRoot = new TreeNode(num);
        deserializeBTree(pRoot->left, str, mark, separator);
        deserializeBTree(pRoot->right, str, mark, separator);
    }
}
/* 反序列化二叉樹
 * str:string 型的序列
 * mark:葉子節點下的 NULL 指針標記符(默認爲 #)
 * separator:分隔符(默認爲空格)
 */
TreeNode* Deserialize(string sequence, char mark, char separator){//【反序列化二叉樹】
    TreeNode* pRoot = NULL;

//    istringstream in(sequence);
//    deserializeBTree(pRoot, in, mark, separator);// 使用流

    deserializeBTree(pRoot, sequence, mark, separator);// 使用 string

    return pRoot;
}

二叉樹的遍歷(遞歸)

//==================================
//        二叉樹遍歷(遞歸)
//==================================
void PreorderTravel(TreeNode *pRoot) {// 【先序遍歷(遞歸)】
    if(pRoot) {
        cout << pRoot->val << " ";
        PreorderTravel(pRoot->left);
        PreorderTravel(pRoot->right);
    }
}

void InorderTravel(TreeNode *pRoot) {// 【中序遍歷(遞歸)】
    if(pRoot) {
        InorderTravel(pRoot->left);
        cout << pRoot->val << " ";
        InorderTravel(pRoot->right);
    }
}

void PostorderTravel(TreeNode *pRoot) {// 【後序遍歷(遞歸)】
    if(pRoot) {
        PostorderTravel(pRoot->left);
        PostorderTravel(pRoot->right);
        cout << pRoot->val << " ";
    }
}

二叉樹的遍歷(非遞歸)

//==================================
//        二叉樹遍歷(非遞歸)
//==================================
void PreorderNonRecursive(TreeNode *pRoot) { // 【前序遍歷(非遞歸)】
    stack<TreeNode*> s;
    TreeNode *p = pRoot;
    while(p != NULL || !s.empty()) {
        while(p != NULL) {
            cout << p->val << " ";// 根
            s.push(p);
            p = p->left;// 左
        }
        if(!s.empty()) {
            p = s.top();
            s.pop();
            p = p->right;// 右
        }
    }
}

void InorderNonRecursive(TreeNode *pRoot) { // 【中序遍歷(非遞歸)】
    stack<TreeNode*> s;
    TreeNode *p = pRoot;
    while(p != NULL || !s.empty()) {
        while(p != NULL) {
            s.push(p);
            p = p->left;// 左
        }
        if(!s.empty()) {
            p = s.top();
            cout << p->val << " ";// 根
            s.pop();
            p = p->right;// 右
        }
    }
}
/* 後序遍歷:要保證根結點在左孩子和右孩子訪問之後才能訪問,因此對於任一結點 p,先將其入棧。如果P不存在左孩子和右孩子,則可以直接訪問它;
 * 或者 p 存在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。
 * 若非上述兩種情況,則將 p 的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。*/
void PostorderNonRecursive(TreeNode *pRoot) { // 【後序遍歷(非遞歸)】
    stack<TreeNode*> s;
    TreeNode *cur;// 當前結點
    TreeNode *pre = NULL;// 前一次訪問的結點
    s.push(pRoot);
    while(!s.empty()) {
        cur = s.top();
        if((cur->left == NULL && cur->right == NULL) || // 如果當前結點沒有孩子結點
                (pre != NULL && (pre == cur->left || pre == cur->right))// 如果有孩子,因爲孩子們是先入棧的,只要有一個孩子訪問了,肯定是孩子們都被訪問了(因爲他們比當前結點先入棧的)
          ) {
            cout << cur->val << " ";
            s.pop();
            pre = cur;
        } else {
            if(cur->right != NULL)// 右孩子先入棧,這樣,每次取棧頂元素的時候,左孩子在右孩子前面被訪問
                s.push(cur->right);
            if(cur->left != NULL)
                s.push(cur->left);
        }
    }
}

按層打印二叉樹

從上往下打印二叉樹(層序遍歷不分行)

vector<int> TravelFromTopToBottom(TreeNode *pRoot) {// 【從上往下打印二叉樹】
    vector<int> result;
    if(NULL == pRoot) return result;
    queue<TreeNode*> myQueue;
    myQueue.push(pRoot);
    while(!myQueue.empty()) {
        TreeNode *p = myQueue.front();
        result.push_back(p->val);
        myQueue.pop();
        if(p->left)
            myQueue.push(p->left);
        if(p->right)
            myQueue.push(p->right);
    }
    return result;
}

把二叉樹打印成多行(層序遍歷分行)

vector<vector<int> > LevelTravel(TreeNode* pRoot) { // 【把二叉樹打印成多行】
    vector<vector<int> > result;
    if(pRoot != NULL) {
        queue<TreeNode*> q;
        q.push(pRoot);
        int levelWith = 0;
        int n = 1;
        vector<int> v;
        while(!q.empty()) {
            TreeNode* p = q.front();
            v.push_back(p->val);
            if(p->left) {
                q.push(p->left);
                ++levelWith;
            }
            if(p->right) {
                q.push(p->right);
                ++levelWith;
            }
            q.pop();
            --n;
            if(0 == n) {
                result.push_back(v);
                v.resize(0);
                n = levelWith;
                levelWith = 0;
            }
        }
    }
    return result;
}

按之字形順序打印二叉樹

vector<vector<int> > ZigzagTravel(TreeNode* pRoot) {// 【按之字形順序打印二叉樹】
    vector<vector<int> > result;
    if(NULL == pRoot) return result;
    stack<TreeNode*> s1/*從右到左壓入*/, s2/*從左到右壓入*/;
    s1.push(pRoot);
    vector<int> vec;
    while(!s1.empty() || !s2.empty()) {
        while(!s1.empty()) {
            TreeNode* node = s1.top();
            vec.push_back(node->val);
            if(node->left)
                s2.push(node->left);
            if(node->right)
                s2.push(node->right);
            s1.pop();
        }
        result.push_back(vec);
        vec.resize(0);
        while(!s2.empty()) {
            TreeNode* node = s2.top();
            vec.push_back(node->val);
            if(node->right)
                s1.push(node->right);
            if(node->left)
                s1.push(node->left);
            s2.pop();
        }
        result.push_back(vec);
        vec.resize(0);
    }
    return result;
}

重建二叉樹

根據前序和中序重建二叉樹

TreeNode* constrcutBT(const vector<int>& pre, vector<int>::size_type preLow, vector<int>::size_type preHigh,
                      const vector<int>& in, vector<int>::size_type inLow, vector<int>::size_type inHigh) {
    // 前序的第一個是根
    int rootValue = pre[preLow];
    TreeNode* tree = new TreeNode(rootValue);

    if(0 == preHigh-preLow && 0 == inHigh-inLow && pre[preLow] == in[inLow])
        return tree;

    // 在中序裏面找到這個根的位置
    vector<int>::size_type i = inLow;
    for(; i != inHigh; i++) {
        if(rootValue == in[i])
            break;
    }

    if(i > inLow) {// 重建左子樹
        vector<int>::size_type in_L_Low = inLow, in_L_High = i - 1;// 左子樹的中序起始點
        vector<int>::size_type pre_L_Low = preLow + 1, pre_L_High = preLow + (i - inLow);// 左子樹的前序起始點
        tree->left = constrcutBT(pre, pre_L_Low, pre_L_High, in, in_L_Low, in_L_High);
    }
    if(i < inHigh) {// 重建右子樹
        vector<int>::size_type in_R_Low = i + 1, in_R_High = inHigh;// 右子樹的中序起始點
        vector<int>::size_type pre_R_Low = preLow + (i - inLow) + 1, pre_R_High = preHigh;// 右子樹的前序起始點
        tree->right = constrcutBT(pre, pre_R_Low, pre_R_High, in, in_R_Low, in_R_High);
    }
    return tree;
}
struct TreeNode* ReConstructBinaryTree(vector<int> pre, vector<int> in) {// 【根據前序和中序重建二叉樹】
    if(pre.empty() || in.empty() || pre.size() != in.size())
        return NULL;
    return constrcutBT(pre, 0, pre.size()-1, in, 0, in.size()-1);
}

判斷二叉樹是否是對稱二叉樹

bool isSymm(TreeNode* pRoot1, TreeNode* pRoot2) {
    if(NULL == pRoot1 && NULL == pRoot2) return true;
    if(NULL == pRoot1 || NULL == pRoot2) return false;
    if(pRoot1->val != pRoot2->val) return false;
    return (isSymm(pRoot1->left, pRoot2->right) && isSymm(pRoot1->right, pRoot2->left));
}
bool IsSymmetrical(TreeNode* pRoot) {// 【對稱二叉樹】
    return isSymm(pRoot, pRoot);
}

二叉樹的鏡像(對換左右子樹)

void Mirror(TreeNode *pRoot) {// 【二叉樹的鏡像(對換左右子樹)】
    if(pRoot != NULL) {
        TreeNode *p = pRoot->left;
        pRoot->left = pRoot->right;
        pRoot->right = p;
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
}

判斷樹的子結構

bool hasST(TreeNode *pRoot1, TreeNode *pRoot2) {// 判斷樹1中以此節點爲根節點的子樹是不是包含數2一樣的結構
    if(NULL == pRoot2) return true;// 遞歸終止條件
    if(NULL == pRoot1) return false;
    if(pRoot1->val != pRoot2->val) return false;
    return (hasST(pRoot1->left, pRoot2->left) && hasST(pRoot1->right, pRoot2->right));
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {// 【判斷樹 pRoot2 是不是樹 pRoot1 的子結構】
    bool flag = false;
    if(pRoot1 != NULL && pRoot2 != NULL) {
        if(pRoot1->val == pRoot2->val)// 在樹1中找到和樹2的根節點的值一樣的節點
            flag = hasST(pRoot1, pRoot2);// 判斷樹1中以此節點爲根節點的子樹是不是包含數2一樣的結構
        if(!flag)
            flag = HasSubtree(pRoot1->left, pRoot2);// 在左子樹中查找
        if(!flag)
            flag = HasSubtree(pRoot1->right, pRoot2);// 在右子樹中查找
    }
    return flag;
}

判斷二叉樹是否是平衡二叉樹

bool isBalanced(TreeNode* pRoot, int& depth) {
    if(!pRoot) {
        depth = 0;
        return true;
    }
    int leftDepth, rightDepth;
    // 如果左右子樹是平衡的,則計算當前節點作爲根節點的樹是否是平衡的
    if(isBalanced(pRoot->left, leftDepth) && isBalanced(pRoot->right, rightDepth)) {
        int d = abs(leftDepth - rightDepth);
        if(d <= 1) {
            depth = leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
            return true;
        }
    }
    return false;
}
bool IsBalanced(TreeNode* pRoot) {// 【判斷二叉樹是否是平衡二叉樹】
//    // 方案一
//    if(!pRoot)
//        return true;
//    int leftDepth = TreeDepth(pRoot->left);// 計算左子樹深度
//    int rightDepth = TreeDepth(pRoot->right);// 計算右子樹深度
//    int d = abs(leftDepth - rightDepth);// 左右子樹深度差
//    if(d > 1)// 如果深度差超過 1 則不是平衡二叉樹
//        return false;
//    return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);

    // 方案二(方案一中一個結點會被重複遍歷多次,效率不高。這裏方案二採用後序遍歷的方式,每個節點只訪問一次)
    int depth;
    return isBalanced(pRoot, depth);
}

二叉樹的深度

//void getTreeDepth(TreeNode* p, int& depth, int& maxDepth) {
//    if(p) {
//        depth++;
//        if(!p->left && !p->right) {// 如果到了葉子節點,則更新深度值
//            if(depth > maxDepth)
//                maxDepth = depth;
//        }
//        else {
//            if(p->left) {
//                getTreeDepth(p->left, depth, maxDepth);
//                depth--;
//            }
//            if(p->right)
//                getTreeDepth(p->right, depth, maxDepth);
//        }
//    }
//}
//int TreeDepth(TreeNode* pRoot) {//【二叉樹的深度】方案一
//    int depth = 0, maxDepth = 0;
//    getTreeDepth(pRoot, depth, maxDepth);
//    return maxDepth;
//}
int TreeDepth(TreeNode* pRoot) {//【二叉樹的深度】方案二
    if(!pRoot) return 0;
    int leftDepth = TreeDepth(pRoot->left);// 計算左子樹深度
    int rightDepth = TreeDepth(pRoot->right);// 計算右子樹深度
    return (leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1);// 樹的深度爲較大者深度 + 1
}

二叉樹中和爲某一值的路徑

void findPath(TreeNode* root, int& curSum, const int& expectNumber, vector<int>& path, vector<vector<int> >& paths) {
    if(root) {
        curSum += root->val;
        path.push_back(root->val);
        if(NULL == root->left && NULL == root->right && curSum == expectNumber)
            paths.push_back(path);
        if(root->left)
            findPath(root->left, curSum, expectNumber, path, paths);
        if(root->right)
            findPath(root->right, curSum, expectNumber, path, paths);
        curSum -= root->val;
        path.pop_back();
    }
}
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {// 【二叉樹中和爲某一值的路徑】
    vector<vector<int> > paths;
    if(NULL == root) return paths;
    int curSum = 0;
    vector<int> path;
    findPath(root, curSum, expectNumber, path, paths);
    return paths;
}

二叉搜索樹

判斷一個序列是不是二叉搜索樹的後序遍歷序列

bool VerifySquenceOfBST(vector<int> sequence) {// 【判斷一個序列是不是二叉搜索樹的後序遍歷序列】
    if(sequence.empty()) return false;
    int rootValue = sequence[sequence.size()-1];// 根節點
    vector<int>::size_type length = 0, i = 0;
    for(; i < sequence.size()-1; i++)// 尋找根節點左右子樹分割點
        if(sequence[i] > rootValue)
            break;
    length = i;// 左子樹節點數
    for(++i; i < sequence.size()-1; i++)// 判斷右子樹節點中,是否有小於根節點值的
        if(sequence[i] < rootValue)
            return false;
    bool L_flag = true, R_flag = true;
    if(length > 0) {// 遞歸判斷左子樹
        vector<int> vecL(sequence.begin(), sequence.begin()+length);
        L_flag = VerifySquenceOfBST(vecL);
    }
    if(length < sequence.size()-1) {// 遞歸判斷右子樹
        vector<int> vecR(sequence.begin()+length, sequence.end()-1);
        R_flag = VerifySquenceOfBST(vecR);
    }
    return (L_flag && R_flag);
}

二叉搜索樹轉爲雙向鏈表

void convertTree2List(TreeNode* p, TreeNode* &pList) {
    if(p) {
        if(p->left)
            convertTree2List(p->left, pList);// 轉換左子樹

        p->left = pList;// 將此根節點左邊指向雙向鏈表的最右邊
        if(pList)
            pList->right = p;// 雙向鏈表的最右邊的節點指向此根節點
        pList = p;// 更新 pList 指向雙向鏈表的最右邊

        if(p->right)
            convertTree2List(p->right, pList);// 轉換右子樹
    }
}
TreeNode* Convert(TreeNode* pRootOfTree) {// 【二叉搜索樹轉爲雙向鏈表】
    if(pRootOfTree) return NULL;
    TreeNode *pList/*指向雙向鏈表最右邊那個節點*/ = NULL, *pListHead/*雙向鏈表*/ = NULL;
    convertTree2List(pRootOfTree, pList);
    pListHead = pList;
    while(pListHead && pListHead->left)// 因爲 pList 指向的是雙向鏈表的最右邊,所以反向遍歷到最左邊得到表頭
        pListHead = pListHead->left;
    return pListHead;
}

二叉搜索樹的第 k 個結點

給定一顆二叉搜索樹,請找出其中的第k小的結點。例如, 5 3 2 # # 4 # # 7 6 # # 8 # #,按結點數值大小順序第三個結點的值爲 4 
bool FindKthOfBST(TreeNode* pRoot, int& k, int& val) {//【查找二叉搜索樹的第 k 小的結點,若找到返回 true,並用 val 存放這個結點的值】
    // 用中序遍歷即可從小到大訪問二叉搜索樹的結點

    bool isFind = false;
    if(pRoot != NULL) {
        // 左
        isFind = FindKthOfBST(pRoot->left, k, val);
        // 根
        k--;
        if(0 == k) {
            val = pRoot->val;
            isFind = true;
        }
        // 右
        if(!isFind)
            isFind = FindKthOfBST(pRoot->right, k, val);
    }
    return isFind;
}

未完待續。。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章