【C++版】排序算法详解

目录

直接插入排序

希尔排序

选择排序

冒泡排序

堆排序 

快速排序

hoare法

挖坑法

 前后指针法

 非递归版本

 快速排序中的优化

归并排序

递归版本

非递归版本

计数排序

 总结


直接插入排序

直接插入排序的思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

其实我们在打牌的时候就是运用了这种插入的思想


 

动图演示

步骤

  1.  从第一个元素开始,可以认为这个元素是有序的序列
  2. 遍历下一个元素,往这个有序的序列进行插入
  3. 从后往前扫描这个有序序列,如果待插入的元素小于这个有序序列中的元素,就将这个有序序列的元素移到下一位,直到遇到比这个待插入元素小的数据就停下来,将这个元素插入到这个数据的下一个位置。

代码实现: 

//直接插入排序—时间复杂度O(N^2)
void InsertSort(vector<int> &v, int n)
{
	//end只用到n-2位置即可
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = v[end + 1];
		while (end >= 0)
		{
			if (tmp < v[end])
			{
				v[end + 1] = v[end];
				end--;
			}
			else
			{
				break;
			}
		}
		v[end + 1] = tmp;
	}
}

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数(gap),把待排序文件中所有记录分成多个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,取重复上述分组和排序的工作。当gap == 1时,所有记录在同一组内的数已经排好序,最后再来个直接插入排序,那么排序就完成了。

说白了希尔排序其实就是在直接插入排序的基础之上先给这组数分组做预排序,让这组数据接近有序,最好再用直接插入排序的思想,将排序完成。因为直接插入排序在一组数接近有序的情况下效率是非常高的。

在进行预排序的时候gap的选择很关键,太大了使数据接近有序的效果不行,太小了又浪费效率。所以可以参考下面的方法。

《数据结构-用面相对象方法与C++描述》--- 殷人昆

但是我这里还是用了gap = gap / 3 + 1这种方法,预排序完成的更快。

代码实现: 

//希尔排序
void ShellSort(vector<int>& v, int n)
{
	//gap > 1时是预排序
	//gap == 1时直接插入排序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = v[end + gap];
			while (end >= 0)
			{
				if (tmp < v[end])
				{
					v[end + gap] = v[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			v[end + gap] = tmp;
		}
	}
}

选择排序

选择排序的思想是:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
选择排序在众多排序算法中,效率并不算好,时间复杂度为O(N^2)无论在最好还是最坏的情况下都是一样的,所以在实际情况下,很少人会使用它。

动图演示

代码实现: 

代码上小优化,可以同时找最大和最小的数,使得排序更快

//选择排序—时间复杂度O(N^2)
void SelectSort(vector<int>& v, int n)
{
	int begin = 0, end = n - 1;
	int mini = begin, maxi = begin;
	while (begin < end)
	{
		for (int i = begin + 1; i < end; i++)
		{
			//找小的数
			if (v[i] < v[mini])
				mini = i;

			//找大的数
			if (v[i] > v[maxi])
				maxi = i;
		}
		Swap(v[begin], v[mini]);

		//如果begin和maxi重叠,那么需要修正maxi
		if (begin == maxi)
		{
			maxi = mini;
		}

		Swap(v[end], v[maxi]);
		begin++;
		end--;
	}
}

 冒泡排序

冒泡排序的基本思想是:每次比较两个相邻的元素,如果他们的顺序错误就把它们交换过来,就好像水底下的气泡一样逐渐向上冒。 

 动图演示

代码实现: 

//冒泡排序—时间复杂度O(N^2)
void BubbleSort(vector<int>& v, int n)
{
	for (int i = 0; i < n; i++)
	{
		int exchange = 0;
		for (int j = 1; j < n - i; j++)
		{
			if (v[j - 1] > v[j])
			{
				Swap(v[j - 1], v[j]);
				exchange = 1;
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}

堆排序 

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据,是一个二叉树,在排序之前需要建堆,建堆的方法有两种,①建大堆,即根节点的元素要比孩子结点的元素要大。②建小堆,即根节点的元素比孩子结点的元素要小需要注意的是排升序要建大堆,排降序建小堆。

堆的性质

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

 

 代码实现: 

//堆排序—建大根堆
void AdjustDown(vector<int> &v, int n, int root)
{
	int child = root * 2 + 1;
	while (child < n)
	{
		//选出左右孩子中大的那个
		if (child + 1 < n && v[child + 1] > v[child])//防止数组越界
		{
			child++;
		}
		//孩子和父亲比较
		if (v[child] > v[root])
		{
			Swap(v[child], v[root]);
			root = child;
			child = root * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(vector<int>& v, int n)
{
	for (int i = (n - 1 - 1 ) / 2; i >= 0; i--)
	{
		AdjustDown(v, n, i);//向下调整前提:左右子树必须是大/小堆
	}
	// o(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(v[0], v[end]);
		AdjustDown(v, end, 0);
		end--;
	}
}

 快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想是:任取待排序元素序列中的某元素作为基准数,按照该基准数将待排序集合分割成两子序列,左子序列中所有元素均小于基准数,右子序列中所有元素均大于基准数,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

要实现快速排序可以有以下几种方法:

hoare法

需要注意的是选择左边的数作为key值,那必须右边先开始走,右边作为key值,那必须左边先开始走。因为这样做才能保证左边和右边相遇的时候是比key值要小的。

下面的挖坑法也是这样。 

代码实现: 

void QuickSort(vector<int>& v, int begin, int end)
{
	//如果只剩一个值或者区间不存在直接返回
	if (begin >= end)
	{
		return;
	}

	int left = begin, right = end;
	int keyi = left;
	while (left < right)
	{
		//右边先走,找到比key小的就停下来
		while (left < right && v[right] >= v[keyi])
		{
			right--;
		}

		//左边走,找到比key大的就停下来
		while (left < right && v[left] <= v[keyi])
		{
			left++;
		}

		Swap(v[left], v[right]);
	}
	Swap(v[keyi], v[left]);
	keyi = left;
	//左区间
	QuickSort(v, begin, keyi - 1);
	//右区间
	QuickSort(v, keyi + 1, end);
}

挖坑法

代码实现: 

void QuickSort(vector<int>& v, int begin, int end)
{
	//如果只剩一个值或者区间不存在直接返回
	if (begin >= end)
	{
		return;
	}

	int left = begin, right = end;
	int key = v[begin];
	int piti = begin;
	while (left < right)
	{
		//右边先走,找到比key小的就停下来
		while (left < right && v[right] >= key)
		{
			right--;
		}

		v[piti] = v[right];
		piti = right;

		//左边走,找到比key大的就停下来
		while (left < right && v[left] <= key)
		{
			left++;
		}

		v[piti] = v[left];
		piti = left;
	}
	
	v[piti] = key;

	//左区间
	QuickSort(v, begin, piti - 1);
	//右区间
	QuickSort(v, piti + 1, end);
}

 前后指针法

代码实现: 

void QuickSort(vector<int>& v, int begin, int end)
{
	//如果只剩一个值或者区间不存在直接返回
	if (begin >= end)
	{
		return;
	}

	int prev = begin, cur = prev + 1;
	int keyi = begin;

	while (cur <= end)
	{
		if (v[cur] < v[keyi] && ++prev != cur)
			Swap(v[prev], v[cur]);

		cur++;
	}
	Swap(v[prev], v[keyi]);
	keyi = prev;

	//左区间
	QuickSort(v, begin, keyi - 1);
	//右区间
	QuickSort(v, keyi + 1, end);
}

 非递归版本

递归的问题,在极端场景下如果深度太深,会出现栈溢出,所以我们要改成非递归版本

我这里就用个栈来模拟递归过程,当然你用个队列来实现也是可以的。

步骤:

  1. 先将大区间进行入栈,先入区间右边的数,再入区间左边的数
  2. 取栈顶元素,划分为小区间
  3. 将这些小区间全部入栈

一直重复这三步就模拟出了递归的过程

int PartQuickSort(vector<int> &v, int begin, int end)
{
	int key = v[begin];
	int piti = begin;
	while (begin < end)
	{
		//右边先走,找到比key小的就停下来
		while (begin < end && v[end] >= key)
		{
			end--;
		}

		v[piti] = v[end];
		piti = end;

		//左边走,找到比key大的就停下来
		while (begin < end && v[begin] <= key)
		{
			begin++;
		}

		v[piti] = v[begin];
		piti = begin;
	}
	v[piti] = key;
	return piti;

}

//利用栈来模拟递归从场景—栈是先进后出
void QuickSortNonR(vector<int> &v, int begin, int end)
{
	stack<int> st;
	//先入右在入左
	st.push(end);
	st.push(begin);

	while (!st.empty())
	{
		int left = st.top();
		st.pop();

		int right = st.top();
		st.pop();

		int keyi = PartQuickSort(v, left, right);

		//左区间入栈
		if (left < keyi - 1)
		{
			st.push(keyi - 1);
			st.push(left);
		}
		
		//右区间入栈
		if (keyi + 1 < right)
		{
			st.push(right);
			st.push(keyi + 1);
		}
	}
}

快速排序中的优化

①对于选择key值的优化,key的选择对于快速排序来说至关重要,因为如果你选择的key值为最大值或者最小值时快速排序的效率就会有所影响,key值最好的是取这组数据的中间值来达到完全二分的情况。所以折中一下在一组数据的首、尾、中间位置去中间值。

//三个数取中间值做为key
int GetMidKeyi(vector<int>& v, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (v[begin] < v[mid])
	{
		if (v[mid] < v[end])
		{
			return mid;
		}
		else if (v[begin] < v[end])
		{
			return end;
		}
		else
		{
			return begin;
		}
	}
	else//v[begin] > v[mid]
	{
		if (v[mid] > v[end])
		{
			return mid;
		}
		else if (v[begin] < v[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}

②对于递归的小区间优化,在递归到小区间时,我们可以选择用插入排序来完成排序工作,因为此时这段小区间的数据肯定是接近有序的而插入排序在接近排序的情况下效率最高为O(N)。

归并排序

归并排序的思想:归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

动图演示

代码实现:

递归版本

//归并排序—时间复杂度O(N*logN)
//空间复杂度O(N)
void _MergeSort(vector<int> &v, int begin, int end, vector<int> &tmp)
{
	//递归返回的条件
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;

	//进行递归
	_MergeSort(v, begin, mid, tmp);
	_MergeSort(v, mid + 1, end, tmp);

	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin1;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (v[begin1] < v[begin2])
		{
			tmp[i++] = v[begin1++];
		}
		else
		{
			tmp[i++] = v[begin2++];
		}
	}

	//如果后面还有数,也要拷贝到tmp中去
	while (begin1 <= end1)
		tmp[i++] = v[begin1++];

	while (begin2 <= end2)
		tmp[i++] = v[begin2++];

	//深拷贝
	int j = begin, k = end - begin + 1;
	while(k-- && j <= end)
	{
		v[j] = tmp[j];
		j++;
	}
}


void MergeSort(vector<int> &v, int n)
{
	vector<int> tmp(n, 0);
	_MergeSort(v, 0, n - 1, tmp);
}

非递归版本

注意边界问题防止越界,对于越界的区间直接进行修正即可

//非递归版本
void MergeSortNonR(vector<int>& v, int n)
{
	vector<int> tmp(n, 0);

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			//修正边界,防止越界
			if (end1 >= n)
			{
				end1 = n - 1;
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}


			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (v[begin1] < v[begin2])
				{
					tmp[j++] = v[begin1++];
				}
				else
				{
					tmp[j++] = v[begin2++];
				}
			}

			//如果后面还有数,也要拷贝到tmp中去
			while (begin1 <= end1)
				tmp[j++] = v[begin1++];

			while (begin2 <= end2)
				tmp[j++] = v[begin2++];
		}

		//深拷贝
		for (int k = 0; k < n; k++)
		{
			v[k] = tmp[k];
		}

		gap *= 2;
	}
}

这里是归并完一次,就将tmp数据一次性拷回原数组中。

你也可以归并完一部分就将这一部分的数据拷回原数组中去,也就是下面的这种写法。

或者

void MergeSortNonR(vector<int>& v, int n)
{
	vector<int> tmp(n, 0);

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			//修正边界,防止越界
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			int k = end2 - begin1 + 1;
			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (v[begin1] < v[begin2])
				{
					tmp[j++] = v[begin1++];
				}
				else
				{
					tmp[j++] = v[begin2++];
				}
			}

			//如果后面还有数,也要拷贝到tmp中去
			while (begin1 <= end1)
				tmp[j++] = v[begin1++];

			while (begin2 <= end2)
				tmp[j++] = v[begin2++];

			int m = i;
			while (k-- && m < n)
			{
				v[m] = tmp[m];
				m++;
			}
		}

		gap *= 2;
	}
}

由于我这里用的是C++来编写的,所以在将tmp数组中的数据拷贝回原数组中时, 不能使用memcpy函数进行拷贝,因为这里涉及到了深浅拷贝的问题,memcpy是浅拷贝。如果你用的是C语言来写的话就可以使用memcpy函数。

计数排序

计数排序的思想:统计相同元素出现的次数,然后将统计的结果写回到原数组中去。

 

对于B应该开多少空间来存放数据合适,使用A中的最大值 - 最小值 + 1,即为B应该开的空间数。时间复杂度:O(max(range,N)) ,空间复杂度:O(range),range为所开空间数。

计数排序的局限性:

  1. 如果是浮点数,字符串之类的就排不了了。
  2. 如果数据范围很大,空间复杂度就会很高,相对不合适。

代码实现:

//计数排序
//时间复杂度:O(max(range,N))
//空间复杂度:O(range)
void CountSort(vector<int>& v, int n)
{
	int max = v[0], min = v[0];
	for (auto vv : v)
	{
		if (vv < min)
			min = vv;

		if (vv > max)
			max = vv;
	}

	int range = max - min + 1;
	vector<int> count(range, 0);

	//统计次数
	for (int i = 0; i < n; i++)
	{
		count[v[i] - min]++;
	}

	//回写排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (count[i]--)
		{
			v[j++] = i + min;
		}
	}
}

 总结

稳定性的概念:假定在待排序的记录序列中,存在多个具有相同的关键字,若经过排序,这些记录的相对次序保持不变,即在原序列中,v[i]=v[j],且v[i]在v[j]之前,而在排序后的序列中,v[i]仍然在v[j]之前,则称这种排序算法使稳定的,否则称为不稳定。 

计数排序在这里不做比较。 

今天的分享就到这里,如果觉得有所收获的话,就给博主三连吧,创作不易,你的支持将是我创作的动力。

谢谢!!!

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

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

相关文章

【Java程序设计】【C00174】基于SSM在线医院管理系统(论文+PPT)

基于SSM在线医院管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的在线医院管理系统 本系统分为前台系统、后台管理员、后台医生以及后台用户4个功能模块。 前台系统&#xff1a;当游客打开系统的网址后&#xf…

flask基于python的个人理财备忘录记账提醒系统vue

在当今高度发达的信息中&#xff0c;信息管理改革已成为一种更加广泛和全面的趋势。 “备忘记账系统”是基于Mysql数据库&#xff0c;在python程序设计的基础上实现的。为确保中国经济的持续发展&#xff0c;信息时代日益更新&#xff0c;蓬勃发展。同时&#xff0c;随着信息社…

在 Android 中使用 C/C++:初学者综合指南

在 Android 中使用 C/C&#xff1a;初学者综合指南 一、为什么有人在他们的 Android 项目中需要 C/C 支持&#xff1f;二、了解 C 如何集成到 Android 应用程序中三、C和Java程序的编译3.1 Java3.2 Android ART 和 DEX 字节码 四、使用 JNI 包装 C 源代码五、CMake和Android ND…

【讲座分享】| 复旦大学张奇教授——《自然语言发表论文如何打怪升级?NLP顶会论文发表》

文章目录 1 基础关1.1 基础书籍1.2 提高书籍1.3 课程链接1.4 编程实战 2 阅读关2.1 分层过滤2.2 集团作战&#xff0c;信息获取2.3 论文如何泛读 3 动机 方向关3.1 快速发论文3.2 好的研究 4 写作关4.1 论文写作流程4.2 从读者角度出发4.3 每一部分怎么写4.3.1 Abstract摘要4.3…

浅谈一下软件 QA 方法论 和 工具

浅谈一下软件 QA 方法论 和 工具 目录概述需求&#xff1a; 设计思路实现思路分析1.QA方法论2.Java QA工具 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result…

探索Go 语言URL:解析与构建

探索Go 语言URL&#xff1a;解析与构建 在 Go 语言中&#xff0c;解析和处理 URL 是日常开发中常见的任务之一。URL&#xff08;统一资源定位符&#xff09;是指定 Web 资源位置的标准方式&#xff0c;它由多个部分组成&#xff0c;包括协议、主机、路径、查询参数等。本文将深…

调试小结:PHY初始化前后Link Status是否能正确反应网线插上、拔下状态

1 说明 为了验证是否需要初始化PHY才能检测到网线插上、拔下&#xff0c;这里我们对比初始化PHY&#xff08;LAN8720&#xff09;前后&#xff0c;插拔网线PHY寄存器1的bit2的是否按照预期变化来进行测试。 我们查看的PHY寄存器是1的bit2&#xff0c;定义如下&#xff1a; 2…

spring框架(一)

1、Spring框架&#xff1a;IoC和AOP 服务端三层开发&#xff1a;表现层、业务层、持久层 ssm, springboot, springcloud(微服务&#xff0c;治理组件) Spring框架是一个流行的Java应用程序框架&#xff0c;它提供了许多功能来简化企业级应用程序的开发。其中&#xff0c;控制反…

[每日一题] 01.30

文章目录 数列求和质数口袋奇怪数求和 数列求和 n int(input()) print(sum([i for i in range(1,n 1)]))质数口袋 n int(input()) i 2 sum 0 count 0 while n - i > sum:flag Truefor j in range(2,i): # 判断i是否为素数if i % j 0:flag Falsebreakif flag:sum i…

【LLM多模态】Cogview3、DALL-E3、CogVLM、CogVideo模型

note 文章目录 noteVisualGLM-6B模型图生文&#xff1a;CogVLM-17B模型1. 模型架构2. 模型效果 文生图&#xff1a;CogView3模型DALL-E3模型CogVideo模型网易伏羲-丹青模型Reference VisualGLM-6B模型 VisualGLM 是一个依赖于具体语言模型的多模态模型&#xff0c;而CogVLM则是…

BL808学习日志-3-DPI-RGB屏幕使用-LVGL D0

一、DPI-RGB驱动 BL808的手册上显示是支持RGB565屏幕显示输出的&#xff0c;但是一直没找到网上的使用例程。且官方的SDK显示也是能够使用的&#xff0c;只是缺少了驱动。这一部分驱动在SIPEED的SDK中已经内置了&#xff0c;今天就是简单的点亮一个800*480 RGB565的屏幕。 二、…

如何优化博客的内容和用户体验

在当今数字时代&#xff0c;博客成为了分享知识、展示个人专业能力和吸引读者的重要工具。然而&#xff0c;随着越来越多的博客涌现&#xff0c;如何优化博客的内容和用户体验成为了一个关键的问题。本文将为你提供一些有效的技巧&#xff0c;帮助你优化博客的内容和提升用户体…

开启公网即访问:GpuMall平台的云服务解决方案

在人工智能的黄金时代&#xff0c;数据科学家和AI工程师正面临着一个共同的挑战&#xff1a;如何快速、有效地将开发中的服务部署至公网&#xff0c;以应对日益增长的计算和访问需求。GpuMall智算云平台提供了一个全面的解决方案&#xff0c;让这一切变得简单。本文将深入探讨G…

【C++】C++入门—— 引用

引用 1 前情提要2 概念剖析3 引用特性4 常引用5 使用场景5.1做参数5.2 做返回值 6 传值 传引用的效率比较7 引用与指针的差异Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读下一篇文章见 1 前情提要 在C语言中&#xff0c;我们往往会遇见复杂的指针&#xff08;如…

python 写入csv文件 内容乱码

问题 python 写入csv文件 内容乱码 详细问题 笔者核心代码 import csv # 将数据写入 CSV 文件 csv_file_path "soil_data.csv" header ["经度", "纬度", "土壤类型", "pH值"]with open(csv_file_path, mode"w&q…

java 社区资源管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web社区资源管系统是一套完善的java web信息管理系统 &#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.…

vue3封装el-pagination分页组件

1、效果如图&#xff1a; 2、分页组件代码&#xff1a; <template><div class"paging"><el-config-provider :locale"zhCn"><el-paginationv-model:current-page"page.currentPage"v-model:page-size"page.pageSize…

如何更新github上fork的项目(需要一定git基础)

如何更新Fork的项目(需要一定git基础) 前言&#xff1a;本文记录一下自己在github上fork了大佬的开源博客项目https://github.com/tangly1024/NotionNext&#xff0c;如何使用git克隆以及自定义开发和同步合并原项目更新迭代内容的的步骤 如何更新fork的项目(进阶版) 首先你…

DML的原理:一篇文章让你豁然开朗

推荐阅读 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;一&#xff09; 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;二&#xff09; 文章目录 推荐阅读DML 数据操纵语言INSERT语句UPDATE语句DELETE语句SELECT语句 DML 数据操纵语言 DML是…

深入了解Yum:Linux系统的软件包管理利器

目录 软件包 软件包的来源 关于yum yum是什么 yum的相关操作 介绍rzsz rz&#xff08;从Windows本地传到Linux服务器&#xff09; sz&#xff08;从Linux服务器传到Windows本地&#xff09; 注意事项 查看软件包 安装软件 卸载软件 yum的本地配置 为什么要进行配置…