七、二叉树学习3(代码随想录学习)

15. 左子叶之和

leetcode链接

思路: 递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。使用bool变量判断是否为左子叶。

class Solution {
public:
	int postorder(TreeNode* t, bool isleft) { // 返回t的所有左子叶的和,后序遍历
		if (t == NULL) return 0;
		int l = postorder(t->left, true); // 统计左子树的所有左子叶的和
		int r = postorder(t->right, false); // 统计右子树的所有左子叶之和
		if (isleft && t->left == NULL && t->right == NULL) // 当当前结点为左子叶时,返回当前结点值
			return t->val;
		else
			return l + r; // 若不为左子叶,返回当前结点的所有左子叶之和
	}

	int sumOfLeftLeaves(TreeNode* root) {
		if (root == NULL) return 0;
		return postorder(root, false);
	}
};

16.找树左下角的值

leetcode链接

思路: 使用层序遍历,保存最后一层的最左结点值即可。

class Solution {
public:
	int findBottomLeftValue(TreeNode* root) {
		queue<TreeNode*> que;
		int res = 0;
		que.push(root);
		while (!que.empty()) {
			int size = que.size();
			res = que.front()->val; // 记录当层的最左结点
			for (int i = 0; i < size; i++) {
				TreeNode* cur = que.front();
				que.pop();
				if (cur->left != NULL) que.push(cur->left);
				if (cur->right != NULL) que.push(cur->right);
			}
		}
		return res;
	}
};

路径总和

leetcode链接

思路: 递归+回溯,保存当前路径和,找到到叶结点的路径和是否与target相同,若相同,则直接返回true;若不相同,则继续找其他路径,直至遍历完所有路径。

递归函数什么时候需要返回值?什么时候不需要返回值?

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。

写法1:无返回值

class Solution {
public:
	void preorder(TreeNode* t,bool &isExist,int &count, int target) {
		count += t->val;
		if (t->left == NULL && t->right == NULL) {  // 退出递归条件
			if (count == target)
				isExist = true;
			return;
		}

		if (t->left) {
			preorder(t->left, isExist, count, target);
			count -= t->left->val; // 回溯
		}
		if (t->right) {
			preorder(t->right, isExist, count, target);
			count -= t->right->val;
		}

	}

	bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == NULL) return false;
		int count = 0;
		bool isExist = false;
		preorder(root, isExist, count, targetSum);
		return isExist;
	}
};

写法2:有返回值

class Solution {
public:
	bool traversal(TreeNode* t,int count) { // count 为减过t->val后的值,在上层已经减去
		// 跳出循环条件
		if (t->left == NULL && t->right == NULL) {
			if (count == 0)
				return true;
			return false;
		}
		// 当左右路线存在符合条件的路径,返回true
		if (t->left) {
			count -= t->left->val;
			if (traversal(t->left, count)) return true;
			count += t->left->val; // 回溯
		}

		if (t->right) {
			count -= t->right->val;
			if (traversal(t->right, count)) return true;
			count += t->right->val;
		}
		// 当左右孩子均不在符合条件的路径,返回false
		return false;
	}

	bool hasPathSum(TreeNode* root, int targetSum) {
		if (root == NULL) return false;
		return traversal(root, targetSum - root->val);
	}
};

18.从中序与后序遍历序列构造二叉树

leetcode链接

思路: 递归遍历,递归得到当前中序数组和后序数组生成的二叉树。就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。

说到一层一层切割,就应该想到了递归。
来看一下一共分几步:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间
class Solution {
public:
	// 返回传入中序数组和后序数组组成的二叉树
	TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {  
		// 如果数组大小为零的话,说明是空节点了。
		if (postorder.size() == 0) return NULL;

		// 遍历后序数组,最后一个元素,即为当前的根节点
		int rootval = postorder[postorder.size() - 1];
		TreeNode* root = new TreeNode(rootval);

		// 如果当前结点为叶结点,也直接返回
		if (postorder.size() == 1) return root;

		// 找到切割点(在中序遍历中的下标)
		int delimiterIndex;
		for (delimiterIndex = 0; delimiterIndex < postorder.size(); delimiterIndex++)
			if (inorder[delimiterIndex] == rootval)
				break;

		// 切割中序数组(中序遍历的根节点在中间,便于分割)
		// 切割时,统一保证左闭右开,[0,delimiterIndex)
		vector<int> inorderLeft(inorder.begin(),inorder.begin() + delimiterIndex);
		vector<int> inorderRight(inorder.begin() + delimiterIndex + 1, inorder.end());// 中间元素舍去

		// 切割后序数组(通过中序数组两边的大小来进行切割)
		// 舍弃末尾元素
		postorder.pop_back();
		vector<int> postorderLeft(postorder.begin(), postorder.begin() + inorderLeft.size());
		vector<int> postorderRight(postorder.begin() + inorderLeft.size(), postorder.end());

		// 递归遍历后序数组
		root->left = buildTree(inorderLeft, postorderLeft);
		root->right = buildTree(inorderRight, postorderRight);

		return root;
	}
};

但这样写代码性能并不好,每层递归均创建了新的vector。使用下标进行递归,对代码进行改进。

class Solution {
public:
	// begin与end是区间左闭右开的index
	TreeNode* travseral(vector<int> &inorder,int inorderBegin,int inorderEnd,vector<int> &postorder,int postorderBegin,int postorderEnd) {
		// 如果数组大小为零的话,说明是空节点了
		if (postorderBegin == postorderEnd) return NULL;

		//遍历后序数组,最后一个元素,即为当前的根节点
		int rootval = postorder[postorderEnd - 1];
		TreeNode* root = new TreeNode(rootval);

		// 若当前结点为叶结点,返回root
		if (postorderEnd - postorderBegin == 1) return root;

		// 找到分割点
		int delimiterIndex;
		for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
			if (inorder[delimiterIndex] == rootval)
				break;

		// 分割中序数组(左闭右开)  中间元素被移除
		int leftInorderBegin = inorderBegin;
		int leftInorderEnd = delimiterIndex;
		int rightInorderBegin = delimiterIndex + 1;
		int rightInorderEnd = inorderEnd;

		// 分割后序数组,末尾元素被移除
		int leftPostorderBegin = postorderBegin;
		int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin;
		int rightPostorderBegin = postorderBegin + delimiterIndex - inorderBegin;
		int rightPostorderEnd = postorderEnd - 1;

		// 递归
		root->left = travseral(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
		root->right = travseral(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);

		return root;
	}


	// 返回传入中序数组和后序数组组成的二叉树
	TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {  
		if (postorder.size() == 0) return NULL;
		// 输入时,区间左闭又开
		return travseral(inorder, 0, inorder.size(), postorder, 0, postorder.size());
	}
};

先序遍历与中序遍历构造一棵二叉树

class Solution {
public:
	// begin与end是左闭右开的下标
	TreeNode* traversal(vector<int> &preorder,int preorderBegin,int preorderEnd,vector<int> &inorder,int inorderBegin,int inorderEnd) {
		if (preorderBegin == preorderEnd) return NULL; // 当子树为空时,返回null
		int rootval = preorder[preorderBegin];

		TreeNode* root = new TreeNode(rootval);
		if (preorderEnd - preorderBegin == 1) return root; // 仅有一个结点时,返回当前结点

		// 找到分割点(下标)
		int delimiterIndex;
		for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
			if (inorder[delimiterIndex] == rootval)
				break;

		// 分割中序数组
		int leftinorderBegin = inorderBegin;
		int leftinorderEnd = delimiterIndex;
		int rightinorderBegin = delimiterIndex + 1;
		int rightinorderEnd = inorderEnd;

		// 分割先序数组
		int leftpreorderBegin = preorderBegin + 1;
		int leftpreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin;
		int rightpreorderBegin = leftpreorderEnd;
		int rightpreorderEnd = preorderEnd;

		// 递归
		root->left = traversal(preorder, leftpreorderBegin, leftpreorderEnd, inorder, leftinorderBegin, leftinorderEnd);
		root->right = traversal(preorder, rightpreorderBegin, rightpreorderEnd, inorder, rightinorderBegin, rightinorderEnd);
		return root;
	}

	TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
		if (preorder.size() == 0) return NULL;
		return traversal(preorder, 0, preorder.size(), inorder, 0, inorder.size());
	}
};

19. 最大二叉树

leetcode链接

思路: 与上题构造二叉树类似,将数组分为两个区间分别构造左右子树,依照数组的最大值分割数组,然后递归循环构造二叉树。

class Solution {
public:
	// begin 与 end分别为左闭右开的下标
	TreeNode* traversal(vector<int> &nums,int begin,int end) {
		if (end - begin == 0) return NULL;
		int max = INT_MIN;
		int maxIndex = 0;
		// 找到最大值,及对应下标,为根节点
		for (int i = begin; i < end; i++) {
			if (nums[i] > max) {
				max = nums[i];
				maxIndex = i;
			}
		}
		TreeNode* root = new TreeNode(max);
		if (end - begin == 1) return root;
		root->left = traversal(nums, begin, maxIndex);
		root->right = traversal(nums, maxIndex + 1, end);
		return root;
	}

	TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
		if (nums.size() == 0) return NULL;
		return traversal(nums, 0, nums.size());
	}
};

20.二叉树周末总结(3)

代码随想录:周末总结3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值