红黑树的理解和简单实现

news2024/11/27 12:58:58

目录

1. 红黑树的概念和性质

2. 红黑树的插入

2.1. 情况一:新增节点的父亲为空

2.2. 情况二:新增节点的父亲非空且为黑色节点

2.3. 情况三:当父亲为红节点,叔叔存在且为红

2.3.1. 当祖父为根节点的时候

2.3.2. 当祖父不是根节点的时候

2.4. 情况四:当父亲为红节点,叔叔不存在或者存在为黑

2.5. 情况五:父亲为红、叔叔不存在或者为黑

3. 验证红黑树的插入

4. 红黑树插入的完整实现


1. 红黑树的概念和性质

红黑树是一种自平衡的二叉搜索树,它通过约束节点的颜色和结构来保持平衡。红黑树是由 Rudolf Bayer 在1972年发明的,被认为是一种优秀的平衡树结构,广泛应用于各种数据结构和算法中。

红黑树具有以下几个性质:

  1. 二叉搜索树性质:红黑树是一种二叉搜索树,即满足以下性质:

    • 左子树中的所有节点的键都小于该节点的键。
    • 右子树中的所有节点的键都大于该节点的键。
    • 左右子树都是二叉搜索树。
  2. 节点颜色性质:每个节点被标记为红色或黑色;

  3. 根节点性质:根节点为黑色

  4. 叶子节点(NIL节点)性质:叶子节点都为黑色的空节点(NIL节点),并被视为红黑树的终止节点;

  5. 红色节点性质:红节点的子节点必须是黑节点,不能出现连续的红色节点

  6. 黑节点性质:从任一节点到其每个叶子节点 (NIL 节点) 的路径上,经过的黑节点数量是相同的(黑色平衡性)

  7. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路

    径会比其他路径长出俩倍以上,因而是接近平衡的。

这些性质保证了红黑树的平衡性和有序性。红黑树通过在插入和删除操作中进行颜色调整和旋转操作来维护平衡性。与AVL树相比,红黑树对于平衡性的要求相对宽松,因此插入和删除操作的平衡调整(旋转)相对较少。

由于红黑树的平衡性,其高度相对较小,平均时间复杂度为 O(log n),提供了快速的查找、插入和删除操作。红黑树在各种应用中被广泛使用,例如C++ STL中的map和set容器,以及各种数据库、编译器和操作系统的实现

总结来说,红黑树是一种具有自平衡性质的二叉搜索树,通过约束节点的颜色和结构来保持平衡。它具有二叉搜索树的性质,并通过节点颜色、根节点性质、叶子节点性质、红色节点性质和黑节点性质来维护平衡性。红黑树提供了高效的查找、插入和删除操作,被广泛应用于不同领域的数据结构和算法中。

思考 :为什么红黑树可以保证任何一条路径不会比其他路径长出两倍以上呢?

首先我们要知道什么是路径?

路径是从根节点开始,沿着树的分支一直走到达叶子节点。注意:这里的叶子节点特指为空节点,在这里一般用NIL节点表示空节点,且NIL节点是黑色的。

对于一棵红黑树而言,它的最短路径和最长路径分别为:

  • 最短路径:就是这条路径上的节点是全黑的;
  • 最长路径:该路径是由一黑一红连续组成的。

红黑树的性质告诉我们,每条路径上的黑色节点个数是一定的,且没有连续的红色节点

那么我们假设最短路径的黑色节点个数为N。

那么最长路径的黑色节点个数也为N,其红色节点个数最多也为N,那么最长路径的节点个数为2N;

因此我们可以得出,红黑树的最长路径可以是最短路径的二倍,但是不会超过二倍, 因此红黑树的任意一条路径的节点个数不可能比其他任何一条路径长过两倍以上,以达到近似平衡的状态。

综上所述,红黑树通过对节点的着色和结构的限制,确保了树的高度相对较小,从而保证了没有一条路径会比其他路径长出两倍以上。这种限制确保红黑树保持近似平衡的状态,从而提供了较好的性能。

2. 红黑树的插入

如下图所示,这就是一颗红黑树

第一个问题,如果我们现在要插入一个新节点,我们是把这个新节点的初始颜色设置为黑色还是红色呢??? 

假设我把新增节点设置为黑色,那么就会有下面的场景:

我们发现,此时就出现了问题,插入了5之后,这棵树还是红黑树吗?

抱歉,根据红黑树的性质:每个路径上的黑色节点个数必须相等,故插入5之后 (黑色节点),此时这棵树已经不是红黑树了。

但如果我将新增节点的初始颜色设置为红色呢,又会出现什么情况?如下图所示:

 

我们发现,当我们把新增节点的初始颜色设置为红色的时候,插入之后会有两种情况:

  • 第一种情况:插入之后违反了红黑树的规则,即不能出现连续的红色节点;
  • 第二种情况:插入之后没有违反任何规则。

综上所述,我们将新增节点的初始颜色设置为红色。因为如果新增节点是黑色,那么一定会违反红黑树的规则,反之,如果是红色,则可能插入之后不违反任何规则,因此我们将新增节点设置为红色。

严格意义上讲,红黑树的插入会有五种情况,让我们一一进行理解。

2.1. 情况一:新增节点的父亲为空

这种情况最为简单,就是当是一颗空树的时候,直接插入即可。并将新增节点的颜色更新为黑即可。

2.2. 情况二:新增节点的父亲非空且为黑色节点

这种情况也很简单,直接插入红色节点即可,不会破环红黑树的规则。

2.3. 情况三:当父亲为红节点,叔叔存在且为红

当新增节点的父亲节点和叔叔节点非空且为红时,变色即可。

因为 parent 为红,那么它一定不是根节点,因此它一定有父亲节点,即 grandfather 一定不为空。

声明:

g --- grandfather(祖父节点 ), p --- parent(父节点) ,u --- uncle(叔叔节点),c --- cur(新增节点)。

变色规则:p、u变黑,g变红

如图所示:

2.3.1. 当祖父为根节点的时候

2.3.2. 当祖父不是根节点的时候

总结,当p、u为红的时候,那么将p、u变黑、将g变红,c = g; p = c->parent,继续判断,如果符合前面的条件(p、u为红)继续向上更新,最后为了避免不同的情况,将根节点的颜色更新为黑即可。

2.4. 情况四:当父亲为红节点,叔叔不存在或者存在为黑

父亲为红、叔叔不存在或者存在且为黑,且g、p、c在同一条直线上,单旋处理,处理完,插入结束。

因为 parent 为红,那么它一定不是根节点,因此它一定有父亲节点,即 grandfather 一定不为空。

单旋非为左单旋和右单旋两种情况,具体操作如图所示:

其中,a、b、c、d、e代表红黑树子树。

总结:当g、p、c在同一条直线上,就进行单旋,虽然有两种情况,但是最后它们的变色情况是一致的,p:由红变黑 g:由黑变红。

2.5. 情况五:父亲为红、叔叔不存在或者为黑

父亲为红、叔叔不存在或者存在为黑,且g、p、c不在同一条直线上,需要双旋处理,处理完,插入结束。

因为 parent 为红,那么它一定不是根节点,因此它一定有父亲节点,即 grandfather 一定不为空。

双旋非为左右双旋和右左双旋两种情况,具体操作如图所示:

其中,a、b、c、d、e代表红黑树子树。

总结:当g、p、c不在同一条直线上(一条折线就是双旋),就进行双旋,虽然有两种情况,但是最后它们的变色情况是一致的,c由红变黑  g由黑变红  p不变,保持红色

3. 验证红黑树的插入

思路:只要满足下面的所有条件就是红黑树。

  1. 第一个条件:
    1. 根节点是黑色的。
  2. 第二个条件:
    1. 红色的节点的左右子树都必须是黑色的,也就是说父子节点只能有一个红色节点;

    2. 思路: 只要某节点是红色的,那么只要判断它的父亲即可,如果父亲是红色的,那么就不是红黑树,反之,则符合红黑树。

  3. 第三个条件:
    1. 每一条路径的黑色节点的个数是相等的,步骤:

    2. 步骤一: 先求一条路径上的黑色节点个数作为基准值;

    3. 步骤二: 用该基准值分别于其他所有路径的黑色节点个数进行比较;

    4. 路径:从根节点到 "叶子节点"。注意:这里的叶子节点不是传统意义上的叶子节点,在这里把空节点当作叶子节点。

代码如下:

bool is_balance_tree()
{
    // 检查根节点
	if (_root->_col != BLACK)
	{
		std::cout << "根节点是红色,异常" << std::endl;
        return false;
	}	
	return _is_balance_tree(_root);
}

bool _is_balance_tree(Node* root)
{
	if (!root)
		return true;
	else
	{
        // basic_value作为这颗红黑树的黑色节点个数的基准值
		int basic_value = _get_black_node_num(root);
		return _is_check_rb_tree(root, 0, basic_value);
	}
}

bool _is_check_rb_tree(Node* root, int black_num, int basic_value)
{
	// basic_value: 红黑树的一条路径中的黑色节点个数,在这里作为基准值
	if (root == nullptr)
	{
		if (black_num != basic_value)
		{
			std::cout << "某条路径中的黑色节点个数不相等,异常" << std::endl;
			return false;
		}
		else
			return true;
	}
	else
	{
		if (root->_col == BLACK)
			++black_num;
		// 如果一个节点是红色的,那么该节点一定不是根,因此一定有父亲
		// 如果这个节点的父亲也是红色的,那么就说明出现异常
		if (root->_col == RED && root->_parent->_col == RED)
		{
			std::cout << "child: " << root->_kv.first << "parent: " << root->_parent->_kv.first << "出现了连续的红色节点,异常" << std::endl;
			return false;
		}
		return _is_check_rb_tree(root->_left, black_num, basic_value)
			&& _is_check_rb_tree(root->_right, black_num, basic_value);
	}
}

// 获得一条路径的黑色节点的个数
int _get_black_node_num(Node* root)
{
	if (root == nullptr)
		return 0;
	else
	{
		int ret = 0;
		while (root)
		{
			if (root->_col == BLACK)
				++ret;
			root = root->_left;
		}
		return ret;
	}
}

4. 红黑树插入的完整实现

#pragma once
#include <iostream>
#include <utility>
#include <assert.h>
#include <queue>
#include <vector>
#include <time.h>

namespace Xq
{
	enum color
	{
		RED,
		BLACK
	};

	template<class K, class V>
	struct rb_tree_node
	{
		rb_tree_node<K, V>* _left;
		rb_tree_node<K, V>* _right;
		rb_tree_node<K, V>* _parent;
		std::pair<K, V> _kv;
		color _col;

		rb_tree_node(const std::pair<K, V>& kv = std::pair<K, V>())
			:_left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _kv(kv)
			, _col(RED)
		{}
	};

	template<class K, class V>
	class rb_tree
	{
	private:
		typedef rb_tree_node<K, V> Node;
	public:
		rb_tree(Node* root = nullptr) :_root(root){}

		bool insert(const std::pair<K, V>& kv)
		{
			if (!_root)
			{
				_root = new Node(kv);
				_root->_col = BLACK;
				return true;
			}
			else
			{
				Node* cur = _root;
				Node* parent = nullptr;
				while (cur)
				{
					if (cur->_kv.first > kv.first)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (cur->_kv.first < kv.first)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return false;
					}
				}
				cur = new Node(kv);
				if (kv.first > parent->_kv.first)
				{
					parent->_right = cur;
				}
				else
				{
					parent->_left = cur;
				}
				cur->_parent = parent;

				// 当父节点parent的颜色为红色时,需要调整
				while (parent && parent->_col == RED)
				{
					// 因为父节点parent->_col == RED,那么说明parent一定有父节点
					Node* grandfather = parent->_parent;
					if (parent == grandfather->_left)
					{
						Node* uncle = grandfather->_right;
						// case 1: 当父节点为红,且叔叔不为空且为红
						// Solution : 变色
						if (uncle && uncle->_col == RED)
						{
							parent->_col = uncle->_col = BLACK;
							grandfather->_col = RED;
							if (grandfather == _root)
								grandfather->_col = BLACK;
							// 继续向上判断,如果依旧符合case 1,继续变色
							cur = grandfather;
							parent = cur->_parent;
							continue;
						}
						// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上
						// Solution: 单旋, 在这里就是对grandfather右单旋
						else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK))
						{
			                //     g             p
			                //   p   u  --->   c   g
		                    // c                     u
							right_rotate(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
							// 旋转完,更新完颜色,调整就结束
							break;
						}
						// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上
						// Solution: 双旋,在这里就是先对p进行左单旋,在对g进行右单旋
						else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK))
						{
			                //    g     先对p进行左单旋       g       在对g进行右单旋        c
			                // p     u    --->            c     u        --->          p     g    
			                //   c                      p                                       u
							left_rotate(parent);
							right_rotate(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
							// 旋转完,更新完颜色,调整就结束
							break;
						}
						else
						{
							// 非法情况
							assert(false);
						}
					}
					// 当parent是grandfather的右孩子,那么uncle就是grandfather的左孩子
					else
					{
						Node* uncle = grandfather->_left;
						// case 1: 当父节点为红,且叔叔不为空且为红
						// Solution : 变色
						if (uncle && uncle->_col == RED)
						{
							parent->_col = uncle->_col = BLACK;
							grandfather->_col = RED;
							if (grandfather == _root)
								grandfather->_col = BLACK;
							// 继续向上判断,如果依旧符合case 1,继续变色
							cur = grandfather;
							parent = cur->_parent;
							continue;
						}
						// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上
						// Solution: 单旋, 在这里就是对grandfather左单旋
						else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK))
						{
					        //   g    对g进行左单旋       p
					        // u   p      --->         g     c
					        //       c               u 
							left_rotate(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
							//旋转并将颜色更新后,就退出调整
							break;
						}
						// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上
						// Solution: 双旋,在这里就是先对p进行右单旋,在对g进行左单旋
						else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK))
						{
                            //    g       先对p进行右单旋        g          在对g进行左单旋          c
                            // u     p      --->             u     c          --->            g      p
                            //     c                                  p                    u                            

							right_rotate(parent);
							left_rotate(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
							break;
						}
						else
						{
							// 非法情况,断死
							assert(false);
						}
					}
				}
                _root->_col = BLACK;
				return true;
			}
		}

		void level_order()
		{
			_level_order(_root);
		}

		bool is_balance_tree()
		{
			if (_root->_col != BLACK)
            {
                std::cout << "根节点是红色的,异常" <<std::endl;
                return false;
            }
				
			return _is_balance_tree(_root);
		}

		int get_rb_tree_high()
		{
			return _get_rb_tree_high(_root);
		}

	private:

		void _level_order(Node* root)
		{
			if (!root)
				return;
			else
			{
				std::queue<Node*> qu;
				qu.push(root);
				while (!qu.empty())
				{
					Node* front = qu.front();
					qu.pop();
					if (front)
					{
						qu.push(front->_left);
						qu.push(front->_right);
					}
					if (!front)
						std::cout << "N ";
					else
						std::cout << front->_kv.first << " ";
				}
				std::cout << std::endl;
			}
		}

		void left_rotate(Node* parent)
		{
			Node* cur = parent;
			Node* cur_right = cur->_right;
			Node* cur_right_left = cur_right->_left;
			Node* cur_parent = cur->_parent;

			cur->_right = cur_right_left;
			if (cur_right_left)
				cur_right_left->_parent = cur;

			cur_right->_left = cur;
			cur->_parent = cur_right;

			if (!cur_parent)
			{
				cur_right->_parent = nullptr;
				_root = cur_right;
			}
			else
			{
				if (cur_parent->_left == cur)
				{
					cur_parent->_left = cur_right;
				}
				else
				{
					cur_parent->_right = cur_right;
				}
				cur_right->_parent = cur_parent;
			}
		}

		void right_rotate(Node* parent)
		{
			Node* cur = parent;
			Node* cur_left = cur->_left;
			Node* cur_left_right = cur_left->_right;
			Node* cur_parent = cur->_parent;

			cur->_left = cur_left_right;
			if (cur_left_right)
				cur_left_right->_parent = cur;

			cur_left->_right = cur;
			cur->_parent = cur_left;

			if (!cur_parent)
			{
				cur_left->_parent = nullptr;
				_root = cur_left;
			}
			else
			{
				if (cur_parent->_kv.first > cur_left->_kv.first)
				{
					cur_parent->_left = cur_left;
				}
				else
				{
					cur_parent->_right = cur_left;
				}
				cur_left->_parent = cur_parent;
			}
		}

		bool _is_balance_tree(Node* root)
		{
			if (!root)
				return true;
			else
			{
				int basic_value = _get_black_node_num(root);
				return _is_check_rb_tree(root, 0, basic_value);
			}
		}

		bool _is_check_rb_tree(Node* root, int black_num, int basic_value)
		{
			// basic_value: 红黑树的一条路径中的黑色节点个数,在这里作为基本值
			if (root == nullptr)
			{
				if (black_num != basic_value)
				{
					std::cout << "路径中的黑色节点个数不相等,异常" << std::endl;
					return false;
				}
				else
					return true;
			}
			else
			{
				if (root->_col == BLACK)
					++black_num;
				// 如果一个节点是红色的,那么该节点一定不是根,因此一定有父亲
				// 如果这个节点的父亲也是红色的,那么就说明出现异常
				if (root->_col == RED && root->_parent->_col == RED)
				{
					std::cout << "child: " << root->_kv.first << "parent: " << root->_parent->_kv.first << "出现了连续的红色节点,异常" << std::endl;
					return false;
				}
				return _is_check_rb_tree(root->_left, black_num, basic_value)
					&& _is_check_rb_tree(root->_right, black_num, basic_value);
			}
		}

		int _get_black_node_num(Node* root)
		{
			if (root == nullptr)
				return 0;
			else
			{
				int ret = 0;
				while (root)
				{
					if (root->_col == BLACK)
						++ret;
					root = root->_left;
				}
				return ret;
			}
		}

		int _get_rb_tree_high(Node* root)
		{
			if (root == nullptr)
				return 0;
			else
			{
				int left_high = _get_rb_tree_high(root->_left);
				int right_high = _get_rb_tree_high(root->_right);
				return left_high > right_high ? ++left_high : ++right_high;
			}
		}

	private:
		Node* _root;
	};
}

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

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

相关文章

揭秘高效引流获客的艺术:转化技巧大公开

在数字化营销的海洋中&#xff0c;每个企业都如同一艘努力航行的船&#xff0c;而流量便是推动船只前行的风帆。如何有效吸引并获取潜在客户&#xff0c;即所谓的“引流获客”&#xff0c;已成为企业市场营销策略中不可或缺的一环。本文将详细探讨几种实用且高效的引流获客技巧…

【RAG 论文】AAR:训练一个LLM喜欢的检索器来做RAG

论文&#xff1a;Augmentation-Adapted Retriever Improves Generalization of Language Models as Generic Plug-In ⭐⭐⭐ ACL 2023, Tsinghua & Microsoft&#xff0c;arXiv:2305.17331 论文速读 以往 RAG 的工作通常联合微调 retriever 和 LLM 导致紧密耦合&#xff0…

实验0.0 Visual Studio 2022安装指南

Visual Studio 2022 是一个功能强大的开发工具&#xff0c;对于计算机专业的学生来说&#xff0c;它不仅可以帮助你完成学业项目&#xff0c;还能为你将来的职业生涯打下坚实的基础。通过学习和使用 Visual Studio&#xff0c;你将能够更高效地开发软件&#xff0c;并在编程领域…

公有云Linux模拟UDP端口并抓包

目录 写在前面操作步骤服务端开启UDP端口并监听客户端连接Wireshark抓包查看 写在前面 关于具体的操作&#xff0c;请参考我的上一篇文章 公有云Linux模拟TCP三次挥手与四次握手&#xff08;Wireshark抓包验证版&#xff09; 在本文&#xff0c;仅介绍与上一篇不同的地方。 操…

Matlab-粒子群优化算法实现

文章目录 一、粒子群优化算法二、相关概念和流程图三、例题实现结果 一、粒子群优化算法 粒子群优化算法起源于鸟类觅食的经验&#xff0c;也就是一群鸟在一个大空间内随机寻找食物&#xff0c;目标是找到食物最多的地方。以下是几个条件: (1) 所有的鸟都会共享自己的位置以及…

泰达克仿钻点水晶饰品包装印刷防滑UV胶特性及应用场景

仿钻点UV滴胶是一种特殊的胶水 常用于模拟钻石的效果 它是一种透明的胶水 具有高光泽度和折射率 可以在物体表面形成类似钻石的亮闪效果 仿钻点UV滴胶通常由紫外线固化胶组成 需要通过紫外线照射来固化和硬化 它具有以下特点&#xff1a; 1. 透明度&#xff1a;仿钻点UV滴胶具有…

B端弹窗设计指南,3000字讲清楚,内附大量案例。

B端系统弹窗是指在企业级&#xff08;Business to Business&#xff09;系统中&#xff0c;弹出的窗口或对话框&#xff0c;用于向用户展示信息、提供操作选项或者收集用户输入。 一、B端系统弹窗的作用 作用如下&#xff1a; 提示和通知&#xff1a;弹窗可以用于向用户展示重…

STM32F407-驱动SHT41采集温湿度

STM32F407-驱动SHT41采集温湿度 SHT41 SHT41通过I2C方式进行驱动 从机地址&#xff1a; 0x44 获取数据方式 1&#xff09;先发送I2C写&#xff0c;写入特定指令 2&#xff09;延时一段时间&#xff0c;等待SHT41处理 3&#xff09;再进行I2C读&#xff0c;读数据即可 一些…

打开远程连接的命令是什么?

远程连接是一种能够在不同设备之间建立连接并共享信息的技术。在许多情况下&#xff0c;我们需要通过远程连接来访问其他设备或处理一些远程任务。本文将介绍一些常用的打开远程连接的命令。 使用SSH连接远程设备 SSH&#xff08;Secure Shell&#xff09;是一种安全的网络协议…

1-02-02:虚拟化与容器化Docker环境搭建

1.02.02 虚拟化与容器化Docker环境搭建 一. 虚拟化与容器化技术简介1. 虚拟机环境2. docker环境 二. Docker 架构与隔离机制2.1 Docker 架构2.2 Docker 隔离机制2.3 资源限制2.4 Docker应用场景 三. 实战:Docker在Centos7安装与镜像加速 ❤❤❤3.1 docker安装3.2 设置镜像加速 …

NIOS II实现LED流水灯以及串口输出(DE2-115开发板)

NIOS II实现LED流水灯以及串口输出&#xff08;DE2-115开发板&#xff09; 前言什么是Qsys?什么是NIOSII?注意事项1、管脚配置2、配置NIOSII时的连接3、注意中断配置好后是这样的4、注意名称的配置5、设置双功能引脚 NIOS II的报错代码以及效果演示流水灯输出到电脑串口助手 …

人工智能轨道交通行业周刊-第79期(2024.4.22-5.12)

本期关键词&#xff1a;无人机巡检、车机联控、减速顶、Agent、GraphRAG、RAGFlow 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道世界铁路那…

【智能算法应用】遗传粒子群算法(GA-PSO)求解选址问题

目录 1.算法原理2.数学模型3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】粒子群算法&#xff08;PSO&#xff09;原理及实现 经典PSO算法用于连续空间优化问题&#xff0c;选址问题作为组合优化问题&#xff0c;需要在离散空间中求解。因此&#xff0c;考虑遗传算…

阮怀俊谈如何盘活和挖掘乡村文旅资源

近年来&#xff0c;浙江凭借高水平建设新时代美丽乡村&#xff0c;各项工作持续走在全国前列&#xff0c;最近&#xff0c;在国家发展改革委关于恢复和扩大消费措施的通知中也提到&#xff1a; “推广浙江‘千万工程’经验&#xff0c;建设宜居宜业和美乡村。实施文化产业赋能乡…

Libcity 笔记:自定义模型

在/libcity/model/trajectory_loc_prediction/&#xff0c;我们复制一份Deepmove.py&#xff0c;得到DM_tst.py&#xff0c;我们不改变其中的机制&#xff0c;只动class name 然后修改相同目录下的__init__.py&#xff1a; 修改task_config文件&#xff1a; 在config/model/tra…

带头单链表 C++实现

节点定义 带头单链表&#xff1a;我们只需要一个结点指针指向整个链表的第一个节点&#xff0c;这样我们就可以通过next指针访问整个链表内的所有节点 template<class T> struct ListNode {T _val;ListNode* _next;ListNode(const T &val):_val(val),_next(nullptr){…

Unity图形图表XChart插件使用

最近做了一款数字孪生项目,其中涉及到了图形图表的应用,网上找了一下,找到了XChart插件,使用起来蛮方便的,不过还有待继续研究,很多细节性的知识点需要进行学习探索。以下是项目中的应用。 官方应用: ![](https://img-blog.csdnimg.cn/direct/ab9de8e84e7b4be4a50ea…

抽丝剥茧:详述一次DevServer Proxy配置无效问题的细致排查过程

事情的起因是这样的&#xff0c;在一个已上线的项目中&#xff0c;其中一个包含登录和获取菜单的接口因响应时间较长&#xff0c;后端让我尝试未经服务转发的另一域名下的新接口&#xff0c;旧接口允许跨域请求&#xff0c;但新接口不允许本地访问&#xff08;只允许发布测试/生…

【058】基于SpringBoot+Vue校园失物招领系统的设计与实现

系统介绍 基于SpringBootVue校园失物招领系统主要通过使用Java语言编码设计系统功能&#xff0c;MySQL数据库管理数据&#xff0c;AJAX技术设计简洁的、友好的网址页面&#xff0c;然后在IDEA开发平台中&#xff0c;编写相关的Java代码文件&#xff0c;接着通过连接语言完成与…

C#实现简单音乐文件解析播放——Windows程序设计作业2

1. 作业内容 编写一个C#程序&#xff0c;要求实现常见音乐文件的播放功能&#xff0c;具体要求如下&#xff1a;     1). 播放MP3文件&#xff1a; 程序应能够读取MP3文件&#xff0c;并播放其中的音频。     2). 播放OGG文件&#xff1a; 应能够播放ogg文件。     …