C++二叉搜索树模拟实现

news2024/11/27 23:46:35

目录

一、二叉搜索树的概念

二、二叉搜索树的结构

三、二叉搜索树的操作(非递归)

1.插入

2.查找

3.删除

4.遍历

四、二叉搜索树的操作(递归)

1.递归插入

2.递归查找

3.递归删除

4.递归遍历

五、二叉搜索树的默认成员函数

1.拷贝构造

2.赋值运算符重载

3.析构函数

4.默认构造函数

六、二叉搜索树的KV模型


一、二叉搜索树的概念

搜索二叉树结构上跟普通的二叉树一样,它要么是一棵空树,要么是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

如下图所示,这就是一颗二叉搜索树。我们可以发现这颗树的中序遍历就是有序的

二、二叉搜索树的结构

首先我们肯定需要树节点,来帮助我们保持树的结构和存放数据。

如下代码我们就使用模板定义了树的结构,并添加了构造函数,方便我们创建结点

template<class T>
struct BSTreeNode
{
	BSTreeNode<T>* _left;
	BSTreeNode<T>* _right;
	T _key;
	BSTreeNode(const T& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

二叉树的成员变量只有根结点

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
private:
    Node* _root
}

三、二叉搜索树的操作(非递归)

1.插入

我们定义了一个数组,现在要将这个数组的内容放到搜索二叉树里。

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

 就会成为如下这棵树

依次插入0和16

        插入部分代码还算简单,首先就判断当前树是否为空,通过成员变量根结点来判断,根节点为空就直接new一个结点赋值给根结点返回true

        我们可以通过key值来比较往左走还是往右走,如果插入的key值比当前结点的值小,就往左走,如果比当前结点的值大,就往右走,如果相等,就证明你找到了结点,我们返回false,代表插入失败(二叉搜索树为了保持他的结构,是不能有相等的key的)。

  •  目前我们已经找到了这个结点应该存放的位置,那么我们如何将他们链接起来呢?

        这就需要一个父结点来帮助我们处理,让父结点一直跟随着当前结点走。直到找到应该存在的地方之后,再判断这个地方是父亲的左还是右(同样是用key来比较,key比父结点的值小,放在父结点的左边,大就放在父结点的右边,通过new一个结点来完成)最后返回true。

代码如下

	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		if (parent->_key > key)
		{
			parent->_left = new Node(key);
		}
		else
		{
			parent->_right = new Node(key);
		}
		return true;
	}

2.查找

查找是插入的简洁版,依然用key来比较,找到了返回true,没找到就返回false

bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			cur = cur->_left;
		}
		else
		{
			return true;
		}
	}
	return false;
}

 3.删除

删除部分是二叉搜索树较难的地方,因为我们得让这棵树删除之后依然保持原来的特性,也就是

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

因此我们得考虑很多种情况 

 首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情 况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程 如下:

情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点 中,再来处理该结点的删除问题--替换法删除

对于情况b和c,我们首先要判断找到的结点是否为根节点,如果是,根节点就会往相应方向走

左为空 

右为空也是同理,我们就不多赘述了。 

对于情况d我们使用替换法删除,比如删除4,我们可以拿4的右子树中的最小值(也就是3)和4交换,交换之后再去删除下面那个结点就行。这样也不会破坏数的结构,

当然你选择删除节点的左子树的最大值也是可行的

知道了先交换之后,我们也要分情况来看,判断右树的第一个节点是否为最左节点 

 

 代码如下

bool Erase(const K& key)
{
	Node* parent = _root;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			//左为空
			if (cur->_left == nullptr)
			{
				//判断是否为根节点
				if (_root == cur)
				{
					_root = cur->_right;
				}
				//判断是父亲的左还是父亲的右
				else if (parent->_left == 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 (parent->_left == cur)
				{
					parent->_left = cur->_left;
				}
				else
				{
					parent->_right = cur->_left;
				}
				delete cur;
			}
			else
			{
				parent = cur;
				//定义右树的最左节点
				Node* subleft = cur->_right;
				//往最左走
				while (subleft->_left)
				{
					parent = subleft;
					subleft = subleft->_left;
				}
				//交换
				swap(subleft->_key, cur->_key);
				//右树第一个节点就是最左节点(没有进入循环)
				if (parent == cur)
				{
					parent->_right = subleft->_right;
				}
				else
				{
					parent->_left = subleft->_right;
				}
				delete subleft;
			}
			return true;
		}
	}
	return false;
}

 4.遍历

对于搜索二叉树,我们选择中序遍历,因为这样遍历出来的值是有序的。

这里我们先用非递归版本,递归版本在后面会写上。

使用stack来帮助我们操作,这里代码的重要点就是cur,对cur进行操作防止再次陷入循环。

循环着先往最左子树走,边走边入栈,走到为空就结束循环,当前取出的栈顶元素就是最左结点,先打印,再将 cur = top->_right;往top结点的右边走,如此循环,便能中序遍历。

代码真的很巧妙,不理解的可以画递归图来帮助理解。

void InOrder()
{
	stack<Node*> s;
	Node* cur = _root;
	while (cur || !s.empty())
	{
		while (cur)
		{
			s.push(cur);
			cur = cur->_left;
		}
		Node* top = s.top();
		s.pop();
		cout << top->_key << " ";
		cur = top->_right;
	}
	cout << endl;
}

我们测试一下,代码正常运行。

四、二叉搜索树的操作(递归)

1.递归插入

我们为了能够进行递归,需要再写一个函数,因为无法传入_root进行递归,这里我们将递归的函数用private修饰,防止类外调用

代码逻辑跟普通的差不都,都是通过key来判断,key比当前结点的值大,就递归当前节点的右边,key比当前结点的值小,就递归当前节点的左边,相等就返回。

重要的点是我们无法找到父亲结点,因此可以选择引用传参,你给到的参数是root->left或者是   root->right,引用传参传递的就是root->left或者是root->right的别名,因此找到合适的位置可以直接 root = new Node(key);

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

 2.递归查找

依然是用两个函数,查找代码比插入更简单,不多赘述

public:
bool FindR(const K& key)
{
	return _FindR(_root,key);
}
private:
bool _FindR(Node* root,const K& key)
{
	if (root == nullptr)
		return false;
	if (root->_key > key)
	{
		return _FindR(root->_left, key);
	}
	else if(root->_key < key)
	{
		return _FindR(root->_right, key);
	}
	else
	{
		return true;
	}
}

 3.递归删除

删除也得使用引用,因为要让父亲结点指向空,因为传递的是别名,不需要让父亲跟随了,因此代码可以简洁不少,情况d(也就是左右都不为空),依然是用之前的交换,交换后再递归到右树去查找即可。

public:
bool EraseR(const K& key)
{
	return _EraseR(_root, key);
}
private:
bool _EraseR(Node*& root, const K& key)
{
	if (root == nullptr)
	{
		return false;
	}
	if (root->_key > key)
	{
		return _EraseR(root->_left, key);
	}
	else if (root->_key < key)
	{
		return _EraseR(root->_right, key);
	}
	else
	{
		if (root->_left == nullptr)
		{
			Node* del = root;
			root = root->_right;
			delete del;
			return true;
		}
		else if (root->_right == nullptr)
		{
			Node* del = root;
			root = root->_left;
			delete del;
			return true;
		}
		else
		{
			Node* subleft = root->_right;
			while (subleft->_left)
			{
				subleft = subleft->_left;
			}
			swap(subleft->_key, root->_key);

			//到右子树再去递归删除
			return _EraseR(root->_right, key);
		}
	}
	return false;
}

 4.递归遍历

更是小菜一碟,这是普通二叉树的操作,递归左,打印,递归右即可。

public:
void InOrderR()
{
	_InOrderR(_root);
	cout << endl;
}
private:
void _InOrderR(Node* root)
{
	if (root == nullptr)
		return;
	_InOrderR(root->_left);
	cout << root->_key << " ";
	_InOrderR(root->_right);
}

 五、二叉搜索树的默认成员函数

1.拷贝构造

想要完成深拷贝,就需要将树的所有结点都拷贝一遍,这里用递归处理也是很方便的,因此我们定义了一个Copy函数,去进行先序遍历拷贝。

public:
BSTree(const BSTree<K>& bst)
{
	_root = Copy(bst._root);
}
private:
Node* Copy(Node* root)
{
	if (root == nullptr)
		return nullptr;
	Node* cur = new Node(root->_key);
	cur->_left = Copy(root->_left);
	cur->_right = Copy(root->_right);
	return cur;
}

2.赋值运算符重载

这个很简单,不要传递 const&  直接交换即可。

BSTree<K>& operator=(BSTree<K> bst)
{
	swap(bst._root, _root);
	return *this;
}

3.析构函数

调用后续递归函数进行析构

public:
~BSTree()
{
	clear(_root);
}
private:
void clear(Node* root)
{
	if (root == nullptr)
		return;
	clear(root->_left);
	clear(root->_right);
	delete root;
}

4.默认构造函数

 因为我们重写了构造函数,因此编译器默认构造函数的就不提供了,我们随便写一份,这里使用了C++11的新特性,default代表默认生成

BSTree() = default;

到目前为止,我们的二叉搜索树就算完成了,以下是代码

namespace k
{
	template<class T>
	struct BSTreeNode
	{
		BSTreeNode<T>* _left;
		BSTreeNode<T>* _right;
		T _key;
		BSTreeNode(const T& key)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
		{}
	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	public:
		
		BSTree() = default;

		BSTree(const BSTree<K>& bst)
		{
			_root = Copy(bst._root);
		}

		~BSTree()
		{
			clear(_root);
		}

		BSTree<K>& operator=(BSTree<K> bst)
		{
			swap(bst._root, _root);
			return *this;
		}


		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			if (parent->_key > key)
			{
				parent->_left = new Node(key);
			}
			else
			{
				parent->_right = new Node(key);
			}
			return true;
		}

		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

		bool Erase(const K& key)
		{
			Node* parent = _root;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					//左为空
					if (cur->_left == nullptr)
					{
						//判断是否为根节点
						if (_root == cur)
						{
							_root = cur->_right;
						}
						//判断是父亲的左还是父亲的右
						else if (parent->_left == 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 (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
						delete cur;
					}
					else
					{
						parent = cur;
						//定义右树的最左节点
						Node* subleft = cur->_right;
						//往最左走
						while (subleft->_left)
						{
							parent = subleft;
							subleft = subleft->_left;
						}
						//交换
						swap(subleft->_key, cur->_key);
						//右树第一个节点就是最左节点(没有进入循环)
						if (parent == cur)
						{
							parent->_right = subleft->_right;
						}
						else
						{
							parent->_left = subleft->_right;
						}
						delete subleft;
					}
					return true;
				}
			}
			return false;
		}

		void InOrder()
		{
			stack<Node*> s;
			Node* cur = _root;
			while (cur||!s.empty())
			{
				while (cur)
				{
					s.push(cur);
					cur = cur->_left;
				}
				Node* top = s.top();
				s.pop();
				cout << top->_key << " ";
				cur = top->_right;
			}
			cout << endl;
		}

		void InOrderR()
		{
			_InOrderR(_root);
			cout << endl;
		}

		bool FindR(const K& key)
		{
			return _FindR(_root,key);
		}

		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}

		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

	private:

		void clear(Node* root)
		{
			if (root == nullptr)
				return;
			clear(root->_left);
			clear(root->_right);
			delete root;
		}

		Node* Copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;
			Node* cur = new Node(root->_key);
			cur->_left = Copy(root->_left);
			cur->_right = Copy(root->_right);
			return cur;
		}
		void _InOrderR(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrderR(root->_left);
			cout << root->_key << " ";
			_InOrderR(root->_right);
		}
		bool _FindR(Node* root,const K& key)
		{
			if (root == nullptr)
				return false;
			if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else if(root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else
			{
				return true;
			}
		}
		bool _InsertR(Node*& root,const K& key)
		{
			if(root==nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else
			{
				return false;
			}
		}
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else
			{
				if (root->_left == nullptr)
				{
					Node* del = root;
					root = root->_right;
					delete del;
					return true;
				}
				else if (root->_right == nullptr)
				{
					Node* del = root;
					root = root->_left;
					delete del;
					return true;
				}
				else
				{
					Node* subleft = root->_right;
					while (subleft->_left)
					{
						subleft = subleft->_left;
					}
					swap(subleft->_key, root->_key);

					//到右子树再去递归删除
					return _EraseR(root->_right, key);
				}
			}
			return false;
		}
	private:
		Node* _root = nullptr;
	};
}

六、二叉搜索树的KV模型

二叉搜索树不仅仅有K模型,还有KV模型,我们只需要给结点多添加上一个value即可

template<class T, class V>
struct BSTreeNode
{
	BSTreeNode<T,V>* _left;
	BSTreeNode<T,V>* _right;
	T _key;
	V _value;
	BSTreeNode(const T& key,const V&value)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
		,_value(value)
	{}
};

代码逻辑部分都是没问题的,只有少部分地方需要略做修改,大家直接看代码吧

namespace kv
{
	template<class T, class V>
	struct BSTreeNode
	{
		BSTreeNode<T,V>* _left;
		BSTreeNode<T,V>* _right;
		T _key;
		V _value;
		BSTreeNode(const T& 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:
		bool Insert(const K& key,const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key,value);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			if (parent->_key > key)
			{
				parent->_left = new Node(key,value);
			}
			else
			{
				parent->_right = new Node(key, value);
			}
			return true;
		}

		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{ 
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			Node* parent = _root;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (_root == cur)
						{
							_root = cur->_right;
						}
						else if (parent->_left == 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 (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
						delete cur;
					}
					else
					{
						parent = cur;
						Node* subleft = cur->_right;
						while (subleft->_left)
						{
							parent = subleft;
							subleft = subleft->_left;
						}
						swap(subleft->_key, cur->_key);
						if (parent == cur)
						{
							parent->_right = subleft->_right;
						}
						else
						{
							parent->_left = subleft->_right;
						}
						delete subleft;
					}
					return true;
				}
			}
			return false;
		}

		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}

		bool InsertR(const K& key,const V& value)
		{
			return _InsertR(_root, key,value);
		}

		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

	private:

		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_key << " " << root->_value << endl;
			_InOrder(root->_right);
		}
		Node* _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return nullptr;
			if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else
			{
				return root;
			}
		}
		bool _InsertR(Node*& root, const K& key,const V& value)
		{
			if (root == nullptr)
			{
				root = new Node(key,value);
				return true;
			}
			if (root->_key > key)
			{
				return _InsertR(root->_left, key,value);
			}
			else if (root->_key < key)
			{
				return _InsertR(root->_right, key, value);
			}
			else
			{
				return false;
			}
		}
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else
			{
				if (root->_left == nullptr)
				{
					Node* del = root;
					root = root->_right;
					delete del;
					return true;
				}
				else if (root->_right == nullptr)
				{
					Node* del = root;
					root = root->_left;
					delete del;
					return true;
				}
				else
				{
					Node* subleft = root->_right;
					while (subleft->_left)
					{
						subleft = subleft->_left;
					}
					swap(subleft->_key, root->_key);

					//到右子树再去递归删除
					return _EraseR(root->_right, key);
				}
			}
			return false;
		}
	private:
		Node* _root = nullptr;
	};
}

KV模型测试

如此一来我们就算大功告成了。

谢谢大家观看!!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1160398.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ Qt QLineEdit如何响应回车事件

在Qt开发中,回车键的处理很常见,本篇博客介绍在QLineEdit里回车键的处理方法,例如下面的界面: QLineEdit回车键的处理有方式,一是链接returnPressed信号,二是用事件过滤。下面分别介绍这两种方式。 一、returnPressed信号 可以查看QLineEdit的头文件声明,有如下信号:…

python3 阿里云api进行巡检发送邮件

python3 脚本爬取阿里云进行巡检 不确定pip能不能安装上&#xff0c;使用时候可以百度一下&#xff0c;脚本是可以使用的&#xff0c;没有问题的 太长时间了&#xff0c;pip安装依赖忘记那些了&#xff0c;使用科大星火询问了下&#xff0c;给了下面的&#xff0c;看看能不能使…

成集云 | 钉钉集成用友T费用报销付款接口 |解决方案

源系统成集云目标系统 方案介绍 钉钉是一款免费沟通和协同的多端平台&#xff0c;提供PC版、Web版和手机版&#xff0c;支持手机和电脑间文件互传。钉钉帮助中国企业通过系统化的解决方案&#xff08;微应用&#xff09;&#xff0c;提升中国企业的沟通和协同效率。应用场景包…

VMware Workstation 17安装教程之设置系统模式

接上文&#xff0c;设置系统模式 返回到安装概要界面后&#xff0c;右侧第一个Installation Destination指的是想把系统安装到哪个硬盘。此时仅仅是让我们进行确认&#xff0c;不需要进行任何修改。 设置系统安装设备 关闭kdump服务 配置网络信息 当在虚拟机中做实验的时候&…

报文的路由过程

路由转发过程 记住路由转发过程结论&#xff1a;报文ip是不变&#xff0c;mac改变。 mac地址在同一个广播域传输过程中是不变的&#xff1b;在跨越广播域的时候会发生改变的&#xff1b;而IP地址在传输过程中是不会改变的&#xff08;除NAT的时候&#xff09;。 ip地址本质上是…

解析mfc100u.dll文件丢失的修复方法,快速解决mfc100u.dll问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是“缺少某个文件”的错误。最近&#xff0c;我也遇到了一个这样的问题&#xff0c;那就是“mfc100u.dll丢失”。这个问题可能会导致某些应用程序无法正常运行&#xff0c;给我们带来困扰。…

怎样解决“缺失msvcp110.dll”错误,msvcp110.dll的修复教程

在计算机使用过程中&#xff0c;我们可能会遇到一些系统错误提示&#xff0c;比如“msvcp110.dll文件丢失”。这是因为msvcp110.dll是Microsoft Visual C 2012的一个动态链接库文件&#xff0c;如果这个文件丢失或者损坏&#xff0c;就可能导致某些程序无法正常运行。那么&…

一种FSK信号符号同步的思想

FSK原理 FSK利用频率传输信息,即将信息流调制到频率上。以最简单的2FSK通俗来讲,用2个不同的频率 f 1 f_1 f1

淘宝协议最新版

我可以为您提供一些示例代码&#xff0c;以演示一些与电商平台相关的功能。请注意&#xff0c;以下代码仅为示例&#xff0c;具体实现还需要根据您的应用程序的架构、技术栈和需求进行调整和扩展。 1. 用户注册功能&#xff1a; - 后端实现&#xff1a;在后端&#xff0c;您可…

浅谈OSG的默认视点方向

目录 1. 前言 2. OPenGL坐标系和OSG坐标系 3. 默认视点有关的几个案例 4. 视点操作 4.1. 视点调整 4.2. 左右转动 4.3. 向前走 5. 总结 6. 参考资料 1. 前言 在OSG开发中&#xff0c;对视点的理解透彻是必须可少的&#xff0c;特别是在进行自定义操控器类的开发中&…

【组件】身份证卡片

效果展示 组件代码 <div class"cardOneRoot"><img :src"itemInfo.psnPicUrl ? itemInfo.psnPicUrl : " alt""/><div class"infoDiv" v-if"itemInfo.noInfo 1"><div v-for"(item2, index2) in …

“第五十七天”

不出意外的话&#xff0c;机组这周也就过完了&#xff0c;不过就真的是简单过一遍&#xff0c;先简单有个印象吧&#xff0c;这样看的话还是放假之前还是有机会把四大件都过一遍的。 这个思路&#xff0c;真的太神了&#xff0c;当然也是自己太弱了&#xff0c;就这我还为自己想…

VulnHub DC-3

一、信息收集 1.nmap扫描 只开放了80端口&#xff0c;所以只能从80入手 访问web页面 提示&#xff1a;只有一个flag&#xff0c;并且只有一个入口 wappalyzer插件 知道站点使用Joomla框架 使用该框架扫描工具 2.Joomla工具 joomscan --help joomscan -u 192.168.103.192 -e…

HNU-计算机网络-实验1-应用协议与数据包分析实验(Wireshark)

计算机网络 课程基础实验一 应用协议与数据包分析实验(Wireshark) 计科210X 甘晴void 202108010XXX 一、实验目的&#xff1a; 通过本实验&#xff0c;熟练掌握Wireshark的操作和使用&#xff0c;学习对HTTP协议进行分析。 二、实验内容 2.1 HTTP 协议简介 HTTP 是超文本…

《算法通关村——缓存机制了解LRU实现》

《算法通关村——缓存机制了解LRU实现》 介绍 LRU是"Least Recently Used"&#xff08;最近最少使用&#xff09;的缓存机制&#xff0c;它是一种常用的缓存算法&#xff0c;用于管理缓存中的数据项。LRU缓存机制的基本思想是&#xff0c;当缓存达到其容量限制时&a…

ChatGPT对未来发展的影响?一般什么时候用到GPT

ChatGPT以其强大的自然语言处理能力对未来的发展具有重要影响。以下是ChatGPT的潜在影响和一般使用情况&#xff1a; 改善自然语言理解和生成&#xff1a;ChatGPT和类似的模型可以改善机器对人类语言的理解和生成。这将有助于改进各种应用领域&#xff0c;包括智能助手、聊天机…

功放电路有这么多种,哪种音质最好呀?

功放电路种类很多&#xff0c;首先得知道各个功放电路的特点。 第一种&#xff0c;A类功放&#xff08;甲类功放&#xff09;&#xff0c;在信号的整个周期内都不会出现电流截止(即停止输出)的一类放大器。 但是A类放大器工作时会产生高热&#xff0c;效率很低&#xff0c;优…

使用postman进行接口自动化测试

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

数据结构之栈的实现

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇: Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”…

干货分享,大厂内部压测方案设计!

01、为什么要做压测 1、什么是压力测试&#xff1f; 不断向被测对象施加压力&#xff0c;测试系统在压力情况下的表现。 2、压力测试的目的是什么&#xff1f; 测试得出系统的极限性能指标&#xff0c;从而给出合理的承诺值或者容量告警&#xff1b; 找出系统的性能瓶颈&a…