中级C++:二叉搜索树、key-Value模型


二叉搜索树的特征

  • 左子树永远比根节点小,右子树永远比根节点大
  • 也是key模型,看某个数据在不在集合里面。

在这里插入图片描述


搜索树的添加

  • key值比根值大,往右边插入,小,往左边插入
  • key值和根节点值比较,确定插入在根节点的左边还是右边,根节点一定是某个叶子结点。
  • 比如插入 4,一定是在3的右边
  • 下面的x 当作key
template<class K>
struct bstn
{
	bstn()
	{
		cout << "单个链表结点的声明初始化" << endl;
	}
	bstn(const K& x)
		:_left(nullptr)
		, _right(nullptr)
		, _value(x)
	{

	}
	bstn* _left;
	bstn* _right;
	K _value;
};

template<class K>
bool bst<K>::Insert(const K& x)
{
	return bst<K>::_Insert(x);
}

template<class K>
bool bst<K>::_Insert(const K& x)
{
	if (_root==nullptr)/ /空树的处理
	{
		_root = new Node(x);/ /调用自定义类型并用x初始化
		return true;
	}
	
	Node* parent = nullptr;/ /防止走到空找不到根节点
	Node* cur = _root;
	while (cur)
	{
		/ /比右边大,就去右子树的右边插入,
		if (x>cur->_value)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if(x<cur->_value)  / /,比根值 小去左边插入
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
			/ /这个版本的搜索二叉树不支持值相等
		}

	}
	//一定比叶子节点值 大或小 进行连接插入
	cur = new Node(x);          /new会调用自定义类型的构造函数初始化
	if (parent->_value < cur->_value)  /判断插入在根的左边还是右边
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	return true;
}


搜索树的遍历打印

  • 仿佛是二叉树的中序遍历,排出来就是升序。
template<class K>
bool bst<K>::Ergodic()
{
	return _Ergodic(_root);
}
template<class K>
bool bst<K>::_Ergodic(Node* root)
{
/视为一个满二叉树进行遍历,找到我左为空返回,根,右子树为空返回,在返回上一层;以此类推

	if (root == nullptr)
	{
		return false;
	}
	_Ergodic(root->_left);
	cout << root->_value << " ";
	_Ergodic(root->_right);
}

搜索树的查找

  • key值比根值大,往右边找;比根值小,往左边找,比如说找:8
    在这里插入图片描述
Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}
Node* _Find(const K& x)
	{
		Node* cur = _root;
		while (cur)
		{
			if (x>cur->_value)
			{
				cur = cur->_right;
			}
			else if(x<cur->_value)
			{
				cur = cur->_left;
			}
			else
			{
				/ /不小于左边,不大于右边
				return cur;
			}
		}
		return nullptr;
	}




搜索二叉树的删除

  • 先找到那个值:key值比根值大,往右边找;比根值小,往左边找,
  • 找到以后两种情况:
    1. (key值)结点的一侧为空 ,父结点连接上不为空的一侧,删除(key值)结点;5,6,8.删除5
    2. (key值)结点的两侧都不为空:找(key值)左右两边的最值替换:找左边最大的或右边最小的替换。
    3. 往根的左边走一步,如果右边不为空,然后持续往右走;最大值的右子树必然是空,往右找到空,记录父节点,然后删除最大值节点。
    4. 比如删除 2
      在这里插入图片描述
bool Erase(const K& key)
	{
		return _Erase(_root, key);
	}
template<class K>
bool bst<K>::_Erase(const K& x)  /  /x  ---》  key
{
	
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_value < x)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if(cur->_value>x)
		{
			parent = cur;
			cur = cur->_left;
		}
		else  / /上面走完之后 此处 值相等 找到
		{
			/ /左子树或右子树为空,双亲结点连接上孩子结点不为空的一侧,删除key值节点
			if (cur->_left == nullptr)//如果cur左边为空,右边不为空
			{
				if (_root == cur)//只有一个结点或单条边为空。
				{
					_root = cur->_right;
				}
				else
				{
					if (cur = parent->_left)//cur是双亲结点的左边
					{
						//则链接cur的右边
						parent->_left = cur->_right;
					}
					else  //是双亲结点的右边 且左边为空
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->_right == nullptr)
			{
				if (_root == cur)//只有一个结点或单条边为空。
				{
					_root = cur->_left;
				}
				else
				{
					if (cur = parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else  / /两边都不为空   左边的最大值节点和 key节点交换值 ,   删除最大值节点
			{

				Node* maxLeft = cur->_left;
				Node* pmax = cur;
				while (maxLeft->_right)   / /不为空就往右边走  一个值记录最值的父节点
				{
					pmax = maxLeft;
					maxLeft = maxLeft->_right;
				}
				cur->_value = maxLeft->_value;
				if (pmax->_right=maxLeft)  / /此时最大值节点的右边为空,左边还可能有子树比它小的
				{
					pmax->_right = maxLeft->_left;/ / 判断最大值节点是父结点的哪一边
				}
				else   / /让父节点接上最大值节点的左子树
				{
					pmax->_left = maxLeft->_left;
				}
				delete maxLeft;
			}
			return true;   / /删除完成返回
		}
	}
	return false;  / /走到空说明没有那个值
}



递归版本

  • 贵在每一次递归进来就是对根节点子树的引用。

递归插入

  • 插入10
    在这里插入图片描述

	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	
template<class K>
bool bst<K>::_InsertR(Node*& root, const K& key)
{
	if (root==nullptr)   / /是对8右指针的引用
	{
		root = new Node(key);  / / 8->right = .....接上即可
		return true;
	}
	else
	{
		if (root->_value < key)
			return _InsertR(root->_right, key);
		else if (root->_value > key)
			return _InsertR(root->_left, key);
		else
			return false;
	}
}


递归查找

  • 比如查找:3
    在这里插入图片描述
Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}

template<class K>
bst<K>::Node* bst<K>::_FindR(Node*& root,const K& key)  / /3
{
	if (root == nullptr)   / /2的右子树引用  3
	{
		return nullptr;
	}
	if (root->_value < key)                       / /2   
		return _FindR(root->_right, key);      / /不大
	else if (root->_value > key)  / / 5   2      / /不小
		return _FindR(root->_left, key);
	else
		return root;    / /相等返回  秒啊
}

递归删除

  • 比如删除:2
    在这里插入图片描述
    在这里插入图片描述
bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
template<class K>
bool bst<K>::_EraseR(Node*& root,const K& key)
{
	if (root == nullptr)
	{
		return false;
	}
	if (root->_value<key)
	{
		return _EraseR(root->_right, key);
	}
	else if (root->_value>key)  //5 > 2  
	{
		return _EraseR(root->_left, key);  //ture   返回....
	}
	else
	{
		Node* del = root;   //root 是 对2的引用  也是2这个节点
		if (root->_left == nullptr)
		{
			root == root->_right;		// 5的left 链接到key值不为空的一侧
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;  	//最后删除del
		}
		else  / /1  3
		{
			Node* maxLeft = root->_left;  	//root 还是2 
			while (maxLeft->_right) 		// 左孩子节点的右子树为空则  说明左孩子的值就是最大值
			{
				maxLeft = maxLeft->_right;
			}
			root->_value = maxLeft->_value;		//替换

			return _Erase(root->_left,maxLeft->key);  //再递归删除 最大值节点 
			// 2的左边引用     1 相等 ; 1的节点交给 del ;1的左边为空,  2左边的引用 链接到引用的右边;删除 del ,返回。
		}
		delete del;
		return true;
	}
}


接口总览

template<class K>
class bst
{
public:
	typedef bstn<K>  Node;
	bst()
	{
		cout << "搜索二叉树的节点" << endl;
	}
	bool Insert(const K& x);
	bool Ergodic();
	Node* Find(const K& x)
	{
		return _Find(x);
	}
	bool Erase(const K& x)
	{
		return _Erase(x);
	}
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
private:
	bool _Insert(const K& x);/ /隐藏实现细节
	bool _Ergodic(Node* root);
	bool _Erase(const K& x);
	Node* _Find(const K& x)
	{
		Node* cur = _root;
		while (cur)
		{
			if (x>cur->_value)
			{
				cur = cur->_right;
			}
			else if(x<cur->_value)
			{
				cur = cur->_left;
			}
			else
			{
				/ /不小于左边,不大于右边
				return cur;
			}
		}
		return nullptr;
	}

	bool _InsertR(Node*& root,const K& key);
	Node* _FindR(Node*& root,const K& key);
	bool _EraseR(Node*& root,const K& key);

	Node* _root;
};


key-Value 模型

  • 场景:字典翻译,统计单词出现的次数; 刷身份证进站。
  • 是对Key模型的改造:
    • 添加一个V模板参数即可,添加时多添加一个参数
    • 找到Key就能删除Value

基建

namespace KEY_VALUE
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		K _key;
		V _value;

		BSTreeNode(const K& key, const V& value)
			: _left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{}
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
	.....
	private:
	Node* _root = nullptr;
	}

key-Value的添加

bool _InsertR(Node*& root, const K& key, const V& value)
{
	if (root==nullptr)  
	{
		root = new Node(key,value);  
		return true;
	}
	else
	{
		if (root->_key < key)
			return _InsertR(root->_right, key);
		else if (root->_key > key)
			return _InsertR(root->_left, key);
		else
			return false;
	}
}

key-Value的遍历打印

bool _Ergodic(Node* root)
{
	if (root == nullptr)
	{
		return false;
	}
	_Ergodic(root->_left);
	cout << root->_key << " "<<root->_value<<endl;
	_Ergodic(root->_right);
}

key-Value的查找

Node* _FindR(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key>cur->_key)
			{
				cur = cur->_right;
			}
			else if(key<cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				//不小于左边,不大于右边
				return cur;
			}
		}
		return nullptr;
	}

key-Value的删除

bool _EraseR(Node*& root,const K& key)
{
	if (root == nullptr)
	{
		return false;
	}
	if (root->_key<key)
	{
		return _EraseR(root->_right, key);
	}
	else if (root->_key>key) 
	{
		return _EraseR(root->_left, key);  
	}
	else
	{
		Node* del = root;  
		if (root->_left == nullptr)
		{
			root == root->_right;
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;  
		}
		else  
		{
			Node* maxLeft = root->_left;  
			while (maxLeft->_right) 
			{
				maxLeft = maxLeft->_right;
			}
			root->_key = maxLeft->_key;		//替换
			root->_value =maxLeft->_value;

			return _Erase(root->_left,maxLeft->_key);  //再递归删除 最大值节点 
		}
		delete del;
		return true;
	}
}

简单字典

void TestDict()
{
	KEY_VALUE::BSTree<string, string> dict;
	dict.InsertR("crash", "崩溃");
	dict.InsertR("priority", "优先级");
	string str;
	while (cin >> str)
	{
		if (str == "Q")//quit 退出
		{
			break;
		}
		else
		{
			auto ret = dict.FindR(str);  //返回的是个结构体指针Node*
			if (ret == nullptr)
			{
				cout << "This word is not available in the dictionary, please re-enter it" << endl;
			}
			else
			{
				cout << ret->_key << "->" << ret->_value << endl;
			}
		}
	}
}

统计单词(字符串)出现次数

void TestCWord()
{
	string str[] = { "i","dont","have","a","dream","when","i","was","a","child" };
	KEY_VALUE::BSTree<string, int> countree;
	for (auto& i : str)
	{
		auto ret = countree._FindR(i);
		if (ret==nullptr)
		{
			countree._InsertR(i, 1);	//没有则进行插入,次数是第一次
		}
		else
		{
			ret->_value++;		//有则 int value ++
		}
	}
	countree._Ergodic();
}

结束

  • 按时吃饭,不要熬夜,太累了就睡觉…
    在这里插入图片描述
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设二叉排序树的二叉链表存储结构的类型定义如下: typedef struct node{ int data; //用整数表示一个结点的名 struct node *LChild,*RChild; //左右指针域 }BSTNode,*BSTree; 设计算法并编写程序求解以下几个问题。 8 12 14 10 7 3 15 6 2 4 1 5 11 9 13 16 13 (1)键盘输入一个元素序列创建一棵二叉排序树,输出该二叉排序树的中序遍历序列; 例如,若输入 45,24,55,12,37,53,60,23,40,70 则创建的二叉排序树为: 输出结果为:12 23 24 37 40 45 53 55 60 70 (2)在(1)中所得的二叉排序树中插入一个值为 58 的结点,再输出它的中序遍历序列,输出 结果为:12 23 24 37 40 45 53 55 58 60 70 (3)在(1)中所得的二叉排序树中删除值为 45 的结点,再输出它的中序遍历序列,输出结果 为:12 23 24 37 40 53 55 58 60 70 (4)利用(1)中所得的二叉排序树的所有叶子结点构造一个带头结点的单链表 L。要求不能 破坏这棵二叉排序树。所得的单链表 L 如下。 输出该链表各结点的值,输出结果为:23 40 53 70 (5)设计算法将(1)中所得的二叉排序树的左右子树进行交换,由于二叉树是一种递归定义, 所以子树的左右两棵子树也要相交换,依此类推。最后输出所得到的二叉树的中序遍历序列。 例如,经过上述操作后,(1)中所得的二叉排序树变为如下形式。 输出该二叉树的中序序列,结果为:70 60 55 53 45 40 37 24 23 12 (6)设计算法统计并输出(1)中所得的二叉排序树中只有一个孩子结点的结点个数。输出结 果为:3(7)在(1)中所得的二叉排序树中,设计算法并编写程序输出结点 40 的所有祖先结点。输 出结果为:45 24 37
### 回答1: 当平衡因子被定义为左右子树高度之差时,这种平衡的二叉搜索树通常被称为AVL树。下面是一个用C语言实现的AVL树的示例代码: ```c #include <stdio.h> #include <stdlib.h> typedef struct node { int key; int height; struct node* left; struct node* right; } node; node* new_node(int key) { node* n = (node*) malloc(sizeof(node)); n->key = key; n->height = 1; n->left = NULL; n->right = NULL; return n; } int max(int a, int b) { return (a > b) ? a : b; } int height(node* n) { if (n == NULL) return 0; return n->height; } int balance_factor(node* n) { if (n == NULL) return 0; return height(n->left) - height(n->right); } node* rotate_right(node* n) { node* left = n->left; node* right = left->right; left->right = n; n->left = right; n->height = max(height(n->left), height(n->right)) + 1; left->height = max(height(left->left), height(left->right)) + 1; return left; } node* rotate_left(node* n) { node* right = n->right; node* left = right->left; right->left = n; n->right = left; n->height = max(height(n->left), height(n->right)) + 1; right->height = max(height(right->left), height(right->right)) + 1; return right; } node* insert(node* root, int key) { if (root == NULL) return new_node(key); if (key < root->key) root->left = insert(root->left, key); else if (key > root->key) root->right = insert(root->right, key); else return root; root->height = 1 + max(height(root->left), height(root->right)); int bf = balance_factor(root); if (bf > 1 && key < root->left->key) return rotate_right(root); if (bf < -1 && key > root->right->key) return rotate_left(root); if (bf > 1 && key > root->left->key) { root->left = rotate_left(root->left); return rotate_right(root); } if (bf < -1 && key < root->right->key) { root->right = rotate_right(root->right); return rotate_left(root); } return root; } void inorder_traversal(node* root) { if (root != NULL) { inorder_traversal(root->left); printf("%d ", root->key); inorder_traversal(root->right); } } int main() { node* root = NULL; root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); root = insert(root, 50); root = insert(root, 25); printf("Inorder traversal of the AVL tree is: "); inorder_traversal(root); printf("\ ### 回答2: 当然可以使用 C 语言编写一个由平衡因子实现二叉搜索树的代码。平衡二叉搜索树,即 AVL 树,是一种自平衡的二叉搜索树,它可以保证在最坏情况下的查询、插入和删除操作的时间复杂度为 O(log n)。 以下是一个用 C 语言实现 AVL 树的简单代码示例: ```c #include <stdio.h> #include <stdlib.h> typedef struct Node { int value; int height; struct Node* left; struct Node* right; } Node; int max(int a, int b) { return (a > b) ? a : b; } int height(Node* node) { if (node == NULL) { return 0; } return node->height; } int getBalance(Node* node) { if (node == NULL) { return 0; } return height(node->left) - height(node->right); } Node* rotateRight(Node* node) { Node* newRoot = node->left; Node* tempNode = newRoot->right; newRoot->right = node; node->left = tempNode; node->height = 1 + max(height(node->left), height(node->right)); newRoot->height = 1 + max(height(newRoot->left), height(newRoot->right)); return newRoot; } Node* rotateLeft(Node* node) { Node* newRoot = node->right; Node* tempNode = newRoot->left; newRoot->left = node; node->right = tempNode; node->height = 1 + max(height(node->left), height(node->right)); newRoot->height = 1 + max(height(newRoot->left), height(newRoot->right)); return newRoot; } Node* insert(Node* node, int value) { if (node == NULL) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->value = value; newNode->height = 1; newNode->left = NULL; newNode->right = NULL; return newNode; } if (value < node->value) { node->left = insert(node->left, value); } else if (value > node->value) { node->right = insert(node->right, value); } else { return node; } node->height = 1 + max(height(node->left), height(node->right)); int balance = getBalance(node); if (balance > 1 && value < node->left->value) { return rotateRight(node); } if (balance < -1 && value > node->right->value) { return rotateLeft(node); } if (balance > 1 && value > node->left->value) { node->left = rotateLeft(node->left); return rotateRight(node); } if (balance < -1 && value < node->right->value) { node->right = rotateRight(node->right); return rotateLeft(node); } return node; } void inorderTraversal(Node* node) { if (node != NULL) { inorderTraversal(node->left); printf("%d ", node->value); inorderTraversal(node->right); } } int main() { Node* root = NULL; root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); root = insert(root, 50); root = insert(root, 25); printf("中序遍历结果:"); inorderTraversal(root); return 0; } ``` 这段代码定义了一个 `Node` 结构体用于表示 AVL 树的节点,包括节点的值、高度以及左右子节点的指针。代码中实现了插入操作 `insert`,以及右旋和左旋操作 `rotateRight` 和 `rotateLeft`。`getBalance` 函数用于计算节点的平衡因子,`max` 函数用于返回两个数中的较大值,`height` 函数用于返回节点的高度。`inorderTraversal` 函数用于中序遍历 AVL 树,方便验证插入操作的结果。 在 `main` 函数中,使用示例数据插入了若干节点,并对 AVL 树进行了中序遍历打印结果。 这只是一个简单的 AVL 树的实现示例,完整的 AVL 树的实现需要考虑更多边界条件和操作,例如删除节点、查找节点等。 ### 回答3: 当然可以用C语言编写一个由平衡因子实现的二叉搜索树代码。 首先,我们需要定义一个二叉树节点的数据结构,代码如下: ``` typedef struct TreeNode { int val; int balance_factor; // 平衡因子 struct TreeNode* left; struct TreeNode* right; } TreeNode; ``` 接下来,我们需要实现插入和平衡化操作。插入操作将根据节点的值来插入新节点,并随之更新节点的平衡因子。平衡化操作会检查节点的平衡因子,如果不平衡则通过旋转来调整树的结构。 插入操作的代码如下: ``` TreeNode* insert(TreeNode* root, int val) { if (root == NULL) { TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); node->val = val; node->balance_factor = 0; node->left = NULL; node->right = NULL; return node; } if (val < root->val) { root->left = insert(root->left, val); } else if (val > root->val) { root->right = insert(root->right, val); } root->balance_factor = get_balance_factor(root); root = balance(root); return root; } ``` 平衡化操作的代码如下: ``` TreeNode* balance(TreeNode* node) { int bf = get_balance_factor(node); if (bf > 1) { if (get_balance_factor(node->left) < 0) { node->left = rotate_left(node->left); } node = rotate_right(node); } else if (bf < -1) { if (get_balance_factor(node->right) > 0) { node->right = rotate_right(node->right); } node = rotate_left(node); } return node; } ``` 以上代码中,`get_balance_factor`函数用于计算节点的平衡因子,`rotate_left`函数用于左旋操作,`rotate_right`函数用于右旋操作。这些函数的具体实现根据具体需求来编写。 总体来说,这段代码实现了一个由平衡因子实现的二叉搜索树,可以通过插入和平衡化操作来维护树的平衡性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值