排序算法讲解

news2024/11/19 17:39:59

1)排序思想:

2)排序代码:

3)注意点:

4)时间/空间复杂度和稳定性

下面的排序是以实现升序讲解的。

(一)直接插入排序

1)排序思想:

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

2)排序代码:

void InsertSort(int* a, int n)
{
    for(int i = 0;i < n - 1;i++)
    {
        // 第一趟排序
        int end = i;
        int temp = a[end + 1];
        while(end >= 0 && temp < a[end])
        {
            // 向后移动数据,直到找到合适的位置
            a[end + 1] = a[end];
            end--;
        }
        // 找到合适的位置,进行插入
        a[end + 1] = temp;
    } 

}

3)注意点:

        a) 总趟排序的end小标的范围是[0, n-1],而不是[0, n], 因为如果是[0, n], temp = a[end+1]会造成越界访问;

        b) 在内部while循环的循环条件要保证end不能小于0,因为当end减到-1时,在进行a[end]会造成越界访问。

4)时间/空间复杂度和稳定性

直接插入排序的时间复杂度:当要排序的数据是逆序时,时间复杂度为O(N^2); 当要排序数据是顺序的时,时间复杂度为O(N)。

空间复杂度:O(1).

稳定性:

算法的稳定性指的是在排序算法中,具有相同键值的元素在排序前后的相对位置是否保持不变。

在某些排序场景中,可能存在相同键值的元素,比如多个学生按照成绩进行排序。如果排序算法是稳定的,那么具有相同成绩的学生,就要以交卷时间进行排名(花费时间少的排名高)在排序后仍然保持原来的顺序,并且排序后的结果是稳定的。

插入排序算法的基本思想是将待排序的元素逐个插入已排序部分的合适位置。在插入过程中,如果存在相同键值的元素,就会将新元素插入到已存在的元素之后,从而保持相同键值元素的相对顺序不变。

所以插入排序算法是稳定的

与冒泡排序的横向比较:

冒泡排序是严格的n-1+(n-2)+(n-3)+(n-4)+……+2+1,无论数据是什么样子的。

而对于插入排序来说,只有当数据是完全逆序是(从前向后插入)才是这么多次的比较次数:1+2+……+(n-2)+(n-1),如果插入数据时,其前面已经是有序的或者有几个不是有序的,那么这个数据就不需要进行完全的比较就可以确定其位置。例如:1 2 3 4 ,中插入一个5,就可以直接将5插入到该序列的后面就能保证数据是有序的;或者是:1 2 4 5中插入一个3,只需要比较依次比较5 和 4两次后就能确定3的位置,保证数据是有序的。

而如果对于 1 2 4 5 3,使用冒泡排序的话,就需要1 2和2 4 和4 5和5 3、1 2和2 4和4 3和4 5

这么多次比较才能将插入的3放到相应的位置。

(二)希尔排序

1)排序思想:

先选定一个整数作为分割距离,把待排序文件中所有记录分成几个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后取重复上述分组和排序的工作。当距离到达1时,所有记录在统一组内排好序。

2)排序代码:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap /= 2;
		for (int i = 0; i < n - gap; i++) // i<n,在第一趟时end=i=n-1,此时temp会越界
		{
			int end = i;
			int temp = a[end + gap];
			if (temp < a[end])
			{
				while (end >= 0 && a[end] > temp) 
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				a[end + gap] = temp;
			}
		}
	}
}

3)注意点:

1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快,最后一次排序的时间复杂度为O(N)。这样整体而言,可以达到优化的效果。
 

这里的注意点和直接插入排序算法要注意的是一样的。直接插入排序的实现就相当于希尔排序算法的gap间距为1.

4)时间/空间复杂度和稳定性

时间复杂度:

空间复杂度:O(1)

稳定性:

在希尔排序的过程中,相同键值的元素可能会因为间隔的不同而被分散到不同的分组中。当进行插入排序时,不同分组内的元素会交替进行比较和移动,这可能导致相同键值的元素之间的相对顺序发生变化。

因此,由于希尔排序采用间隔分组的方式,相同键值元素可能跨越不同的分组,排序后的结果可能会改变相同键值元素的相对位置,所以希尔排序是不稳定的

这个分析方法是按照,每次排序后数据还是逆序的进行分析。实际上效率会比下面的分析得到的结果高。

(三)选择排序

A. 直接选择排序

1)排序思想:

它的基本思想是:每次从待排序序列中选择最小的元素,将其放到序列的开头;从排序序列中选择最大的元素,将其放到序列的结尾。

2)排序代码:

void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int maxi = begin, mini = begin;
		// 找到最大值和最小值的下标
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}

			if (a[i] < a[mini])
			{
				mini = i;
			}
		}
		// 交换
		Swap(&a[maxi], &a[end]);
		// 如果最小值在序列结尾,将最大值与最小值交换后,最小值已经不在序列结尾处了
		// 所以要记录下最小值的位置
		if (mini == end)
		{
			mini = maxi;
		}
		Swap(&a[mini], &a[begin]);

		begin++;
		end--;
	}
}

3)注意点:

一要注意更新每一次最大值和最小值,以便能够找到正确的最大值和最小值;

二要注意最大值放到序列最后时,可能会导致最小值的下标指向的不再是最小值。

4)时间/空间复杂度和稳定性

时间复杂度:选择排序的外层循环执行了 n-1 次,内层循环在最坏情况下需要执行 (n-1) + (n-2) + ... + 1 = n(n-1)/2 次比较操作。同时,每次外层循环还需要进行两次元素交换操作。 因此,整个选择排序的时间复杂度为 O(n^2)。

空间复杂度:选择排序只需要使用常数级别的额外空间用于存储临时变量,因此空间复杂度为 O(1)。

稳定性:

稳定性分析: 如果序列中存在相同键值的元素,并且该值是最大值(在从前向后寻找最大值时,会选择前面一个值作为最大值并交换到本次序列的最后;在第二次寻找最大值时,会选择这个相等的值,并放到本次序列的最后,但是本次序列的最后是上一次序列最后的前一个),在排序完成后它们的相对位置发生变化,导致算法不稳定。所以直接选择排序是不稳定的

选择排序是一种简单的排序算法,它的基本思想是每次从未排序部分选择最小(或最大)的元素,放到已排序部分的末尾。在对长度为n的数组进行选择排序时,需要进行n-1轮比较和交换。

在第一轮比较中,需要将第一个元素与后面n-1个元素进行比较,共需要n-1次比较。 在第二轮比较中,需要将第二个元素与后面n-2个元素进行比较,共需要n-2次比较。 以此类推,最后一轮比较只需要将倒数第二个元素与最后一个元素进行比较,共需要1次比较。

因此,总的比较次数可以计算为:(n-1) + (n-2) + ... + 2 + 1 = n(n-1)/2。

对于长度为100的数组进行选择排序,比较的次数为100(100-1)/2 = 4950次。

B. 堆排序

1)排序思想:

用向下调整法建一个大堆,将根节点和最后一个节点进行交换,然后再对根节点进行向下调整使新的根节点到达合适的位置。每完成一次排序,堆的节点个数减一,以此类推,直到堆的节点个数为1的时候,完成排序。

2)排序代码:

// 向下调整法
void AdjustDwon(int* a, int n, int root)
{
	int child = root * 2 + 1;
	while (child < n)
	{
		// 找到较大的孩子节点
		if (child + 1 < n && a[child] < a[child + 1])
		{
			child++;
		}
		if (a[root] < a[child])
		{
			// 交换
			Swap(&a[root], &a[child]);
			root = child;
			child = root * 2 + 1;
		}
		else
		{
			break;
		}
	} 	
}

void HeapSort(int* a, int n)
{
	// 利用大堆实现升序

	// 使用向下调整法将数组调整成为大堆
	int end = n - 1; // 最后一个叶子节点的下标
	for (int i = (end - 1) / 2; i >= 0; i--)
	{
		AdjustDwon(a, n, i);
	}

	// 排序
	// 根节点与最后一个节点交换,根节点向下调整,堆的数据量减一;直到堆的数据量为0时结束排序
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		end--;
	}
}

3)注意点:

注意向下调整的实现方法。

4)时间/空间复杂度和稳定性

时间复杂度:O(N*logN)

空间复杂度:O(1)

稳定性:

使用向下调整时,如果一个节点的两个孩纸节点的值相同的并且为最大值,我们默认实现的是左孩子结点与其父节点进行交换,最终这个左孩子结点会被交换到根节点的位置,然后进行根节点和最后一个叶子节点进行交换,堆的节点数减一;第二次进行向下调整时,右孩子节点为最大值会被移动到根节点,然后再与本次最后一个叶子节点进行交换,但此时最后一个叶子节点是上一次排序的最后一个叶子节点的前一个。起初,这两个节点的顺序是AB == 最大值,但排序过后的顺序变成BA == 最大值。

所以,堆排序是不稳定的

(四)交换排序

A. 冒泡排序法

1)排序思想:

它的基本思想是:比较相邻的元素,如果前面的元素大于后面的元素,则将它们互换位置。重复进行这个过程,直到不能再交换为止。

具体来说,冒泡排序的过程如下:

  1. 依次比较相邻的元素,如果前面的元素大于后面的元素,则将它们互换位置。

  2. 对所有相邻的元素进行一次遍历后,最后一个元素一定是当前未排序部分中最大的元素。

  3. 对剩余的元素继续进行第1、2步操作,直到整个序列有序为止。

2)排序代码:

void Swap(int* x, int* y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

void BubbleSort(int* a, int n)
{
	// 整趟排序逻辑
	for (int i = 0; i < n - 1; i++)
	{
		// 一趟排序
		int Isexchange = 0;
		for (int j = 1; j < n - i;j++)
		{
			if (a[j - 1] > a[j])
			{
				Swap(&a[j - 1], &a[j]);
				Isexchange = 1;
			}
		}
		if (Isexchange == 0) // 如果没有发生交换,则说明数组已经是有序的了,此时不需要再进行下去了
		{
			break;
		}
	}
}

3)注意点:

内部循环的范围:第一次范围是[1, n-1];此时最后一个数已经到最后要到的位置,第二次排序不需要再对最后一个数进行排序,所以第二次范围是[1, n-1-1]……

4)时间/空间复杂度和稳定性

时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:相同键值的元素不会发生交换,所以在排序前后两者相对位置不会发生变化,所以冒泡排序算法是稳定的

B. 快速排序

1)排序思想:

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

一趟排序逻辑:key左边的值小于key,key右边的值大于key-->最终左右序列中间中间部分就是key值排序完成的位置。就相当于一次单趟排序将基准值移动到其最终的位置。

当右边指针先移动时, 左右指针在找大和找小过程中最终相遇位置一定是比key值小的:

如果右指针先走,最终相遇前有两种情况:

a)  R动L不动,R与L相遇在L的位置上;

b)L动R不动,L与R相遇在R的位置上。

a)R先走,左边没有比key值小的,就一直向左移动,直到与L指针相遇,而由于上一轮L和R交换后(R值是小于key值的),L指针指向的值就是小于key值的

b) R先走找到小于key的值后停下来,接着L向后走直到找到大于key的值或与R相遇,而R最后停下的地方就是小于key值的。

这里补充一下:如果是实现降序,那就是左边找小,右边找大。

先让左边先走还是先让右边先走却决于key值的位置:如果key值在左边,那就让右边先后;如果key值在右边,那就让左边先走。

总之就是让key对面的指针先走才能保证两指针相遇的位置是小于key值的

2)排序代码:

// 单趟排序
int PartSort1(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		// 左边找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		// 右边找小
		while (left< right && a[right] >= a[keyi])
		{
			right--;
		}
	}
	Swap(&a[keyi], &a[left]);
	return left;
}


void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int keyi = PartSort1(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

3)注意点:

hoare单趟排序:

a) 要求右指针先进行找小,在进行左指针找大(保证两者相遇位置的值小于key值)

b) 如果条件只是 大于或小于 key值,找小和找大过程中可能会死循环:当找大/找小过程中有值等于key值会跳出找大和找小循环,然后进行交换,再回到外层循环判断条件(begin<end)循环继续进入内部,由于当前值等于key值所以不会进入内部循环,会再次进行交换、外部条件判断……最后死循环;

c) 也可能会找不到小于key值和大于key值,导致越界

d) 递归结束条件:最后的情况是有一组两个数的数组:一个是key,另一个要么在key的左边(左边进行递归的参数为(begin,key-1),begin等于key-1;右边进行递归的参数为(key+1,key)该范围不存在);要么在key的右边,那么其左边的范围不存在,右边范围两端点相等。

4)时间/空间复杂度和稳定性

时间复杂度:O(N^logN)

空间复杂度:O(1)

稳定性:如果待排序序列中存在相同关键字的元素,而它们在划分过程中被放置到不同的子序列中,那么它们的相对顺序就会改变,导致不稳定性。所以快速排序是不稳定的

5)对hoare快速排序的优化

在数据理想的情况下,如果每次选取的key值最终位置都在序列的中间那么时间复杂度为O(N^logN);

但如果数据接近有序的情况下,需要进行N次排序才能保证排序完成,最终时间复杂度为O(N^2); 

a) 优化方案一:三数取中

避免上图的第二种情况:key值在最左边,尽量保证key值最终在两序列的中间,

如何实现呢? 我们发现当key值的大小接近最大值和最小值的中间值时,那么最终数据被分为左右序列时,左右序列的数据个数会比较平均,这样最后交换后key值最终就尽可能处于中间位置了。

而出现第二种情况的原因是数据基本已经接近有序了,这也就意味着序列中间位置的值接近中间值,所以可以选择中间位置的值作为key值。

但是如果选择中间位置的值作为key值后,在进行单趟排序时还要确定左右指针先走还是后走问题,为了不改变单趟排序的逻辑,我们可以将中间位置的值与最左边的值交换。

但是又不能对于所有将要排序的序列都交换中间值,要记住交换仅仅是为了应对数据接近有序这种极端情况

所以,我们可以选择三个数中的中间值下标与左边交换,这样既能够应对极端情况,还能不影响普通情况。

int Getmidi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
			return left;
	}
	else  //a[mid] < a[left]
	{
		if (a[right] < a[mid])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
			return left;
	}
}

	int midi = Getmidi(a, left, right);
	Swap(&a[left], &a[midi]);
注意:

三数取中法的快速排序并不总是在任何情况下都是速度最快的排序方式。

快速排序的时间复杂度是平均情况下最好的,但在某些特殊情况下,例如已经有序或者逆序的序列,快速排序的性能会下降。三数取中法是一种优化手段,用于尽量避免这种情况。

三数取中法选择首、尾和正中三个数进行取中,选取它们的中间值作为基准值。这样可以有效避免快排单链的情况,尤其对已经有序的序列的速度改善明显。

但是仍然存在一些特殊序列,比如大量重复元素的序列,三数取中法也无法完全解决 每次划分操作的时间复杂度是O(n) 的情况,并且需要执行n次划分操作。这样,总的时间复杂度将是O(n * n),即O(n^2)。

b)挖空法

由于hoare这种方法需要考虑左右指针先走问题,我们可以对这个进行改进:

当我们选取key值后,就在key值位置形成一个坑,这样就自然而然地让右面先走找到一个值填到这个坑中,右边的值填过去后,右边就会形成一个新的坑,接着让左边找值填坑。

int PartSort2(int* a, int left, int right)
{
	int midi = Getmidi(a, left, right);
	Swap(&a[left], &a[midi]);
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		while (left < right && a[right] >= a[hole])
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= a[hole])
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

所以挖坑法并不是对快速排序进行优化,而是让我们实现快速排序时逻辑更简单。

c)前后指针法

使用前后指针法会更容易实现快速排序。

主要思想是(实现升序):让cur找小于key的值,++prev,交换prev和cur的值。

在移动过程中prev和cur的位置会有两种情况:

1. prev相邻,当cur找到小于key的值时,++prev后交换,相当于自己跟自己交换;

2. 当cur遇到大于key值时,cur和prev会拉开距离,当cur再次找到小于key值时,两指针中间的数值都是大于key值的,++prev,prev指向大于key的值,交换后,大于key的值被放到后面,小于key的值被放到前面。(本质类似于推箱子,将大于key的区间推到后面,小于key的区间推到前面)

3. 当cur移动到数组之外时,prev指向小于key的值

int PartSort3(int* a, int left, int right)
{
	int midi = Getmidi(a, left, right);
	Swap(&a[left], &a[midi]);

	int keyi = left;
	int pre = left;
	int cur = pre + 1;

	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++pre != cur) //尽量用这种方式
		{
			Swap(&a[cur], &a[pre]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[pre]);
	return pre;
}
d) 递归深度的优化

由二叉树的知识可以知道:最后几层的节点个数占据整个树节点的大多数,所以对于后面几层的节点在使用快速排序(递归),消耗会比较大,我们可以使用插入排序进行排序,减少递归的消耗。

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end) 
		return;
	if ((begin - end + 1) > 10)
	{
		int key = PartSort3(a, begin, end);//第一趟排序
		QuickSort(a, begin, key - 1);//左边递归,排序
		QuickSort(a, key + 1, end);//右边递归,排序
	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

6)非递归实现快速排序

在使用递归算法时也存在一些消耗,包括:

1.内存消耗:递归算法需要使用系统栈空间来保存函数的执行现场和局部变量等信息,当递归深度较大时,占用的栈空间也会随之增加。如果递归深度过大,可能会导致栈溢出的异常,从而使程序崩溃。

2.时间消耗:递归算法一般需要进行多次函数调用,而函数调用的过程涉及到参数传递、现场保存和恢复等操作,这些操作都会耗费一定的时间。此外,在某些情况下,递归算法还可能存在重复计算的问题,造成额外的时间消耗。

3.维护复杂度:递归算法通常需要维护递归深度、参数传递等复杂度,增加了程序的复杂度和维护难度。

为了消除递归的消耗,我们可以用其他方法来模拟递归过程。

模拟实现递归的方法基本上有两种:一是用栈模拟;而是用循环模拟。

快排的思想是:第一次将key值移动到正确位置,并以key值位置将序列分割为两个序列,然后再对这两个序列进行key值的移动---这个过程类似于二叉树的前序遍历,可以用栈进行模拟实现。

入栈要保存的数据是要进行单趟排序的范围。

具体过程如下:

  1. 先将数据的起始位置压入栈中,然后出栈得到第一次要进行排序的范围,进行第一次单趟排序;
  2. 第一次排序结束后,key位置将序列分为两个部分:[left, key-1] 和 [key+1, right];判断这两个范围是否合法:合法将这两个范围压入栈中;不合法(范围不存在 或左右端点相等)就不需要再排序了;
  3. 之后出栈得到第二次排序的范围,进行第二次单趟排序的范围;
  4. 重复上述过程,直到栈为空时,排序完成
void QuickSortNonR(int* a, int left, int right)
{
	// 创建栈
	Stack stack;
	StackInit(&stack);
	StackPush(&stack, right);
	StackPush(&stack, left);

	while (!StackEmpty(&stack))
	{
		int begin = StackTop(&stack);
		StackPop(&stack);

		int end = StackTop(&stack);
		StackPop(&stack);

		// 单趟排序
		int keyi = PartSort1(a, begin, end);
		// 右数组入栈
		if (keyi + 1 < end)
		{
			StackPush(&stack, end);
			StackPush(&stack, keyi + 1);
		}
		
		// 左数组入栈
		if (begin < keyi - 1)
		{
			StackPush(&stack, keyi - 1);
			StackPush(&stack, begin);
		}
		
	}

	StackDestroy(&stack);
}

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

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

相关文章

[Angular] 笔记 10:服务与依赖注入

什么是 Services & Dependency Injection? chatgpt 回答&#xff1a; 在 Angular 中&#xff0c;Services 是用来提供特定功能或执行特定任务的可重用代码块。它们可以用于处理数据、执行 HTTP 请求、管理应用程序状态等。Dependency Injection&#xff08;依赖注入&#…

竞赛保研 基于人工智能的图像分类算法研究与实现 - 深度学习卷积神经网络图像分类

文章目录 0 简介1 常用的分类网络介绍1.1 CNN1.2 VGG1.3 GoogleNet 2 图像分类部分代码实现2.1 环境依赖2.2 需要导入的包2.3 参数设置(路径&#xff0c;图像尺寸&#xff0c;数据集分割比例)2.4 从preprocessedFolder读取图片并返回numpy格式(便于在神经网络中训练)2.5 数据预…

002、使用 Cargo 创建新项目,打印 Hello World

1. Cargo 简介 Cargo 是 Rust 的构建系统和包管理工具&#xff0c;比如构建代码、下载依赖的库、构建这些库等等。在安装 Rust 时&#xff0c;Cargo也会一起安装。 2. 创建新项目的具体步骤 步骤1&#xff1a; 我们在桌面新建一个文件夹&#xff0c;用于存放后面练习用的代码文…

如何让机器人具备实时、多模态的触觉感知能力?

人类能够直观地感知和理解复杂的触觉信息&#xff0c;是因为分布在指尖皮肤的皮肤感受器同时接收到不同的触觉刺激&#xff0c;并将触觉信号立即传输到大脑。尽管许多研究小组试图模仿人类皮肤的结构和功能&#xff0c;但在一个系统内实现类似人类的触觉感知过程仍然是一个挑战…

【计算机网络】快速做题向 极限数据传输率的计算(有噪声/无噪声)

首先需要理解什么是码元 码元在课本上的概念比较难理解 但是只要记住 二进制码元在图上显示的就是有两种高度的横杠“—”&#xff08;对应0&#xff0c;1&#xff09;&#xff0c;即&#xff0c;有两种二进制码元 四进制就是有四种高度的横杠“—”&#xff08;对应00&…

与擎创科技共建一体化“数智”运维体系,实现数字化转型

小窗滴滴小编获取最新版公司简介 前言&#xff1a; 哈喽大家好&#xff0c;最近分享的互联网IT热讯大家都挺喜欢&#xff0c;小编看着数据着实开心&#xff0c;感谢大家支持&#xff0c;小编会继续给大家推送。 新岁即将启封&#xff0c;我们一年一期的运维干货年末大讲也要…

【C++】开源:fast-cpp-csv-parser数据解析库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍fast-cpp-csv-parser数据解析库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一…

小程序面试题 | 22.精选小程序面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

亚马逊云科技 re:Invent 大会 - ElastiCache Serverless 模式来袭

大会介绍 亚马逊云科技的 re:Invent 大会是一年一度的&#xff0c;面向全球技术开发者科技盛会。几乎每次都会发布云科技、云计算等相关领域的产品重磅更新&#xff0c;不但将时下主流热门的技术不断整合&#xff0c;也未将来的发展标明了方向。 亚马逊云科技开发者社区为开发…

一键启动Python世界:PyCharm安装全攻略与pyinstaller魔法转换

文章目录 一、 前言二、PyCharm1.PyCharm的介绍2.PyCharm相比较cmd的优势3.PyCharm的下载4.PyCharm的安装4.1 第一步4.2 第二步4.3 第三步4.4 第四步4.5 第五步4.6 第六步4.7 安装完成4.8 同意条款4.9 数据共享4.10 软件界面4.11 新建项目4.12 项目编写和运行4.13 汉化 三、 打…

python消费rabbitmq

队列经常用&#xff0c;能保持信息一致性。也能跨语言&#xff0c;java写的生产者&#xff0c;推到队列中&#xff0c;python写的消费者消费。 这里&#xff0c;生成者&#xff0c;我们是java&#xff0c;已经发了一条消息了。 python是使用pika来链接rabbitmq 安装pika pip…

java SSM课程平台系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM课程平台系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S…

若依(Spring boot)框架中如何在不同的控制器之间共享与使用数据

在若依框架或Spring boot框架中&#xff0c;控制器共享和使用数据是为了确保数据一致性、传递信息、提高效率和降低系统复杂性。这可以通过全局变量、依赖注入或数据库/缓存等方式实现。共享和使用数据对框架的正常运行非常关键&#xff0c;有助于促进控制器之间的协同工作&…

After Effects 2021 for Mac(AE 2021)

After Effects 2021是一款由Adobe公司开发的视频特效和动态图形制作软件&#xff0c;它主要用于电影、电视和网络视频的后期制作。该软件可以帮助用户创建各种令人惊叹的视觉效果&#xff0c;包括动态图形、文字特效、粒子系统、3D渲染等。 After Effects 2021提供了数百种特效…

钦丰科技(安徽)股份有限公司携卫生级阀门管件盛装亮相2024发酵展

钦丰科技(安徽)股份有限公司携卫生级阀门管件盛装亮相2024济南生物发酵展&#xff01; 展位号&#xff1a;2号馆A65展位 2024第12届国际生物发酵产品与技术装备展览会&#xff08;济南&#xff09;于3月5-7日在山东国际会展中心盛大召开&#xff0c;展会同期将举办30余场高质…

Linux应用程序管理与安装

一.Linux应用程序基础&#xff1a; 1.Linux应用程序与命令的关系&#xff1a; 两者的用途区别&#xff1a; 系统命令&#xff1a;命令文件一般在安装操作系统一起安装&#xff0c;用于辅助操作系统本身的管理。 应用程序&#xff1a;应用程序一般需要在操作系统之外另行安装&a…

学习笔记11——Spring的XML配置

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://www.baobeihuijia.com/bbhj/contents/3/192584.html SSM框架——IOC基础【BeanSetter注入加载xml】 框架总览 Spring Framework 谈谈我对Spring的理解 - 知乎 (zhihu.com)java - 【架构视角】一篇文章带你彻底…

PYTHON基础:K最邻近算法

K最邻近算法笔记 K最邻近算法既可以用在分类中&#xff0c;也可以用在回归中。在分类的方法&#xff0c;比如说在x-y的坐标轴上又两个成堆的数据集&#xff0c;也就是有两类&#xff0c;如果这个时候有个点在图上&#xff0c;它是属于谁&#xff1f; 原则就是哪一类离它比较近…

【Unity6.0+AI】Unity版的Pytorch之Sentis-把大模型植入Unity

本教程详细讲解什么Sentis。以及恶补一些人工智能神经网络的基础概念,概述了基本流程,加载模型、输入内容到模型、使用GPU让模型推理数据、输出数据。 官方文档 Unity Sentis: Use AI models in Unity Runtime | Unity 主页介绍 官方文档链接:Sentis overview | Sentis | 1…

常见的一些库函数

什么是库函数&#xff1a; 库函数是一组预先定义好的函数&#xff0c;可以通过包含相应的头文件来使用。它们提供了各种常用的功能和算法&#xff0c;使得编程更加方便和高效。 库函数的作用如下&#xff1a; 提供常用功能&#xff1a;库函数提供了各种常用的功能&#xff0c;…