代码随想录算法训练营Day22 | 235. 二叉搜索树的最近公共祖先、701. 二叉搜索树中的插入操作、450. 删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先

这题相比于236简单很多,由于二叉搜索树的性质,从上向下遍历,找到第一个值介于p与q之间节点即为最近公共祖先(一侧子树上的所有节点都大于/小于根节点,所以公共祖先的值一定介于p和q之间)

TreeNode* ans = nullptr;

void traversal(TreeNode* cur, int& i1, int& i2) {
	if (!cur || ans)
		return;
	if ((cur->val <= i1) && (cur->val >= i2)) {
		ans = cur;
		return;
	}
	traversal(cur->left, i1, i2);
	traversal(cur->right, i1, i2);
}

// 前序遍历,找到第一个值介于p与q之间节点即为最近公共祖先
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
	int i1, i2;    // i1为p、q中的较大值
	if (p->val > q->val) {
		i1 = p->val;
		i2 = q->val;
	}
	else {
		i1 = q->val;
		i2 = p->val; 
	}
	traversal(root, i1, i2);
	return ans;
}

701. 二叉搜索树中的插入操作

这题想复杂了,一定能够存在一个叶子节点能够插入

 分为两步:

1、搜索到插入值应该在的地方,一定有一个空叶子节点满足条件

2、将该值插入在该空叶子节点上

递归写法1:

void findPos(TreeNode* cur, TreeNode*& node) {
	if (!cur)
		return;
    // 按二叉树性质递归访问左右子树,找到符合条件的空叶子节点则将其替换为需要插入的节点
	if (node->val < cur->val) {
		if (cur->left)
			findPos(cur->left, node);
		else
			cur->left = node;
	}
	else {
		if (cur->right)
			findPos(cur->right, node);
		else
			cur->right = node;
	}
}

TreeNode* insertIntoBST(TreeNode* root, int val) {
    // 先使用插入值构造一个二叉树节点
	TreeNode* node = new TreeNode(val);
	if (root)
		findPos(root, node);
	else
		root = node;
	return root;
}

递归写法2:

// 按二叉树性质不断递归,到达空叶子节点时将该节点替换为插入节点
TreeNode* findPos(TreeNode* cur, int& val) {
	if (!cur) {
		TreeNode* node = new TreeNode(val);
		return node;
	}
	if (cur->val > val)
		cur->left = findPos(cur->left, val);
	else
		cur->right = findPos(cur->right, val);

	return cur;
}

TreeNode* insertIntoBST(TreeNode* root, int val) {
	return findPos(root, val);
}

450. 删除二叉搜索树中的节点

这题同样需要做好情况分类整理

(注意递归的返回值类型是TreeNode*,返回的不一定是当前节点,也可能是删除该节点后用于替代的节点)

情况1:如果没找到需要删除的节点,不作任何修改。返回该节点自身
情况2:如果该节点为叶节点(无左右子树),直接删除该节点。返回一个空指针
情况3:如果该节点只有一个子树,删除节点后将该子树与被删除节点的父节点相连(使用该子树取代被删除节点)。返回该子树
情况4:如果该节点有两个子树,选择左子树的右下角节点或右子树的左下角节点代替被删除节点节点(替代的节点性质与被删除节点最接近,如左子树的右下角节点,也满足大于该左子树中的其余所有节点)。返回该替代节点

整理好所有情况就正常按规则书写了,情况4会比较麻烦。

TreeNode* traversal(TreeNode* cur, int& key) {
	if (!cur)
		return nullptr;
    // 当前值大于需要删除节点的值,左转
	if (cur->val > key)
		cur->left = traversal(cur->left, key);
    // 当前值小于需要删除节点的值,右转
	else if (cur->val < key)
		cur->right = traversal(cur->right, key);
	// 找到了需要删除的节点,分情况书写
    else {
		// 情况2(叶子节点,无左右子树)
		if (!cur->left && !cur->right) {
			cur = nullptr;
            delete cur;
			return nullptr;
		}
		// 情况3(有左子树无右子树)
		else if (cur->left && !cur->right) {
			TreeNode* temp = cur->left;
			cur = nullptr;
            delete cur;
			return temp;
		}
		// 情况3(无左子树有右子树)
		else if (!cur->left && cur->right) {
			TreeNode* temp = cur->right;
            cur = nullptr;
			delete cur;
			return temp;
		}
		// 情况4(左右子树都有)
		else {
			// 获取替代节点,替代节点为右子树的左下角节点(右转一次后不断左转)
			TreeNode* replace = cur->right;
			TreeNode* parent = cur;
			bool inLeft = false;
			while (replace->left) {
				inLeft = true;
				parent = replace;
				replace = replace->left;
			}
			// 如果替代节点还有孩子(替代节点已经处于左下角,只会有右孩子),将孩子接到替代节点原先的位置
			TreeNode* children = replace->right;
			if (inLeft)
				parent->left = children;
			else
				parent->right = children;
			// 使用替代节点被替换删除节点的位置
			replace->left = cur->left;
			replace->right = cur->right;
            // 释放被删除节点内存
            cur = nullptr;
			delete cur;
			return replace;
		}
	}
	return cur;
}

TreeNode* deleteNode(TreeNode* root, int key) {
	return traversal(root, key);
}

你可能感兴趣的:(算法)