考研——二叉树算法大题整理(二)

文章目录

一、表达式树

1. 求数值

无题目链接

// BTNode的val为char型变量
int calcul(BTNode* root)
{
    if (!root) return 0;
    if (!root->lchild && !root->rchild) return root->data - '0';
    else
    {
        int A = calcul(root->lchild);
        int B = calcul(root->rchild);
        return op(A, B, root->data);
    }
}

2. 求字符串

题目链接

// 方法1
void inorder(BTNode* root, int depth)
{
    if (!root) return;
    if (!root->lchild && !root->rchild) cout << root->data; // 叶子结点
    else 
    {
        if (depth > 1) cout << "(";
        inorder(root->lchild, depth + 1);
        cout << root->data;
        inorder(root->rchild, depth + 1);
        if (depth > 1) cout << ")";
    }
}
// 方法2
string res;
string expressionTree(BTNode* root)
{
    if (!root) return res;
    if (!root->lchild && !root->rchild) res += root->data;
    else {
        res += "(";
        expressionTree(root->lchild);
        res += root->data;
        expressionTree(root->rchild);
        res += ")";
    }
    return res.substr(1, res.size() - 2);
}

二、二叉树的深度与宽度(easy)

1. 二叉树的深度

题目链接
a. 递归方法

int maxDepth(BTNode* root)
{
    if (!root) return 0;
    int ldepth = maxDepth(root->lchild);
    int rdepth = maxDepth(root->rchild);
    return max(ldepth, rdepth) + 1;
}

b. 非递归方法(王道P149)

// 方法1
int maxDepth(BTNode* root) 
{
    if (!root) return 0;
    int depth = 0;
    queue<BTNode*> q;
    q.push(root);
    while (q.size())
    {
        depth ++;
        int len = q.size();
        for (int i = 0; i < len; i ++)
        {
            auto t = q.front();
            q.pop();
            if (t->lchild) q.push(t->lchild);
            if (t->rchild) q.push(t->rchild);
        }
    }
    return depth;
}
// 方法2
int maxDepth(BTNode* root)
{
    if (!root) return 0;
    int depth = 0;
    queue<BTNode*> q;
    q.push(root);
    while (q.size())
    {
        queue<BTNode*> tmp;
        depth ++;
        while (q.size())
        {
            auto t = q.front();
            q.pop();
            if (t->lchild) tmp.push(t->lchild);
            if (t->rchild) tmp.push(t->rchild);
        }
        q = tmp;
    }
    return depth;
}

2. 二叉树的宽度(同一层最左边到最右边非空结点的最大个数)

无题目链接

int widthOfBinaryTree(BTNode* root) {
        int max_width = 0;
        if (!root) return max_width;
        queue<BTNode*> q;
        q.push(root);
        while (q.size())
        {
            int len = q.size();
            if (len > max_width) max_width = len;
            for (int i = 0; i < len; i ++)
            {
                auto t = q.front();
                q.pop();
                if (t->lchild) q.push(t->lchild);
                if (t->rchild) q.push(t->rchild);
            }
        }
        return max_width;
    }
};

3. 某一结点值为x的深度

无题目链接

// 某一结点值为x的深度
int depth = 1;
void findXDepth(BTNode *root, char x)
{
	 if (!root) return;
	 if (root->data == x)
	 		cout << depth << endl;
	 depth ++;
	 findXDepth(root->lchild, x);
	 findXDepth(root->rchild, x);
	 depth --;
}
// 某一结点为p的深度
int getNodeDepth(BTNode *root, BTNode *p)
{
    if (!root) return 0;
    if (root == p) return 1;	// 遍历到p点就设其深度为1,跟第1句道理类似
    int ldepth = getNodeLevel(root->lchild, p);
    int rdepth = getNodeLevel(root->rchild, p);
    // 如果左子树或者右子树中有p点,则返回max(ldepth, rdepth) + 1
    // 如果左子树和右子树中没有p点,直接返回0
    if (ldepth || rdepth) return 1 + max(ldepth, rdepth);
    else return 0;
}

4. 给出二叉树自下而上、从右到左的BFS

无题目链接

void reverseOrder(BTNode *root) {
	 if (!root) return;
	 queue<BTNode*> q;
	 stack<BTNode*> s;
	 q.push(root);
	 while (q.size()) 
	 {
	    auto t = q.front();
		  q.pop();
       // 队头压栈,栈中得到逆层次遍历序列
       s.push(t);
       if (t->lchild) q.push(t->lchild);
       if (t->rchild) q.push(t->rchild);
    }
    while (s.size())
    {  // 打印栈中内容
       auto t = s.top();
       s.pop();
       cout<<t->data<<" ";
    }
}

三、找到第k个结点的值(easy)

1. 先序序列

无题目链接

int cnt = 0;
void pretrave(BTNode* root, int k)
{
    if (!root) return;
    if (++ cnt == k)
    {
        cout << root->data << endl;
        return;
    }
    trave(root->lchild, k);
    trave(root->rchild, k);
}

2. 中序序列

无题目链接

int cnt = 0;
void intrave(BTNode* root, int k)
{
    if (!root) return;
    trave(root->lchild, k);
    if (++ cnt == k)
    {
    	cout << root->data << endl;
    	return;
    }
    trave(root->rchild, k);
}

3. 后序序列

无题目链接

int cnt = 0;
void posttrave(BTNode* root, int k)
{
    if (!root) return;
    trave(root->lchild, k);
    trave(root->rchild, k);
    if (++ cnt == k)
    {
    	cout << root->data << endl;
    	return;
	}
}

四、重建二叉树

1. 先序和中序确定二叉树

题目链接

vector<int> pre, in;
    unordered_map<int, int> pos;
    BTNode* buildtree(int il, int ir, int pl, int pr)
    {
        int k = pos[pre[pl]];
        auto root = new BTNode(pre[pl]);
        if (il < k) 
            root->lchild = buildtree(il, k - 1, pl + 1, pl + 1 + k - 1 - il);
        if (k < ir) 
            root->rchild = buildtree(k + 1, ir, pl + 1 + k - 1 - il + 1, pr);
        return root;
    }
    BTNode* reConstructBiTree(vector<int>& preOrder, vector<int>& inOrder) {
        pre = preOrder, in = inOrder;
        if (pre.empty() || in.empty()) return NULL;
        for (int i = 0; i < in.size(); i ++) pos[in[i]] = i;
        auto root = buildtree(0, in.size() - 1, 0, pre.size() - 1);
        return root;
    }

2. 中序和后序确定二叉树

题目链接

vector<int> in, post;
    unordered_map<int, int> pos;
    BTNode* build(int il, int ir, int pl, int pr)
    {
        int k = pos[post[pr]];
        auto root = new BTNode(post[pr]);
        if (il < k)
        	root->lchild = build(il, k - 1, pl, pl + k - 1 - il);
        if (k < ir) 
        	root->rchild = build(k + 1, ir, pl + k - 1 - il + 1, pr - 1);
        return root;
    }
    BTNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        in = inorder, post = postorder;
        if (in.empty() || post.empty()) return NULL;
        for (int i = 0; i < in.size(); i ++) pos[in[i]] = i;
        auto root = build(0, in.size() - 1, 0, post.size() - 1);
        return root;
    }

3. 满二叉树和先序确定二叉树的后序遍历序列(天勤P172)

无题目链接
参考链接,这个博主写得好

void preToPost(int pre[], int pl, int pr)
{
	int root = pre[pl];   //暂存根,子树遍历完后最后输出
	
	if (pl < pr) {
		pl = pl + 1;//左端 + 1(把根节点独立出来)
		//此时左子树从pl + 1位开始,到(pl + pr) / 2)结束
		preToPost(pre, pl, (pl + pr) / 2); //左子树
		//此时右子树从(pl + pr) / 2 + 1位开始,到pr结束
		preToPost(pre, (pl + pr) / 2 + 1, pr); //右子树
	}
	printf("%d", root);  //输出
}

五、二叉树的各种结点数

1. 确定二叉树的所有结点数

题目链接

// 方法1
int cnt = 0;
void dfs(BTNode* root)
{
	 if (!root) return;
	 cnt ++;
	 dfs(root->lchild);
	 dfs(root->rchild);
}
int node_num(BTNode* root)
{
	 dfs(root);
	 return cnt;
}
// 方法2
int node_num(BTNode* root) 
{
	if (!root) return 0;
	int lcnt = node_num(root->left);
	int rcnt = node_num(root->right);
	return lcnt + rcnt + 1;
}

2. 确定二叉树的所有叶子结点数

无题目链接

// 方法1
int cnt = 0;
void dfs(BTNode* root)
{
	 if (!root) return;
	 if (!root->lchild && !root->right) cnt ++;
	 dfs(root->lchild);
	 dfs(root->rchild);
}
int leaf_num(BTNode* root)
{
	 dfs(root);
	 return cnt;
}
// 方法2
int leaf_num(BTNode* root)
{
	 if (!root) return 0;
	 else if (!root->lchild && !root->rchild) return 1;
	 else return leaf_num(root->lchild) + leaf_num(root->rchild);
}

3. 确定二叉树的双分支结点数

无题目链接

int cnt = 0;
void dfs(BTNode* root)
{
     if (!root) return;
     if (root->lchild && root->rchild) cnt ++;
     dfs(root->lchild);
     dfs(root->rchild);
}
int double_num(BTNode* root)
{
     dfs(root);
     return cnt;
}

六、判断问题

1. 判断给定二叉树是否是完全二叉树

题目链接

bool isCompleteTree(BTNode* root)
{
     if (!root) return true;
     queue<BTNode*> q;
     q.push(root);
     bool null_flag = false;
     while (q.size())
     {
         auto t = q.front();
         q.pop();

         if (!t) null_flag = true;
         else
         {
             if (null_flag) return false;
             q.push(t->lchild);
             q.push(t->rchild);
          }
     }
     return true;
}

2. 判断给定二叉树是否是二叉搜索树(BST)

题目链接

bool isValidBST(BTNode* root)
{
     if (!root) return true;
     stack<BTNode*> s;
     int minval = -0x7fffffff;
     auto p = root;
     while (p || s.size())
     {
         if (p)
         {
             s.push(p);
             p = p->lchild;    
         }
         else
         {
			   // 注意!!这里不是auto p!!
             p = s.top();
             s.pop();
             if (p->data <= minval) return false;
             else minval = p->data;
             p = p->rchild;
         }
     }
     return true;
}
// 另一种差不多更习惯的写法
bool isValidBST(BTNode* root)
{
     if (!root) return true;
     stack<BTNode*> s;
     int minval = -0x7fffffff;
     auto p = root;
     while (p || s.size())
     {
         if (p)
         {
             s.push(p);
             p = p->lchild;    
         }
         else
         {
             auto t = s.top();
             s.pop();
             if (t->data <= minval) return false;
             else minval = t->data;
             p = t->rchild;
         }
     }
     return true;
}

3. 判断给定二叉树是否是平衡二叉树(AVL)

类似题目:题目链接(只需要判断其平衡性)

int depth(BTNode* root)
{
    if (!root) return 0;
    int ldepth = depth(root->lchild);
    int rdepth = depth(root->rchild);
    return max(ldepth, rdepth) + 1;
}
bool is_balance(BTNode* root)
{
    if (!root) return true;
    int ldepth = depth(root->lchild);
    int rdepth = depth(root->rchild);
    if (abs(ldepth - rdepth) > 1) return false;
    return is_balance(root->lchild) && is_balance(root->rchild);
}
// 私认为针对题目来说,答案如下,即把判断BST和balance的集成一下
bool isValidBST(BTNode* root)
{
     if (!root) return true;
     stack<BTNode*> s;
     int minval = -0x7fffffff;
     auto p = root;
     while (p || s.size())
     {
         if (p)
         {
             s.push(p);
             p = p->lchild;    
         }
         else
         {
             p = s.top();
             s.pop();
             if (p->data <= minval) return false;
             else minval = p->data;
             p = p->rchild;
         }
     }
     return true;
}
int depth(BTNode* root)
{
    if (!root) return 0;
    int ldepth = depth(root->lchild);
    int rdepth =depth(root->rchild);
    return max(ldepth, rdepth) + 1;
}
bool is_balance(BTNode* root)
{
    if (!root) return true;
    int ldepth = depth(root->lchild);
    int rdepth = depth(root->rchild);
    if (abs(ldepth - rdepth) > 1) return false;
    return is_balance(root->lchild) && is_balance(root->rchild);
 }
 bool is_balanced_BST(BTNode* root)
 {
 		if (!root) return true;
 		return is_balance(root) && isValidBST(root);
 }

4. 判断两棵二叉树是否相似

类似题目:题目链接(判断两棵二叉树是否相同)

bool is_same(BTNode* root1, BTNode* root2)
{
    if (!root1 && !root2) return true;
    else if (!root1) return false;
    else if (!root2) return false;
    else
    {
        if (root1->data != root2->data) return false;
        else return is_same(root1->lchild, root2->lchild) && is_same(root1->rchild, root2->rchild);
    }
}
// 私认为针对题目来说,答案如下
bool is_similar(BTNode* root1, BTNode* root2)
{
    if (!root1 && !root2) return true;
    else if (!root1) return false;
    else if (!root2) return false;
    else return is_similar(root1->lchild, root2->lchild) && is_similar(root1->rchild, root2->rchild);
}

七、路径问题

1. 找到所有结点到根结点的路径(天勤P171)

题目链接

vector<vector<int>> paths;
    vector<int> path;
    vector<string> res;
    void find_path(BTNode* root)
    {
        if (!root) return;
        // 下面第一句总是忘记写在哪。。导致报错
        path.push_back(root->data);
        if (!root->lchild && !root->rchild)
            paths.push_back(path);
        find_path(root->lchild);
        find_path(root->rchild);
        path.pop_back();
    }
    vector<string> binaryTreePaths(BTNode* root) {
        if (!root) return res;
        find_path(root);
        // 力扣上不加下面两句话,会报错
        // if (paths.size() == 0 || paths[0].size() == 0) 
        //     return res;
        for (auto p : paths)
        {
            string re = to_string(p[0]);
            for (int i = 1; i < p.size(); i ++)
            {
                re += "->";
                re += to_string(p[i]);
            }
            res.push_back(re);
        }
        return res;
    }

2. 计算二叉树的WPL(带权路径长度)

题目链接

// 方法1
int WPL = 0;
void dfs(BTNode* root, int depth)
{
    if (!root) return;
    if (!root->lchild && !root->rchild) WPL += root->data * depth;
    dfs(root->lchild, depth + 1);
    dfs(root->rchild, depth + 1);
}
int pathSum(BTNode* root)
{
    if (!root) return 0;
    int depth = 0;
    dfs(root, depth);
    return WPL;
}
// 方法2
vector<vector<BTNode*>> paths;
vector<BTNode*> path;
int WPL = 0;
void find_path(BTNode* root)
{
    if (!root) return;
    path.push_back(root);
    // 遍历到叶子结点就把path弹入paths,如果遍历到null就弹入的话,
    // 一个叶子结点因为有两个null,所以,同一个path会被弹入paths两次
    if (!root->lchild && !root->rchild) paths.push_back(path);
    find_path(root->lchild);
    find_path(root->rchild);
    path.pop_back();
}
int pathSum(BTNode* root) 
{
    if (!root) return 0;
    find_path(root);
    for (auto p : paths)
    {
        int len = p.size();
        WPL += (len - 1) * p[len - 1]->data;
    }
    return WPL;
}

八、祖先问题

1. 找到二叉树中任意两个结点的最近公共祖先(顺序+链式)

链式二叉树:题目链接

// 1. 顺序存储的二叉树的最近的公共祖先问题
// BTNode的val为char型变量,不然不好区分啥表示的空结点
int find_LCA(BTNode T[], int i, int j)
{
    if (T[i].data != '#' && T[j].data != '#')	// 如果为空结点,直接return 0
    {
        while (i != j)
        {
            if (i > j)	i /= 2;
            else j /= 2;
        }
        return i;
    }
    else return 0;
}
// 2. 链式存储的二叉树的最近公共祖先问题
// 方法1(我自己AC的版本,听过讲解感觉还是较难理解。。)
	BTNode* find_LCA(BTNode* root, BTNode* p, BTNode* q) 
	{
     	if (!root) return NULL;
    
     	if (root == p || root == q) return root;
    		BTNode* lanc = find_LCA(root->lchild, p, q);
     	BTNode* ranc = find_LCA(root->rchild, p, q);
     	if (lanc && ranc) return root;
     	if (lanc) return lanc;
     	return ranc;
	}
// 方法2(后续非递归序列,也AC了,很好理解,写着冗杂)
	stack<BTNode*> find_ancestors(BTNode* root, BTNode* t)
    {
        stack<BTNode*> s;
        BTNode *p = root, *r = NULL;
        while (p || s.size())
        {
            if (p)
            {
                s.push(p);
                p = p->lchild;
            }
            else
            {
                p = s.top();
                if (p == t) break;
                if (p->rchild && p->rchild != r) p = p->rchild;
                else
                {
                    s.pop();
                    r = p;
                    p = NULL;
                }
            }
        }
        return s;
    }
    BTNode* find_LCA(BTNode* root, BTNode* p, BTNode* q) {
        if (!root) return NULL;
        stack<BTNode*> s1, s2;
        s1 = find_ancestors(root, p);   // 放p和他的祖先们
        s2 = find_ancestors(root, q);   // 放q和他的祖先们
        
        unordered_set<BTNode*> h;
        while (s1.size())
        {
            auto t = s1.top();
            s1.pop();
            h.insert(t);
        }
        while (s2.size())
        {
            auto t = s2.top();
            s2.pop();
            if (h.count(t)) return t;
        }
        return NULL;
    }

2. 打印值为x的结点的所有祖先

参考链接,这个博主文字解说部分写得好
无题目链接

// 方法1
// 适合于寻找二叉树中只有一个结点值为x的结点的祖先
void find_ancestors(BTNode* root, int x)
{
    if (!root) return;
    stack<BTNode*> s;
    BTNode *p = root, *r = NULL;
    while (p || s.size())
    {
        if (p)
        {
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            if (p->data == x)
            {
                s.pop();
                while (s.size())
                {
                    cout << s.top() << ' ';
                    s.pop();
                }
                return;
            }
            if (p->rchild && p->rchild != r)
                p = p->rchild;
            else
             {
                s.pop();
                r = p;
                p = NULL;
             }
        }
    }
}
// 方法2(自己想的,核心部分已经写了,但有些尾巴没写完)
// 适合于寻找多个结点值为x的结点的全部的祖先/“残缺”路径
vector<int> path;
vector<vector<int>> paths;
void dfs(BTNode* root, int x)
{
    if (!root) return;
    
    path.push_back(root->data);
    if (root->data == x) 
    {
        // for (auto p : path) cout << p << ' ';
        // puts("");
        paths.push_back(path);
    }
    if (root->lchild) dfs(root->lchild, x);
    if (root->rchild) dfs(root->rchild, x);
    path.pop_back();
}

最后——杂七杂八

1. 将二叉树的叶结点按从左到右的顺序连成一个单链表,叶结点右指针域存放单链表指针(左—>右)(天勤P171)

无题目链接

// 注意!此处dummy和cur都是链表的结点,
// 但为了跟叶子结点的二叉链表结构(有lchild和rchild域)对应,其原来的next域用rchild表示
// 尾插法创建链表
auto dummy = new LNode(-1);	// 虚拟头结点
LNode *cur = dummy;
LNode* inorder(BTNode* root)
{
		if (!root) return NULL;
		inorder(root->lchild);    // 中序遍历左子树
		if (!root->lchild && !root->rchild)	// 叶结点
		{
			 cur->rchild = root;
			 cur = root;
		}
		inorder(root->rchild);  // 中序遍历右子树
		cur->rchild = NULL;   // 设置链表尾
    	return dummy;
}

2. 将二叉树的所有结点的左右子树进行交换

题目链接

BTNode* Mirror(BTNode* root)
{
    if (!root) return NULL;
    swap(root->lchild, root->rchild);
    Mirror(root->lchild);
    Mirror(root->rchild);
    // 交换语句在这也可以:
    // swap(root->lchild, root->rchild);
    return root;
}

3. 对于树中每个元素值为x的结点,删除以它为根的子树,释放相应空间(王道P149)

无题目链接

void delete_tree(BTNode* root)
{
    if (!root) return;
    delete_tree(root->lchild);
    delete_tree(root->rchild);
    free(root);
}
void levelorder(BTNode* root, int x)
{
    if (!root) return;
    if (root->data == x)
    {
        delete_tree(root);
        return;
    }
    queue<BTNode*> q;
    q.push(root);
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        if (t->lchild)
        {
            if (t->lchild->data == x)
            {
                delete_tree(t->lchild);
                t->lchild = NULL;
            }
            else q.push(t->lchild);
        }
        if (t->rchild)
        {
            if (t->rchild->data == x)
            {
                delete_tree(t->rchild);
                t->rchild = NULL;
            }
            else q.push(t->rchild);
        }
    }
}

4. 从大到小输出BST中所有值不小于k的关键字

无题目链接

void print(BTNode* root, int k)
{
	if(!root) return;
	print(root->rchild, k);
	if(root->data >= k) cout << root->data << ' ';
	print(root->lchild, k);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值