【高阶数据结构】红黑树的插入(超多精美图解+完整代码)

🤡博客主页:醉竺

🥰本文专栏:《高阶数据结构》

😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!


✨✨💜💛想要学习更多《高阶数据结构》点击专栏链接查看💛💜✨✨ 


目录

1. 什么是红黑树? 

1.1 红黑树存在的意义 

1.2 红黑树的定义和性质

1.3 判断是否为红黑树(练习) 

2. 红黑树的插入

2.1 插入案例详细图解

2.2 案例练习 

3 红黑树完整代码实现

3.1 红黑树结点的定义

3.2 插入代码

3.3 红黑树的验证

3.4 红黑树的查找


1. 什么是红黑树? 

在 本文之前,我写了有关“平衡二叉树”的性质、插入和删除的文章,引入了“平衡性”的概念:

《二叉搜索树的插入、删除和查找(精美图解+完整代码)》icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43382136/article/details/140280070?spm=1001.2014.3001.5501《平衡二叉树(AVL)的插入(4种旋转方法+精美图解+完整代码)》icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43382136/article/details/140403187?spm=1001.2014.3001.5501《平衡二叉树(AVL)的删除和调整》icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_43382136/article/details/140577239主要目的是让这棵二叉搜索树左右看起来比较“平衡”,不出现左子树很高、右子树很矮,或者左子树很矮、右子树很高的情形。这样,在进行节点的查找、插入、删除等操作时效率会比较高。

但这同时也带来了缺点——在插入和删除节点时,为了调整平衡,必须对树中的节点进行旋转,从而在一定程度上影响程序的执行效率。

平衡二叉树的一条重要性质是“任一节点的左子树和右子树的高度之差不超过1”。这里引入一个“ 非严格平衡二叉树”的概念。

非严格平衡二叉树指的是 不完全符合 前面平衡二叉树的定义,或者说并不是一种严格意义上的平衡二叉树。但这种二叉树最小高度仍旧在log_{2}^{n}(n代表节点数)附近,或者说,查找操作的时间复杂度仍旧为O(log_{2}^{n})。所以,我们仍旧可以认为这种二叉树是一种平衡二叉树。这就引出了这节课要讲解的“红黑树”。

1.1 红黑树存在的意义 

如果你正在思考为什么已经有AVL树了还需要引入红黑树,那么简单来说可以这样理解。

  • 如果AVL树 很大,那么在 插入和删除 操作时会进行大量的旋转操作以达到AVL树的平衡,尤其是删除节点时,可能要经过若干次的旋转操作,甚至可能需要从最底部的节点一直到根节点都进行平衡性调整。

  • 但在红黑树中,当进行插入和删除操作时,维护红黑树的平衡性成本就比较低——多数情况下只需要旋转两次或者不需要旋转只需要对节点的颜色进行修改,也就是把红色修改为黑色。

所以,虽然AVL树比红黑树更加平衡,但针对 插入和删除 操作,红黑树可以保证平均情况时间复杂度更接近O(log_{2}^{n})。换句话说,如果从单纯 搜索 角度来讲, AVL树更快,但如果 频繁 进行 插入和删除 操作,因为 红黑树 需要更少的旋转,无疑 效率会更高,而且红黑树的查找、插入、删除操作性能较稳定。

下面一张图总结为什么需要存在红黑树:

1.2 红黑树的定义和性质

红黑树(Red-Black Tree),简称R-B树,是一种高效的二叉查找树,由Rudolf Bayer在1972年发明的,当时被称为“对称/平衡二叉B树”,后来在1978年由Leo J. Guibas和Robert Sedgewick 修改为“红黑树”。  

红黑树首先是一棵 二叉搜索树,也是一种典型的非严格平衡二叉树,或者你也可以理解成一种 特殊/特化 的AVL树,甚至在很多资料中, 提到平衡二叉树指的就是红黑树。红黑树在 插入和删除 节点时,会通过特定的操作保持二叉查找树的相对平衡,从而获得 比较高的查找效率。既然是相对平衡,所以任一节点的左子树和右子树的高度之差很可能会超过1。  

下面先看一张红黑树的图片,我们比对着图片来讲解红黑树的性质:

性质1每个节点或是红色,或是黑色

性质2根节点必须是黑色的。

性质3叶子节点(外部节点、Nil节点、失败节点)均是黑的。

性质4不存在两个连续的红节点。同时,下面几种说法都是一样的理解一种即可。

  1. 红色节点是被黑色节点分隔开的。

  2. 红色节点的两个孩子节点或者父亲节点必须是黑色的。

  3. 从叶节点到根的所有路径上不可以有两个连续的红色节点。

性质5: 对于每个节点,从该节点到任一叶节点的简单路径上,所含黑节点的数目相同。

这个黑色节点数量叫做“ 黑高度”或者“ 黑高”(bh),用于保证黑色节点的平衡性。  

与“黑高”相关的推论:

根据红黑树需要同时满足 性质4 和 性质5还能得到红黑树有两个特点(为了减少记忆负担了解即可,重要的还是上述性质): 

性质1证明: 

  • 从根到叶的最短路径:这一路径下全是黑色节点没有红色节点。

  • 从根到叶的最长路径:这个路径将包含尽可能多的红色节点。由于红黑树的性质,红色节点后面必然跟随一个黑色节点,因此每增加一个红色节点,就会增加一个黑色节点。当黑色节点数和最短路径上的黑色节点数量相同时,这个最长路径的节点数量(红+黑)就是最短路径黑色节点数量的两倍了。 

性质2证明:

设根节点的黑高为 h, 则 内部节点数最少有2^{h}-1个;
若红黑树的高度为 h, 则 根节点的黑高 ≥ h/2;(最短路径),因此内部节点的个数 n最少有2^{\frac{2}{h}}-1,即n\geqslant 2^{\frac{2}{h}}-1,由此推出h\leqslant 2log_{2}^{(n+1)} 

1.3 判断是否为红黑树(练习) 

为了方便记忆红黑树以上的定义和性质,可以背诵下面的顺口溜:

接下来我们练习一下,来判断下面的树是不是红黑树?

例1: 

例2: 

例3: 

例4: 


2. 红黑树的插入

我们先来想想,红黑树插入节点操作一般分为几种情况呢?

  • 首先,对于没有任何节点的空树,直接创建一个 黑色 节点作为根节点即可。
  • 其次,对于非空的树,查找要插入的位置并插入一个 红色 节点,然后要判断该节点的父节点。

为什么插入的新节点是红色的而不是黑色的呢? 

若我们插入的是黑色结点,那么插入路径上黑色结点的数目就比其他路径上黑色结点的数目多了一个,即破坏了红黑树的性质5(“黑路同”),就会影响红黑树的多条路经。而之所以插入的是红色,是因为红色节点不会增加黑高度,从而尽量减少插入节点后导致的平衡性调整。

  • 插入红色节点之后,如果其父节点为黑色,此时不需要做任何调整。
  • 若其父节点为红色,最多会违背性质红黑树的性质4(“不红红”),此时必须进行平衡性调整。

红黑树的插入看似复杂,其实不难,相比于平衡二叉树而言,红黑树的插入无需更新平衡因子,只是在旋转调整的基础上,再对相关节点进行变色即可!
下面是对红黑树的插入进行的总结,只需要理解下面的内容就能完全理解红黑树的插入,请认真阅读下图。

注意:当叔叔不存在(为叶子节点,Nil,NULL)也属于 “黑叔”。 

LL型: 新插入节点是 爷爷左孩子的左孩子 (右旋)

RR型:新插入节点是 爷爷右孩子的右孩子 (左旋)

LR型:新插入节点是 爷爷左孩子的右孩子 (先左旋,后右旋)

RL型:新插入节点是 爷爷右孩子的左孩子 (先右旋,后左旋)

对上图内容进行分析:
我们知道,插入一个新的红节点时,若父亲也是红色,则最多违背了性质4(“不红红”),此时必须进行调整。从上图中我们了解到红黑树插入后进行调整时:需要看新节点叔叔的颜色

因此,当我们插入一个新的红节点并且违背了红黑树的性质需要进行调整时,此时必有下面情况:

  1. 父亲一定是红色(否则插入后怎么会违背性质呢?);同理,此时爷爷一定是黑色(否则在没插入时就已经父爷俩节点就违背了“不红红”)。
  2. 若新插入节点的叔叔为红色,则有 叔父为红,爷为黑。
  3. 若新插入节点的叔叔为黑色(叔叔不存在也为黑色Nil),则有 父为红,爷叔为黑。

上述 3 条无需记忆,只是进行分析理解即可,为后续代码实现以及调整过程中的“染色”进行铺垫。 

上图调整过程中,所谓的“变色”,就是把相关节点的颜色进行 “ 变黑,黑变 ”。

根据上面3条,以及“染色”,我们可以推出:

调整染色过程中,爷爷要变为红,父亲要变为黑,叔叔不变(原来啥颜色就啥颜色)。
有一点需要注意:若爷爷变为红后,但其是整个红黑树的根,还要将爷爷恢复成黑色(性质1)。


2.1 插入案例详细图解

1. 红叔:变色+变新(无需旋转)
调整方法:无论什么型,父叔变色(父、叔变黑,爷变红),再将爷爷看作为新插入的节点(继续向上调整)

  1. 若爷爷是整个二叉树的根节点,那么为了满足红黑树性质1 “根是黑色”,要把爷爷节点变回黑色。此时整个调整才完毕。
  2. 如果爷爷节点不是整个二叉树的根节点,则还需要继续沿着爷爷节点向上调整,调整中如果遇到一个黑色的前辈节点,则整个平衡性调整完毕。 


 2.黑叔(或者叔叔为空):旋转+变色

调整方法:

LL型:右单旋,父换爷并变色

RR型:左单旋,父换爷并变色

LR型:左、右双旋,儿换爷并变色

RL型:右、左双旋,儿换爷并变色

LL型:新插入节点是爷爷左孩子的左孩子,叔叔节点不存在或者存在但为黑色。 以爷爷节点为根向右旋转。接着将原父亲节点变为黑色,原爷爷节点变为红色。


LR型:新插入节点是爷爷右孩子的右孩子,叔叔节点不存在或者存在但为黑色。 首先以父亲节点为根向左旋转,然后再以爷爷节点为根向右旋转。接着将原来的新节点变为黑色,原来的爷爷节点变为红色。


 RR型:新插入节点是爷爷右孩子的右孩子,叔叔节点不存在或者存在但为黑色。 以爷爷节点为根向左旋转。接着将原父亲节点变为黑色,原爷爷节点变为红色。


RL型:新插入节点是爷爷右孩子的左孩子,叔叔节点不存在或者存在但为黑色。首先以父亲节点为根向右旋转,然后再以爷爷节点为根向左旋转。接着将原来的新节点变为黑色,原来的爷爷节点变为红色。 

2.2 案例练习 

​上述2.1节已经涵盖红黑树所有插入和修改的情况,接下来请根据上图的插入调整步骤,看一下下面的实战练习,图比较多! 


3 红黑树完整代码实现

3.1 红黑树结点的定义

我们这里直接实现 KV模型 的红黑树,为了方便后序的旋转操作,将红黑树的结点定义为三叉链结构,除此之外还新加入了一个成员变量,用于表示结点的颜色。 

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	Colour _col;

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

 红黑树类的定义框架

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;

private:
	Node* _root = nullptr;
};

下面红黑树相关成员函数的实现,都是在RBTree这个类中实现的。 

3.2 插入代码

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}

	// 1.找到插入位置
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}

	// 2.插入新节点,新增节点给红色
	cur = new Node(kv);
	cur->_col = RED;
	if (kv.first < parent->_kv.first)
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_right = cur;
		cur->_parent = parent;
	}

	// 3.若插入节点的父亲是黑色的,则不需要调整,
	// 若父亲是红色的,则需要修正红黑树的性质进行调整
	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left) // LL型插入+LR型插入
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED) // 红叔 : 父叔爷变色+爷变新节点(继续往上处理)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上更新处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 黑叔(Nil)
			{
				if (cur == parent->_left) // 黑叔(Nil)+LL型插入 : 右旋+父爷变色
				{
					// R单旋
					//     g
					//   p   bu(Nil)
					// c
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else //(cur == parent->_right) 黑叔(Nil)+LR型插入 : 左旋+右旋+儿爷变色
				{
					// LR双旋
					//     g
					//   p   bu(Nil)
					//     c
					RotateLR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
		else  // RR型插入+RL型插入(parent == grandfather->_right)
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED) // 红叔 : 父叔爷变色+爷变新节点(继续往上处理)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 黑叔(Nil)
			{
				if (cur == parent->_right) // 黑叔(Nil)+RR型插入 : 左旋+父爷变色
				{
					// L单旋
					//     g
					//  bu   p
					//         c
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else //(cur == parent->_left) // 黑叔(Nil)+RL型插入 : 右旋+左旋+儿爷变色
				{
					//     g
					//  bu   p 
					//     c

					RotateRL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
	}

	_root->_col = BLACK; // 根节点必须是黑色(无论中间过程如何调整过)

	return true;
}

对应的4种旋转代码(看不懂的话,请移步我的文另一篇文章每种旋转方式讲解的特别清晰!) 

// LL型插入,右旋
void RotateR(Node* gf)
{
	Node* p = gf->_left;
	Node* pr = p->_right;
	Node* ggf = gf->_parent;

	gf->_left = pr;
	if (pr != nullptr)
		pr->_parent = gf;

	p->_right = gf;
	gf->_parent = p;

	if (_root == gf)
	{
		_root = p;
		p->_parent = nullptr;
	}
	else
	{
		if (ggf->_left == gf)
		{
			ggf->_left = p;
		}
		else
		{
			ggf->_right = p;
		}

		p->_parent = ggf;
	}
}

// RR型插入,左旋
void RotateL(Node* gf)
{
	Node* p = gf->_right;
	Node* pl = p->_left;
	Node* ggf = gf->_parent;

	gf->_right = pl;
	if (pl != nullptr)
		pl->_parent = gf;

	p->_left = gf;
	gf->_parent = p;

	if (_root == gf)
	{
		_root = p;
		p->_parent = nullptr;
	}
	else
	{
		if (ggf->_left == gf)
		{
			ggf->_left = p;
		}
		else
		{
			ggf->_right = p;
		}

		p->_parent = ggf;
	}
}

// LR型插入,左旋+右旋
void RotateLR(Node* gf)
{
	RotateL(gf->_left);
	RotateR(gf);
}

// RL型插入,左旋+右旋
void RotateRL(Node* gf)
{
	RotateR(gf->_right);
	RotateL(gf);
}

3.3 红黑树的验证

bool ISRBTree()
{
	if (_root == nullptr)
	{
		return true;  // 空树是平衡的
	}

	if (_root->_col == RED)
	{
		return false;  // 根节点必须是黑色
	}

	// 计算参考的黑色节点数量(这个值是从根节点到最左侧叶子节点路径上的黑色节点数。该值将作为比较的标准)
	int refVal = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			++refVal;
		}
		cur = cur->_left;
	}

	int blacknum = 0;
	return _ISRBTree(_root, blacknum, refVal);
}

// 根节点->当前节点这条路径的黑色节点的数量
bool _ISRBTree(Node* root, int blacknum, const int refVal)
{
	if (root == nullptr)
	{
		// 到达叶子节点
		if (blacknum != refVal)
		{
			// 黑色节点数量不相等
			cout << "存在黑色节点数量不相等的路径" << endl;
			return false;
		}
		return true;
	}

	if (root->_col == RED && root->_parent->_col == RED)
	{
		// 存在连续的红色节点
		cout << "有连续的红色节点" << endl;
		return false;
	}

	if (root->_col == BLACK)
	{
		// 当前节点是黑色,增加黑色节点计数
		++blacknum;
	}

	// 递归检查左子树和右子树
	return _ISRBTree(root->_left, blacknum, refVal) && _ISRBTree(root->_right, blacknum, refVal);
}

3.4 红黑树的查找

//查找函数
Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (key < cur->_kv.first) //key值小于该结点的值
		{
			cur = cur->_left; //在该结点的左子树当中查找
		}
		else if (key > cur->_kv.first) //key值大于该结点的值
		{
			cur = cur->_right; //在该结点的右子树当中查找
		}
		else //找到了目标结点
		{
			return cur; //返回该结点
		}
	}
	return nullptr; //查找失败
}

创作不易,对你有用的话麻烦点个收藏或者评论吧~❤ 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机应用开发(进阶)---外部中断(按键+数码管显示0-F)

实现目标 1、巩固数码管、外部中断知识 2、具体实现&#xff1a;按键K4&#xff08;INT1&#xff09;每按一次&#xff0c;数码管从0依次递增显示至F&#xff0c;再按则循环显示。 一、共阳数码管 1.1 共阳数码管结构 1.2 共阳数码管码表 共阳不带小数点0-F段码为&#xff…

MacOS上Homebrew 安装、配置、更改国内镜像源及使用教程

Homebrew笔记 1. 介绍 官网&#xff1a;https://brew.sh/ 对于习惯了使用命令来完成一切的程序员来说&#xff0c;安装软件这种小事&#xff0c;自然是能够用命令解决&#xff0c;就不用图形界面选择。但是在 Linux 中&#xff0c;我们有 yum、apt、dnf、pkg等命令来完成软件的…

LeetCode 热题 100之链表1

1.相交链表 思路分析&#xff08;直接上双指针&#xff09;&#xff1a; 初始化两个指针&#xff0c;分别指向两个链表的头节点 headA 和 headB遍历两个链表&#xff0c;当指针到达链表的末尾时&#xff0c;将指针移动到另一个链表的头部 如果链表相交&#xff0c;两个指针会在…

【含开题报告+文档+PPT+源码】基于SSM的旅游与自然保护平台开发与实现

开题报告 围场县拥有丰富的自然景观和野生动植物资源&#xff0c;同时面临着旅游业发展和自然保护之间的平衡问题&#xff0c;通过强调自然保护&#xff0c;这个平台可以教育游客如何尊重和保护当地的生态环境。同时&#xff0c;平台还可以提供关于生态保护的信息&#xff0c;…

立仪光谱共焦在玻璃上奥秘与应用

在现代工业和科学研究中&#xff0c;玻璃因其透明、坚硬和易加工的特性被广泛应用于各个领域。然而&#xff0c;玻璃的厚度测量一直是困扰业界的一大难题。传统的千分尺或电容式传感器虽然在一定程度上能满足生产需求&#xff0c;但在精度、效率以及适用范围上存在明显的局限。…

中航资本:市盈率静和动分别是什么意思?市盈率静和动看哪个准?

市盈率静和动别离是什么意思&#xff1f; 市盈率静就是指静态市盈率&#xff0c;是以最新一期的年报为核算根据&#xff0c;其数据核算公式为&#xff1a;总市值最新一期的年报的净利润&#xff0c;年报的净利润可所以作用快报或作用预告发布的数据。 市盈率动就是动态市盈率…

动态规划 - 背包问题 - 完全背包

完全背包物品数量无限制&#xff0c;可以使用多次的实现方式&#xff1a;背包正序遍历 0-1背包&#xff1a;先物品后背包&#xff0c;物品正序、背包倒序&#xff08;保证每个物品只用一次&#xff09; 完全背包&#xff1a;先后顺序都可以&#xff0c;物品正序、背包正序 如果…

基于卷积神经网络的苹果病害识别与防治系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 苹果病害识别与防治系统&#xff0c;卷积神经网络&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积…

appium+mumu模拟器 嚼碎菜鸟教程

1、android sdk 下载安装 下载地址&#xff1a;https://www.androiddevtools.cn/index.html# 选择版本&#xff1a;android sdk【sdk tools:installer_r24.4.1-windows.exe】 参考步骤&#xff1a;https://blog.csdn.net/2401_83004375/article/details/139300339 2、jdk 安装…

day11:磁盘管理

一&#xff0c;磁盘概述 磁盘概述 磁盘是一种持久性存储设备&#xff0c;用于存储操作系统、应用程序和数据。磁盘通常分为**机械硬盘&#xff08;HDD&#xff09;和固态硬盘&#xff08;SSD&#xff09;**两种&#xff0c;HDD 基于旋转的磁性盘片&#xff0c;而 SSD 基于闪存…

【WRF数据处理】基于GIS4WRF插件将geotiff数据转为tiff(geogrid,WPS所需数据)

【WRF数据处理】基于GIS4WRF插件将geotiff数据转为tiff&#xff08;geogrid&#xff0c;WPS所需数据&#xff09; 数据准备&#xff1a;以叶面积指数LAI为例QGis实操&#xff1a;基于GIS4WRF插件将geotiff数据转为tiff警告&#xff1a;GIS4WRF: Input layer had an unexpected …

ES8JC-ASEMI超快恢复二极管ES8JC

编辑&#xff1a;ll ES8JC-ASEMI超快恢复二极管ES8JC 型号&#xff1a;ES8JC 品牌&#xff1a;ASEMI 封装&#xff1a;SMC 安装方式&#xff1a;贴片 批号&#xff1a;最新 恢复时间&#xff1a;35ns 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;8A 最大循…

ECCV 2024论文分享┆Agent Attention: Softmax注意力和线性注意力的高效融合

简介 本推文主要介绍了由清华大学黄高老师团队发表在ECCV 2024上的一篇论文《Agent Attention: On the Integration of Softmax and Linear Attention》&#xff0c;文中提出了一种新型的代理注意力&#xff08;Agent Attention&#xff09;。近年来&#xff0c;Transformer在…

Github 2024-10-29Python开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-29统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10TypeScript项目1gpt4free存储库:强大语言模型的集合 创建周期:300 天开发语言:Python协议类型:GNU General Public License v3…

【Java】逻辑控制 —— 三大结构 和 猜数字游戏

目录 1. 顺序结构 2. 分支结构【与C略有不同】 2.1 if语句 2.2 switch语句 注意事项【与C不同】 3. 循环结构【与C略有不同】 3.1 while循环 * break和continue 3.2 for循环 3.3 do while循环 * 输入的判断&#xff08;hasNext&#xff09; 4. 猜数字游戏 1. 顺序结…

大文件秒传,分片上传,断点续传

大文件分片上传 一 功能描述 1.文件通过web端分片多线程上传到服务端&#xff0c;然后web端发起分片合并&#xff0c;完成大文件分片上传功能 2.上传过的大文件&#xff0c;实现秒传 3.上传过程中&#xff0c;服务异常退出&#xff0c;实现断点续传 二 流程图 三 代码运行…

【含开题报告+文档+PPT+源码】基于Java的社会公益平台

开题报告 随着社会的不断进步和人们公益意识的日益增强&#xff0c;社会公益事业在全球范围内得到了广泛的关注和参与。然而&#xff0c;传统的公益模式往往受到信息不对称、资源分散、管理效率低下等问题的困扰&#xff0c;导致公益活动的效果有限&#xff0c;难以满足社会的…

【C语言】C语言入门--函数

文章目录 前言一、函数的概念一、pandas是什么&#xff1f;二、库函数 1.标准库和头文件2.库函数的使用方法3.库函数文档的一般格式三、自定义函数四、形参和实参五、return语句六、数组做函数参数七、嵌套调用和链式访问 1.嵌套调用2.链式访问八、函数的声明和定义 1.单个文件…

C++在实际项目中的应用第二节:C++与区块链

第五章&#xff1a;C在实际项目中的应用 第二课&#xff1a;C与区块链 区块链技术因其去中心化、不可篡改和透明性而受到广泛关注。在这门课程中&#xff0c;我们将深入探讨区块链的基本原理、智能合约的开发以及实际应用的案例分析&#xff0c;重点使用 C 作为实现语言&…

微服务之网关、网关路由、网关登录校验

简介&#xff1a;来源&#xff1a;SpringCloud微服务开发与实战&#xff0c;java黑马商城项目微服务实战开发&#xff08;涵盖MybatisPlus、Docker、MQ、ES、Redis高级等&#xff09; 认识网关 前端请求不能直接访问微服务&#xff0c;而是要请求网关&#xff1a; 网关可以做…