当前位置: 首页 > news >正文

《算法导论》12.3 插入和删除

一、插入和删除

1、插入

1、思路:引入一个辅助指针y,跟随树的结点向下遍历;根据z关键字的值和x的值的大小比较,遍历下面的孩子结点;最终决定插入到什么位置上。
2、伪代码:

TREE-INSERT(T,z)
1 y = NIL
2 x = T.root
3 while x != NIL
4 	y = x
5 if z.key < x:key
6 	x = x.left
7 else x = x.right
8 z.p = y
9 if y == NIL
10 	T.root = z // tree T was empty
11 elseif z.key < y:key
12 	y.left = z
13 else y.right = z 

3、图解:
在这里插入图片描述
图中浅阴影结点表示了一条从树根向下到插入数据项位置处的简单路径(也是遍历顺序),虚线表示加入的一条链。
4、正式代码

#include <iostream>
using namespace std;
struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode* parent;
	TreeNode(int x):val(x), left(NULL), right(NULL), parent(NULL) {}
};

//向二叉树中插入结点
TreeNode* Insert_Node(TreeNode* root, TreeNode* z)
{
	TreeNode* y = NULL;
	TreeNode* x = root;
	//y随着结点向下遍历,途中选择往左还是往右
	while (x != NULL) {
		y = x;
		if (z->val < x->val) {
			x = x->left;
		}
		else {
			x = x->right;
		}
	}
	z->parent = y;
	if (y == NULL) {
		root = z;
	}
	else if (z->val < y->val) {
		y->left = z;
	}
	else {
		y->right = z;
	}
	return root;
}

//遍历二叉树
//递归写法
void inorderSortCure(TreeNode* root)
{
	if (root != NULL)
	{
		inorderSortCure(root->left);
		cout << root->val << " ";
		inorderSortCure(root->right);
	}
}

int main()
{
	TreeNode* root = new TreeNode(3);
	TreeNode* tmpl = new TreeNode(1);
	TreeNode* tmpr = new TreeNode(5);
	root->left = tmpl; tmpl->parent = root;
	root->right = tmpr; tmpr->parent = root;
	TreeNode* tmprl = new TreeNode(4);
	tmpr->left = tmprl; tmprl->parent = tmpr;
	TreeNode* tmprr = new TreeNode(6);
	tmpr->right = tmprr; tmprr->parent = tmpr;
	cout << "Result:" << endl;
	TreeNode* t = new TreeNode(0);
	root = Insert_Node(root,t);
	cout << "result:" << endl;
	inorderSortCure(root);
}

2、删除

(1)基本思路:分为三种情况
a、z没有孩子结点,直接将其删除并用NIL代替就行
b、z只有一个孩子,那么将孩子提升到树里z的位置上(代替)
c、z有两个孩子,先找到z的后继(一定在z的右子树中)y,并且让y来代替z。z中原来的右子树和左子树部分都成为了y新的右子树和左子树,但是这个比较复杂,下面会讲到。
(2)通过图解考虑:
在这里插入图片描述
(a):z没有左孩子用右孩子来替换z,右孩子可以是NIL也可以不是。右孩子为NIL时,这种情况归为z没有孩子结点的情形;当不为NIL时,归为仅有一个孩子结点(右孩子)的情形。
(b):结点z有一个左孩子但是没有右孩子,用l替换z。
(c):结点z有两个孩子,左孩子是结点l,右孩子是其后继,y的右孩子是结点x。用y替换z;使l变为y的左孩子,x作为y的右孩子的现实不改变。
(d):结点z有两个孩子,并且z的后继结点y!=r位于以r为根的子树中。用y自己的右孩子x来代替y,并且让y成为r的双亲;然后置y为q的孩子和l的双亲。

(3)Transplant
用TransPlant来让一棵以v为根的子树来替换一棵以u为根的子树,此时结点u的双亲就变成了结点v的双亲,并且最后v成为u双亲的孩子。

TRANSPLANT(T,u,v)
//当u为根的时候,直接赋给v
if u.p ==NIL
	T.root = v
//当u为左孩子的时候,将v变成左孩子
else if u == u.p.left 
	u.p.left = v
else u.p.right = v //右孩子
if v !=NIL
	v.p = u.p

(4)TREE-DELETE

if x.left == NIL
	TRANSPLANT(T,z,z.right)
elseif z.right ==NIL
	TRANSPLANT(T,z,z,left)
else y = TREE-MINIMUM(z.right)
	if y.p != z
		TRANSPLANT(T,y,y.right)
		y.right = z.right
		y.right.p = y
	TRANSPLANT(T,z,y)
	y.left = z.left
	y.left.p = y

解释伪代码:
(1)第1~2行代码处理结点z没有左孩子的情况,第3 ~ 4行处理z有左孩子但是没有右孩子的情况。
(2)第5~12行处理剩下两种情况:第5行查找结点y,y是z的后继结点
(a)如果y是z的右孩子,那么10~12行将y替换z成为z双亲的一个孩子,用z的左孩子替换y的左孩子,右孩子不变。
(b)如果y不是z的左孩子,那么7 ~ 9行用y的右孩子替换y成为y双亲的一个孩子,然后将z的右孩子转变为y的右孩子,最后10~12行用y替换z变成z双亲的一个孩子,再用z的左孩子替换为y的左孩子(左孩子这个放前面也可以,但是要和(a)统一代码)。
在这里插入图片描述
再附一张和上面一样的图。

C++代码(delete完整代码)

#include <iostream>
using namespace std;
struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode* parent;
	TreeNode(int x) :val(x), left(NULL), right(NULL), parent(NULL) {}
};

//查找最小值
TreeNode* TreeNode_Minmum(TreeNode* root)
{
	TreeNode* p = root;
	while (p->left != NULL)
	{
		p = p->left;
	}
	return p;
}

//中序遍历打印递归写法
void inorderSortCure(TreeNode* root)
{
	if (root != NULL)
	{
		inorderSortCure(root->left);
		cout << root->val << " ";
		inorderSortCure(root->right);
	}
}
//TRANSPLANT(T,u,v)
void Transplant(TreeNode* root, TreeNode* u, TreeNode* v)
{
	//u为树根
	if (u->parent == NULL) {
		v = root;
		v->left->parent = v;
		v->right->parent = v;
	}
	//v替代u成为u父结点的子结点
	else if (u == u->parent->left) {
		u->parent->left = v;
	}
	else {
		u->parent->right = v;
	}
	//u父结点成为v的父结点
	if (v != NULL) {
		v->parent = u->parent;
	}
}

//TREE-DELETE(T,z)
void Tree_delete(TreeNode* root, TreeNode* z)
{
	//针对没有左孩子和有一个左孩子但是没有右孩子的情况
	if (z->left == NULL) {
		Transplant(root, z, z->right);
	}
	else if (z->right == NULL) {
		Transplant(root, z, z->left);
	}
	//针对后两种情况
	else {
		TreeNode *y = TreeNode_Minmum(z->right);
		if (y->parent != z) {
			Transplant(root, y, y->right);//y的right取代y
			//y代替z获得z的right
			y->right = z->right;
			y->right->parent = y;
		}
		Transplant(root, z, y);//y成为z父结点的子结点
		//y代替z获得z.left
		y->left = z->left;
		y->left->parent = y;
	}
}

int main()
{
	TreeNode* root = new TreeNode(3);
	TreeNode* tmpl = new TreeNode(1);
	TreeNode* tmpr = new TreeNode(5);
	root->left = tmpl; tmpl->parent = root;
	root->right = tmpr; tmpr->parent = root;
	TreeNode* tmprl = new TreeNode(4);
	tmpr->left = tmprl; tmprl->parent = tmpr;
	TreeNode* tmprr = new TreeNode(6);
	tmpr->right = tmprr; tmprr->parent = tmpr;
	Tree_delete(root,tmpr);
	inorderSortCure(root);
	return 0;
}

相关文章:

  • C++与C的区别终于说清楚了!
  • 前端面试知识查漏补缺
  • WEIXIN day_02(8.17) 小程序的组件库
  • 社区交友源码 支持聊天私聊-礼物系统-直播系统-缘分匹配+搭建教程
  • Reactor 之 手把手教你 Spring Boot 整合 Reactor
  • 【42STL-函数对象使用详情】
  • LVS-Nat模式实战
  • java毕业设计基于的测试项目管理平台Mybatis+系统+数据库+调试部署
  • 对于钾,钙,锌,铁,钠,镁金属离子荧光探针的详细知识整理如下
  • Soft Actor-Critic(SAC算法)
  • C语言的头文件的处理
  • 使用 DM binary 部署 DM 集群
  • iOS小技能:RSA签名、验签、加密、解密的原理
  • 使用 Argon2 的 Java 密码散列
  • 基于多次傅里叶变换算法的快速相位解包裹算法研究
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • ES6--对象的扩展
  • Laravel Telescope:优雅的应用调试工具
  • Vultr 教程目录
  • 分布式任务队列Celery
  • 给Prometheus造假数据的方法
  • 给第三方使用接口的 URL 签名实现
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 实现简单的正则表达式引擎
  • 详解移动APP与web APP的区别
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • #### go map 底层结构 ####
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (3)llvm ir转换过程
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (C++17) std算法之执行策略 execution
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (编译到47%失败)to be deleted
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (转)memcache、redis缓存
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .Net 4.0并行库实用性演练
  • .NET Micro Framework初体验
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .NET企业级应用架构设计系列之开场白
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce
  • [20190416]完善shared latch测试脚本2.txt
  • [BZOJ4337][BJOI2015]树的同构(树的最小表示法)
  • [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)
  • [EFI]Lenovo ThinkPad X280电脑 Hackintosh 黑苹果引导文件
  • [E链表] lc83. 删除排序链表中的重复元素(单链表+模拟)
  • [FROM COM张]如何解决Nios II SBTE中出现的undefined reference to `xxx'警告
  • [linux学习]apt-get参数解析
  • [MySQL] 二进制文件