二叉树----C++学习(3)

二叉搜索树

待定

平衡搜索树

定义:在二叉搜索树的基础上,增加节点左右子树高度差的特点。平衡二叉树中的每个节点的左右子树高度差小于等于1
在插入和删除时会出现不平衡的情况,这个时候就需要根据不平衡的节点进行调整,有四种情况:LL,RR,LR,RL

LL

在这里插入图片描述
蓝色节点左右子树相差为2,此处应该使用单左旋进行调整
因为平衡树又有搜索树的特性,能够看出橘黄色大于黄色小于蓝色,因此应该调整橘黄色为根节点,蓝色调整为橘黄色节点的右子树。如果橘黄色原先有右子树,那么该右子树的值都大于橘黄色节点且小于蓝色节点,所以可以作为调整后蓝色节点的左子树

RR

在这里插入图片描述
蓝色节点左右子树相差为2,此处应该使用单右旋进行调整

LR

在这里插入图片描述
在LR的情况下,应该从下向上的方向进行调整,先将橘黄色和和黄色节点进行RR操作,在对蓝色节点实施LL操作

RL

在这里插入图片描述
在RL的情况下,同样应该从下向上的方向进行调整,先将橘黄色和和黄色节点进行LL操作,在对蓝色节点实施RR操作

在平衡二叉树中,其实主要的操作还是插入和删除节点,在这个过程中需要注意调整每个节点的高度

插入

在插入操作中,因为不确定最终会插入到哪个叶子节点上,因此经过的叶子节点的高度只能在插入后才能确定
因此使用递归的方式
在未找到指定插入地点时,一直查找,直到插入后,从底向上依次返回对应节点执行未完成的操作,调整平衡和更新高度

当xdata,有两种情况:LL,LR 也可能不需要调整
1、当xleft->data,则属于LL
2、当x>root->left->data,则属于LR
当x>root->data,有两种情况:RR,RL
3、当x > root->right->data,则属于RR
4、当x < root->right->data,则属于RL

删除

删除的话不同于插入,删除的节点有可能是叶子节点也可能是中间的某个节点。
因为不确定性,因此同样使用递归的方式进行删除
递归的方式寻找删除的节点,在找到对应的节点后,首先判断他的左右子树是否为空
1、若都不为空,则为了二叉树的平衡,需要判断两个子树哪个更高
若左树更高,则找到左树的最大值,将其替换掉要删除的节点,然后使用递归删除左树最大值节点
若右树更高,则找到右树的最小值,将其替换掉要删除的节点,然后使用递归删除右树最小值节点
2、若其中一个为空,则直接将另一个子树的根节点替换掉该节点即可,为了指针实参能够跟随形参指向地址的改变而改变,因此传参时使用的引用指针 : *&p
因为删除节点的位置下方,由于Delete替换的叶子节点,将下方的子树进行了调整和更新高度,因此该位置无需操作
直接依次返回上一个节点实施调整和更新高度的操作
当xdata,有两种情况:LL,LR 也可能不需要调整
1、当xleft->data,则属于LL
2、当x>root->left->data,则属于LR
当x>root->data,有两种情况:RR,RL
3、当x > root->right->data,则属于RR
4、当x < root->right->data,则属于RL
代码:
AvlTree.h

#include <iostream>
#include <algorithm>
using namespace std;

#pragma once

//平衡二叉树节点
struct AvlNode
{
	int data;
	int height;//节点的高度
	AvlNode *left;
	AvlNode *right;
	AvlNode(const int theData) :data(theData), left(NULL), right(NULL), height(0) {}
};

//平衡二叉树

class AvlTree
{
public:
	AvlTree() {};
	~AvlTree() {};
	AvlNode *root;//根节点
	//1、插入节点
	void Insert(AvlNode *&t, int x);
	//2、删除节点
	bool Delete(AvlNode *&t, int x);
	//3、查找是否存在给定值的节点
	bool Contains(AvlNode *t, const int x)const;
	//4、中序遍历
	void InorderTraversal(AvlNode *t);
	//5、前序遍历
	void PreorderTraversal(AvlNode *t);
	//6、最小值节点
	AvlNode *FindMin(AvlNode *t) const;
	//7、最大值节点
	AvlNode *FindMax(AvlNode *t) const;
private:
	//8、求树的高度
	int GetHeight(AvlNode* t);
	//9、单旋转 左
	AvlNode* LL(AvlNode* t);
	//10、单旋转 右
	AvlNode* RR(AvlNode* t);
	//11、双旋转 右左
	AvlNode* RL(AvlNode* t);
	//12、双旋转 左右
	AvlNode* LR(AvlNode* t);
};

//1、插入节点
void AvlTree::Insert(AvlNode *&t, int x)
{
	if (t == NULL) t = new AvlNode(x);
	else if(x<t->data)
	{
		Insert(t->left, x);
		//判断平衡情况
		if (GetHeight(t->left) - GetHeight(t->right) > 1)
		{
			//分两种情况 左左  左右
			if (x < t->left->data)
			{
				t=LL(t);
			}
			else
			{
				t = LR(t);
			}
		}
	}
	else if (x > t->data)
	{ 
		Insert(t->right, x);
		//判断平衡情况
		if (GetHeight(t->left) - GetHeight(t->right) < -1)
		{
			//分两种情况 右右   右左
			if (x > t->right->data)
			{
				t = RR(t);
			}
			else
			{
				t = RL(t);
			}
		}
	}
	else//数据重复
	{
		//no operate
	}

	//更新t的height
	t->height = max(GetHeight(t->left), GetHeight(t->right))+1;
}
//2、删除节点
bool AvlTree::Delete(AvlNode *&t, int x)
{
	bool ans = false;
	//t为空 未找到要删除的节点
	if (t == NULL) return false;
	//找到要删除的节点
	else if (t->data == x)
	{
		//左右子树都非空
		if (t->left != NULL && t->right != NULL)
		{
			//在高度更高的子树上进行删除操作,实际上就是将更高的子树的节点替代要删除的节点
			if (GetHeight(t->left) > GetHeight(t->right))
			{
				t->data = FindMax(t->left)->data;//更新节点值
				Delete(t->left, t->data);//删除左子树中的最大值
			}
			else
			{
				t->data = FindMin(t->right)->data;//更新节点值
				Delete(t->right, t->data);//删除右子树中的最小值
			}
			//t->height=max(GetHeight(t->left),GetHeight(t->right));
		}
		else
		{
			//左右子树有一个不为空,直接用需要删除的节点的子节点替换即可    当然也可能两个子树都为空
			AvlNode *old = t;
			t = t->left ? t->left : t->right;
			delete old;
		}
	}
	else if (x < t->data)//要删除的节点在左子树上
	{
		Delete(t->left, x);
		//判断是否满足平衡条件
		if (GetHeight(t->left) - GetHeight(t->right)<-1)
		{
			if (t->left->data < x)
			{
				t=LL(t);
			}
			else
			{
				t = LR(t);
			}
		}
		else
		{
			t->height = max(GetHeight(t->left), GetHeight(t->right))+1;
		}
	}
	else
	{
		Delete(t->right, x);
		//判断是否满足平衡条件
		if (GetHeight(t->left) - GetHeight(t->right) > 1)
		{
			if (t->right->data > x)
			{
				t = RR(t);
			}
			else
			{
				t = RL(t);
			}
		}
		else
		{
			t->height = max(GetHeight(t->left), GetHeight(t->right))+1;
		}
	}
	return true;

}
//3、查找是否存在给定值的节点
bool AvlTree::Contains(AvlNode *t, const int x)const
{
	if (t == NULL) return false;
	if (x < t->data)
		return Contains(t->left, x);
	else if (x > t->data)
		return Contains(t->right, x);
	else
		return true;

}
//4、中序遍历
void AvlTree::InorderTraversal(AvlNode *t)
{
	if (t)
	{
		InorderTraversal(t->left);
		cout << t->data << " ";
		InorderTraversal(t->right);
	}
}
//5、前序遍历
void AvlTree::PreorderTraversal(AvlNode *t)
{
	if (t)
	{
		cout << t->data << " ";
		PreorderTraversal(t->left);
		PreorderTraversal(t->right);
	}
}
//6、最小值节点
AvlNode * AvlTree::FindMin(AvlNode *t) const
{
	/*if (t == NULL) return NULL;
	while (t->left != NULL)
	{
		t = t->left;
	}
	return t;*/
	if (t == NULL) return NULL;
	if (t->left == NULL) return t;
	return FindMin(t->left);
}
//7、最大值节点
AvlNode * AvlTree::FindMax(AvlNode *t) const
{
	/*if (t == NULL) return NULL;
	while (t->right != NULL)
	{
		t = t->right;
	}
	return t;*/
	if (t == NULL) return NULL;
	if (t->right == NULL) return t;
	return FindMax(t->right);
}

//8、求树的高度
int AvlTree::GetHeight(AvlNode* t)
{
	if (t != NULL)
	{
		return t->height;
	}
	return -1;
}
//9、单旋转 左
AvlNode* AvlTree::LL(AvlNode* t)
{
	AvlNode *q = t->left;
	t->left = q->right;
	q->right = t;
	t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
	q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
	return q;
}
//10、单旋转 右
AvlNode* AvlTree::RR(AvlNode* t)
{
	AvlNode *q = t->right;
	t->right = q->left;
	q->left = t;
	t->height = max(GetHeight(t->left), GetHeight(t->right))+1;
	q->height = min(GetHeight(q->left), GetHeight(q->right))+1;
	return q;
}
//11、双旋转 右左    
AvlNode* AvlTree::RL(AvlNode* t)
{
	//双旋转可以通过两次单旋转实现
	//对t的右节点施行LL旋转,在对t实施RR
	LL(t->right);
	return RR(t);
}
//12、双旋转 左右
AvlNode* AvlTree::LR(AvlNode* t)
{
	该方法也可以
	//AvlNode* q = t->left;
	//AvlNode* p = q->right;

	//q->right = p->left;
	//t->left = p->right;
	//p->left = q;
	//p->right = t;
	//t->height = max(GetHeight(t->left), GetHeight(t->right));
	//q->height = max(GetHeight(q->left), GetHeight(t->right));
	//p->height = max(GetHeight(p->left), GetHeight(p->right));
	//return p;

	//使用两次单旋实现
	//对t的左节点先施行RR,再对t实施LL
	RR(t->left);
	return LL(t);
}

测试:
AvlTree.cpp

#include "AvlTree.h"


int main()
{
	AvlTree Tree;

	int val;
	int tmp;
	cout << "请输入整数建立二叉树(-1结束):" << endl;
	while (cin >> val)
	{
		if (val == -1) break;
		Tree.Insert(Tree.root, val);
	}
	cout << "中序遍历"<<endl;
	Tree.InorderTraversal(Tree.root);
	cout << "前序遍历"<<endl;
	Tree.PreorderTraversal(Tree.root);
	cout << "请输入要查找的节点:" << endl;
	cin >> tmp;
	if (Tree.Contains(Tree.root, tmp))
		cout << "已找到" << endl;
	else
		cout << "值为" << tmp << "的节点不存在" << endl;
	cout << "请输入要删除的节点:";
	cin >> tmp;
	Tree.Delete(Tree.root, tmp);
	cout << "删除后的中序遍历:"<<endl;
	Tree.InorderTraversal(Tree.root);
	cout << "删除后的前序遍历:"<<endl;
	Tree.PreorderTraversal(Tree.root);
}

红黑树

待定

前缀树(字典树)

前缀树,通过复用节点,能够极大地节省为保存大量字符串的存储开销

在这里插入图片描述
每个节点中含有
1、字符串结束标志isend:true代表结尾 false代表不是结尾
2、根据下一个字符指向的 节点指针数组
主要的操作:
插入:实际上就是遍历前缀树,已经有的前缀直接查找下一个节点,不匹配的为该字符创建一个指针指向新建的节点
最后一个节点注意把isend置为true
搜索:除了需要全部匹配,还要检查最后一个节点的isend的标识是不是true
前缀匹配:只需要字符全部匹配就可以
删除:这个的话比较麻烦,我选择的是递归
删除一般分为三种情况:
1、从头到尾都要删除,只留一个头节点
2、较长,删除一半,中间有isend为true
3、较短,不需要删除,只需要将isend置为false即可
过程:通过递归遍历到最后一个节点,先将该节点isend置为false,如果该节点包含其他节点指针,直接返回false;否则返回true
上一层收到返回的false,则直接返回false;收到true,说明需要删除字符对应的指针。删除后需要判断是否是头节点,是否isend为true,是否还有其他字符对应的节点指针,是的话全部返回false;否则需要删除该节点,返回true。再执行上一层递归,直到到头节点。
代码:
Trie.h

#include<unordered_map>
#include<string>

using namespace std;
#pragma once

class Trie
{
private:
	bool isend= false;//是否是字符串结尾
	unordered_map<char, Trie*> dict;//保存的分叉
public:
	Trie()
	{
		isend = false;
	}
	//向前缀树中插入字符串
	void insert(string word)
	{
		Trie* p = this;//遍历指针
		for (auto ch : word)
		{
			if (p->dict.count(ch) == 0)
			{
				p->dict[ch] = new Trie();//为该节点增加分叉
			}
			p = p->dict[ch];
		}
		p->isend = true;
	}
	//字符串是否已经在前缀树中
	bool search(string word)
	{
		Trie* p = this;
		for (auto ch : word)
		{
			if (p->dict.count(ch) == 0)
			{
				return false;
			}
			p=p->dict[ch];
		}
		if (p->isend == false)
		{
			return false;
		}
		return true;
	}
	//前缀匹配
	bool startsWith(string prefix)
	{
		Trie* p = this;
		for (auto ch : prefix)
		{
			if (p->dict.count(ch) == 0)
			{
				return false;
			}
			p = p->dict[ch];
		}
		return true;
	}
	//删除字符串:首先判断字符串是否在前缀树中,然后使用递归的方式删除字符串
	//会存在三种情况:1、删除整条2、删除一部分3、只是修改最后一个节点的isend状态为false
	bool del_word(string word)
	{
		if (!search(word)) return false;

		Trie* p = this;
		del(p, word, 0);

		return true;
	}
	
	bool del(Trie* p ,string word,int i)
	{
		if (word.size() == i)
		{
			p->isend = false;
			if (p->dict.size() != 0)
			{
				return false;
			}
			else
			{
				delete p;
				return true;
			}
		}
		if (del(p->dict[word[i]], word, i + 1))
		{
			p->dict.erase(word[i]);//删除对应字符

			if (p->isend == true)//防止删除比自己短的字符串的结尾节点
			{
				return false;
			}

			if (p->dict.size() != 0)//防止删除还有其他字符的节点
			{
				return false;
			}
			else
			{
				if (p == this)//防止删掉最后一个节点
				{
					return true;
				}
				delete p;//删除对应节点
				return true;
			}
		}
	    
		return false;
	}
};


测试:
Trie.cpp

#include "Trie.h"
#include<iostream>



int main()
{
	string s1 = "qwert";
	string s2 = "qwe";
	string s3 = "qwerdc";

	Trie tree;

	tree.insert(s1);
	tree.insert(s2);
	tree.insert(s3);
	if (tree.search(s1))
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
	if (tree.search(s2))
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
	if (tree.search(s3))
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}



	tree.del_word(s1);
	//tree.del_word(s2);
	tree.del_word(s3);




	if (tree.search(s1))
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
	if (tree.search(s2))
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
	if (tree.search(s3))
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月光在发光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值