LeetCode题目总结——树

LeetCode题目总结——树

1. 遍历

采用递归或非递归实现中序、前序、后序、层序遍历。

// 中序遍历
void inorder(vector<int>& res, TreeNode* root) {
	if (root == nullptr) return;
	inorder(res, root->left);
	res.push_back(root->val);
	inorder(res, root->right);
}

// 非递归
while (!stack.empty() || root != nullptr) {
	while (root != nullptr) {
		stack.push(root);
		root = root->left;
	}
	root = stack.top();
	stack.pop();
	res.push_back(root->val);
	root = root->right;
}
// 前序遍历
void preorder(vector<int>& res, TreeNode* root) {
	if (root == nullptr) return;
	res.push_back(root->val);
	preorder(res, root->left);
	preorder(res, root->right);
}

// 非递归1
while (!stack.empty() || root != nullptr) {
	while (root != nullptr) {
		res.push_back(root->val);
		stack.push(root);
		root = root->left;
	}
	root = stack.top();
	stack.pop();
	root = root->right;
}

// 非递归2
stack.push(root);
while (!stack.empty()) {
	root = stack.top();
	stack.pop();
	res.push_back(root->val);
	if (root->right) stack.push(root->right);
	if (root->left) stack.push(root->left);
}
// 后序遍历
void postorder(vector<int>& res, TreeNode* root) {
	if (root == nullptr) return;
	postorder(res, root->left);
	postorder(res, root->right);
	res.push_back(root->val);
}

// 非递归
while (!stack.empty() || root != nullptr) {
	while (root != nullptr) {
		res.push_back(root->val);
		stack.push(root);
		root = root->right;
	}
	root = stack.top();
	stack.pop();
	root = root->left;
}
reverse(res.begin(), res.end());

// 非递归2
stack.push(root);
while (!stack.empty()) {
	root = stack.top();
	stack.pop();
	res.push_back(root->val);
	if (root->left) stack.push(root->left);
	if (root->right) stack.push(root->right);
}
reverse(res.begin(), res.end());
// 层序遍历,非递归
if (root) q1.push(root);
while (!q1.empty()) {
	q2.clear();
	for (auto node : q1) {
		res.push_back(node->val);
		if (node->left) q2.push_back(node->left);
		if (node->right) q2.push_back(node->right);
	}
	swap(q1, q2);
}
  1. 0094 - 二叉树的中序遍历
  2. 0098 - 验证二叉搜索树
  3. 0099 - 恢复二叉搜索树
    中序遍历,寻找pre->val > cur->val的位置。
    找到第一个时,p1 = pre, p2 = root;找到第二个时,p2 = root(第二个位置有可能找不到)。
    最后交换p1与p2的值。
  4. 0102 - 二叉树的层序遍历
  5. 0103 - 二叉树的锯齿形层序遍历
  6. 0107 - 二叉树层次遍历 II
  7. 0116 - 填充每个节点的下一个右侧节点指针
    可以借助next指针进行层序移动,实现O(1)的空间复杂度。
Node* leftmost = root;
// 若为最后一层则结束
while (leftmost->left) {
	// 从该层最左侧节点开始,至该层最右侧节点
	Node* head = leftmost;
	while (head) {
		// 左子节点指向右子节点
		head->left->next = head->right;
		// 右子节点指向下一节点的左子节点
		if (head->next) {
			head->right->next = head->next->left;
		}
		// 移动至下一节点
		head = head->next;
	}
	// 向下移动一层
	leftmost = leftmost->left;
}

7. 0117 - 填充每个节点的下一个右侧节点指针
与0116相同,可以借助next指针进行层序移动,实现O(1)的空间复杂度。由于不存在完全二叉树条件,所以每次需要动态记录当前遍历的前一个节点,以及当前层的首节点。

Node* leftmost = root;
while (leftmost) {
	Node* pre = nullptr, next_leftmost = nullptr;
	// 从该层最左侧节点开始,至该层最右侧节点
	Node* head = leftmost;
	while (head) {
		// 遍历并处理左子节点
		if (head->left) {
			if (pre) pre->next = head->left; // 前一节点指向该节点
			pre = head->left;
			if (!next_leftmost) next_leftmost = head->left; // 若为该层第一个节点,则记录
		}
		// 遍历并处理左子节点
		if (head->right) {
			if (pre) pre->next = head->right; // 前一节点指向该节点
			pre = head->right;
			if (!next_leftmost) next_leftmost = head->right; // 若为该层第一个节点,则记录
		}
		// 移动至下一节点
		head = head->next;
	}
	// 向下移动一层
	leftmost = next_leftmost;
}

2. 根据遍历规则寻找前驱节点

遍历算法的特殊处理方法,不需要额外的栈存储,可以使空间复杂度从O(n)降低到O(1)。如Morris遍历。

  1. 0114 - 二叉树展开为链表
while (cur) {
	if (cur->left) {
		auto next = cur->left;
		auto pre = next;
		while (pre->right) {
			pre = pre->right;
		}
		pre->right = cur->right;
		cur->right = next;
		cur->left = nullptr;
	}
	cur = cur->right;
}

3. 遍历序列反推二叉树

两种遍历序列,构造二叉树。

  1. 0105 - 从前序与中序遍历序列构造二叉树
    前序 = { [root], [left part], [right part] }
    中序 = { [left part], [root], [right part] }
    根据前序遍历序列,寻找中序遍历的左右子树分界点(root),然后根据中序遍历的左右子树长度,寻找前序遍历的左右子树分节点,递归构建。
TreeNode* buildTree(vector<int>& preorder, int pre0, int pre1, 
                    vector<int>& inorder, int in0, int in1) {
    if (in1 == in0) return nullptr;

    auto root = new TreeNode(preorder[pre0]);
    int idx = inorder_idx_map[preorder[pre0]];

    root->left = buildTree(preorder, pre0 + 1, pre0 + (idx - in0 + 1), 
                           inorder, in0, idx);

    root->right = buildTree(preorder, pre0 + (idx - in0 + 1), pre1,
                            inorder, idx + 1, in1);
    return root;
}
  1. 0106 - 从中序与后序遍历序列构造二叉树
    中序 = { [left part], [root], [right part] }
    后序 = { [left part], [right part], [root] }
    根据前序遍历序列,寻找后序遍历的左右子树分界点(root),然后根据中序遍历的左右子树长度,寻找后序遍历的左右子树分节点,递归构建。
TreeNode* buildTree(vector<int>& inorder, int in0, int in1, 
                    vector<int>& postorder, int post0, int post1) {
    if (in0 == in1) return nullptr;
    int idx = inorder_idx_map[postorder[post1 - 1]];
    auto root = new TreeNode(postorder[post1 - 1]);

    root->left = buildTree(inorder, in0, idx,
                           postorder, post0, post0 + (idx - in0));

    root->right = buildTree(inorder, idx + 1, in1,
                            postorder, post0 + (idx - in0), post1 - 1);

    return root;
}

4. 自底向上生成

递归调用,自底向上生成,每个节点生成以当前节点为根节点的局部解,然后向上传递,上层结点统计左右节点的结果,并继续生成局部解并向上传递。

  1. 0095 - 不同的二叉搜索树 II
    左右节点返回局部解Rl、Rr,当前节点将Rl、Rr依次组合,生成(Rl * Rr)个当前局部解。
  2. 0098 - 验证二叉搜索树
    也可用中序遍历解决。
  3. 0100 - 相同的树
  4. 0101 - 对称二叉树
  5. 0104 - 二叉树的最大深度
int maxDepth(TreeNode* root) {
	if (!root) return 0;
	return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
  1. 0108 - 将有序数组转换为二叉搜索树
    寻找中点构建当前树节点,中点左右两侧的数组分别递归构建左右子树。
  2. 0109 - 将有序链表转换为二叉搜索树
    思路同题目0108,寻找中点的方式采用快慢指针。
  3. 0110 - 平衡二叉树
    计算所有子树最大深度的同时,判断当前子树是否时平衡二叉树。
int maxDepth(TreeNode* root) {
	if (!root) return 0;
	int dl = maxDepth(root->left);
	int dr = maxDepth(root->right);
	if (dl < 0 || dr < 0 || abs(dl - dr) > 1) {
		return -1;
	}
	return max(dl, dr) + 1;
}
  1. 0111 - 二叉树的最小深度
    注意不能简单的将0104中的max改为min,只有叶节点才能返回有效深度,所以为nullptr的节点不能返回为0的深度值(除根节点)。
int minDepth(TreeNode* root) {
	if (!root) return 0;
	if (root->left && root->right) {
		return min(minDepth(root->left), minDepth(root->right)) + 1;
    }
    if (root->left) {
        return minDepth(root->left) + 1;
    }
    if (root->right) {
        return minDepth(root->right) + 1;
    }
    else {
        return 1;
    }
}
  1. 0112 - 路径总和
bool hasPathSum(TreeNode* root, int sum) {
	if (!root) return false;
	if (!root->left && !root->right) {
		return root->val == sum;
	}
	else {
        return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
    }
}
  1. 0124 - 二叉树中的最大路径和
    生成当前节点的左右子树的最大单向路径和,
if (root->left) 
	l = max(0, search(res, root->left));
if (root->right) 
	r = max(0, search(res, root->right));
res = max(res, val + l + r);
return val + max(l, r);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值