冒泡、插入、希尔、选择、堆排序、快速排序(附源码)

news2024/11/19 1:43:33

目录

插入排序:

核心思想:

时间复杂度:

冒泡排序:

核心思想:

时间复杂度:

希尔排序:

核心思想:

时间复杂度:

选择排序:

核心思想:

时间复杂度:

堆排序:

核心思想:

时间复杂度:

快速排序:

霍尔版本:

核心思想:

快速排序小区间优化:

快速单趟排序改进思路1:挖坑法

快速单趟排序改进思路2:前后指针法

非递归快速排序:

所有排序的源码:

头文件

实现文件:

测试文件:


插入排序:


核心思想:

前面的数据有序,再将后一个数据插入前面的数据,就是一直保证前面的是有序的
例如:前1个有序、前2个有序、前3个有序、塞纳4个有序。。。以此类推,最后n个数据全部有序

在已经排序好的系序列内插入值
先单趟,再多趟 

使用顺序表第一个位置记录数据个数很不好,例如哨兵的设计,因为数据的个数和数据的类型不一致,假设数据类型是char

设计数据结构不要感觉,如果感觉可以,那么可以的理由是什么;如果感觉不可以,那么不可以的理由是什么,不可以似是而非
要有具体的理由,而不是空凭感觉,有充分的理由,据具体的分析,好为什么好?有理有据

该种算法在顺序有序或者接近有序效率比较高,但是逆序效果就会非常差,注意,这个特点很重要

写排序算法,先单趟控制,不要一来直接整体控制,比较复杂,不好把握

时间复杂度:

最坏O(n^2)
最好O(n
)


冒泡排序:

核心思想:

每一趟都保证将最大的数据放到最后一个位置
每一次两两比较,(升序)前一个比后一个大,交换位置,再比较后两个数据,前一个比后一个大,再交换
                  在第一躺的交换就将所有的数据进行了一次遍历比较,保证了将最大的数据放到最后面
                  再在此基础上进行第二躺的比较,目的在于将第二大的数据放到最后的位置

对于冒泡排序和插入排序的区别:
冒牌排序无论是有序、乱序还是逆序,都是严格的等差数列
但是对于插入排序来说,除非是最坏的情况,基本都难以达到等差数列的量级,因为中间的交换次数会少于最多的次数

时间复杂度:

都是O(n^2)
                   


希尔排序:

核心思想:


1、预排序(接近有序):核心思想是让小的数据尽快往前,大的数据尽快往后
2、直接插入排序
完成预排序之后,整个序列就已经接近于有序了,再进行插入排序


gap代表有多少组
随着预排序的进行,会逐渐接近有序,后面挪动的就少了

预排序:
一组一组排序:先第一组,再控制gap躺
 多组并排:直接++i,不用分组,直接对间隔为gap的两个数据之间进行插入排序,每一次都是一次数据有限的插入排序。理解清楚

先控制单趟,假设[0,end]是有序的,从end位置开始,从end+1往前进行对比排序,写完单趟,再对整体进行控制

控制单趟:对前end位置循环对比,这是一组;再对下一组进行比较。对gap分组的一整个数据组进行排序,这算作一趟
再整体:整个数组分成gap组,对每个gap组进行单趟排序

时间复杂度:

平均下来是O(n^1.3)  (非常叼)

选择排序:

核心思想:

在整个序列选出最小值和最大值,最小值放在左边,最大值放在右边,然后依次再选出次小的和次大的。

先控制单趟,然后再整体控制,将区间往中间缩小
选择排序:遍历选择出最大的和最小的,然后将最大的放在后面,最小的放在前面;这就是单趟
然后,begin++,end--,缩小数组序列的间隔,因为最后的位置已经排序好了,因此缩小范围,在还未进行排序的序列组内继续进行上述的逻辑,挑出该组最大的和最小的进行操作。 

但是注意有一个坑:例如,当最大的值在第一个位置,那么选出最小的值的时候就会将两者交换,但是下标没有变化,此时已经将最小的值交换到了第一个位置,然而maxi的位置依旧指向第一个位置,那么,此时选出最小值的操作已经结束,但是找到最大值的操作就会出现错误,因为此时maxi指向的第一个位置已经不是最大值了,而是变化最小值了,交换就会出现错误。
当此时再堆maxi进行交换,就会出现将第一个最小值和最后一个位置交换,结果导致,最小的在最后,最大的在某个位置,序列全乱,完全没有达到排序的效果
怎么解决这个问题?
很简单,进行一个if判断即可
maxi的值被改变,maxi的值应该是mini的位置,更新一下maxi = mini即可
因为交换第一个位置的时候,第一个位置是maxi,但是交换后,第一个位置已经被换成了min值,而最大值也被换到了mini的位置,所以此时最大值在mini的位置,此时更新一下maxi的位置即可

时间复杂度:

O(n^2)
因为无论是有序还是无序,无论交换与否都需要每次选出最大最小,所以,即使是有序时间复杂度依旧是O(n^2)

堆排序:

核心思想:

首先将数据进行建堆,然后将堆顶和最后的位置交换,再将堆顶向下调整。


向下调整建堆:倒着往上调整,而叶子节点是不需要向下调整的,所以调整位置从最后一个节点的父亲节点开始向上,逐个进行向下调整
我们的数据序列在物理结构上是一个数组,逻辑上是一个二叉树,从最后一个节点的父亲节点开始,依次向前,对数组的每一个数据进行向下调整,这样就保证了每一个节点都进行了向下调整建堆
因此,比较重要的是向下调整的逻辑:
从堆顶开始,和左右孩子进行比较,假设法,首先假设左孩子是符合条件的值,再对左后孩子的值进行比较,选出合适的孩子值,这是一个比较,子节点和父亲节点进行交换,再依次更新父节点和孩子节点,再考虑结束条件即可。当end为0时,执行向下调整,说明就剩下一个数据了,不需要进行调整了。

假设数组的大小为size,那么数组的下标范围是【0,size-1】
如果条件为<size,那么正好

时间复杂度:

O(NlogN)  

快速排序:

霍尔版本:

核心思想:

找一个数据key,比key小的在左边,大的在右边,最后key的位置在序列的位置即定


例如说,左边有三个比key小的,那么key就在第四个位置
左边有4个比key小的,那么可以就在第五个位置
也就是说,当把所有比key小的放在左边,比key大的放在右边,那么最终即使是排好序的序列里,key的位置依旧是这个位置
那么,再把左右两边的序列变化有序的,那么整体就是有序的了

单趟:先选择第一个位置为key,然后在整体位置上,先找到右边比较小的值,再找到左边比较大的值,进行交换,再继续进行查找大小值
但是,有一个坑:即while(left < right)的判断条件不是if,l和R是动态变化着的,就有一种可能,即右边已经找到了比较小的值,然后左边开始从当前位置向前查找大的值,但是都没有,那么就会一直跑到right的右边
因为内部并没有判断,得完成了右边小值和左边大值的寻找结束才会进行判断,此时,right在左边,left在右边,再交换,就出错了。所以,在内部就需要多进行一个判断,即left < right

当进行第一次key排序好位置之后,要对key的左边和右边进行处理
怎么处理?
类似一个二叉树的递归过程
根(key)、左子树(区间)、右子树(右区间)
先对左边的区间同上一样的处理,只是此时的区间变成了【begin,keyi - 1】,对该区间进行key排序
再对右边的区间同上一样的处理,只是此时的区间变成了【keyi + 1,   end】,对该区间进行key排序
什么时候条件结束?
当左子树或者右子树只有一个节点,即只剩下一个数据的时候,说明已经递归到最底层了
即,begin == end

但是,还有一个坑:就是当右边遇到一个和key相等的值会停止,左边也遇到一个和key相等的值也会停止,二者进行交换,交换过后;再继续下一个循环,可是,当前位置的right指向的是从左边left换过的key值,
当前位置的left指向的也是刚刚right交换过来的key值;那么再进行循环,还是会停止再当前的位置,两个位置循环交换,循环找,循环停,陷入死循环。
为什么会出现这样的情况?
因为,小的放在左边,大的放在右边
但是,相等的放在哪里?
似乎我们没有进行特殊的处理,仅仅只是对大的和小的进行了处理
因此,我们需要将相等的值也考虑进去
相等的值,放在左边和右边,都无关紧要。
因此,在右边找小值的时候,遇到和key相等的值,不管,继续往左边
同样,在左边找到大值得时候,遇到和key相等得值,不管,继续往右边


还有一个坑:当有序的情况下,其实开始位置用begin+1是有问题的,因为是有序的,右边的right往左边找小值,因为有序所以会一直往左边找,直到遇到left,但是我们初始的位置是begin+1位置
那么就会在begin+1的位置相遇,就会导致begin和begin+1位置进行交换,但是我们的数据本身就是有序的,结果你交换了,反倒打乱了顺序,不符合预期,所以初始位置要从begi位置开始
从begin位置开始,就可以避免这种情况,因为相遇的位置就是key位置本身,自己和自己交换,不影响序列的有序性

但是在有序的情况下,快排的效率是非常低的。因为我们选定key,例如一个有序序列,key是从第一个位置依次往后,那么就会导致,右边的right找小,一直都找不到,需要从头到尾遍历一遍,相当于一个冒泡了
相反,如果我们每次选择的key是一个中间值,那么整体的效率就会变得高效的多,时间复杂度是N*logN,因为类似于二叉树,n个数据有logN层,每一层有n个。
那怎么解决这个问题呢?
导致这个问题的根节点在于,选定第一个位置作为key
那么,我们只需要改变这个key即可
那么是不是意味着我们要将整个的单趟操作重写一遍呢?
不用
我们只需要将比较合适的数据和第一个位置的key进行交换即可,这样就不需要重写单趟逻辑
而且,这样的写法,本质上只是将key换成了一个更合适的值而已,其他都没有变
那么问题来了:怎么选择一个合适的key值呢?
第一种方法:从数据中随机选一个值
第二种方法:三数取中,即第一个、最后一个、中间值,取中间值。逻辑是,选择不是最大的,也不是最小的值做key,可以做到近似二分,效率更高。

为什么相遇左边一定比右边的大?:因为只有两种情况
1、R遇L
为什么相遇位置一定比key小?因为右边先走,当left找到大的,right找到小的,交换位置,交换位置后此时left的值是比key小的,而right是比key大的,此时继续再剩下的序列中ringht寻找比key小的值,直到相遇,将key和相遇位置交换,此时是right动,而在最后的一个过程中,一定是right往左边寻找,此时没有找到比key小的,就会直接找下去直到遇见left位置,交换位置
2、L遇R
当right已经找到了一个比key小的值的时候,轮到左边的left找比key大的值,但是没有,就会和right相遇,即相遇位置依旧是比key小
以上两种相遇方式保证了不论是那种相遇位置值都比key要小,但是关键是right先走

Debug版本本质是往代码文件录入很多调试信息,因此单个栈帧会比较大(所以栈容易溢出)
Release版本优化很多,例如一些中间一些没必要的步骤优化了(速度更快,栈也更多)

快速排序小区间优化:

快速排序递归对于小区间的排序付出的代价是比较大的,我们可以在小区间使用插入排序对区间进行排序,这种方法叫小区间优化
为什么说代价比较大呢?
例如最后几层,有7个值,按照我们的递归写法,我么需要建立几个函数栈帧?


7个函数栈帧,代价是比较大的。
而且如果是比较极端的情况,一个满二叉树,那么,最后几层的节点会非常的多,需要建立大量的函数栈帧,消耗代价是比较大的。
所以,考虑将这部分进行局部优化
快排类似于二叉树的遍历,而满二叉树的下面三层几乎占据整个数据序列的绝大多数,因此插入排序用于最后的3~4层的排序减少了很多递归
但是事实上,这个区间优化的效果并不是很明显,所以也不是很有必要,看你自己需求

上面我们说的是霍尔版本的快速排序,你会发现很麻烦,需要注意的点很多,稍不注意就会写错,所以我们有以下几个改进的方法:

快速单趟排序改进思路1:挖坑法


核心思想:在key位置首先挖一个坑,R往左边找,找到比key小的值,将该值填到key的位置,R所在位置为新的坑


那么什么叫做挖坑呢?就是把key的值记录下来,因为记录下来了,就可以进行覆盖,那么相当于key的位置就空出来了,形象的说,我们将之视为一个坑位。
而后L开始向右找比key大的值,找到后将该值放到右边R所在位置的坑,此时L所在位置又成为一个新的坑,如此反复,最后在而这相遇的位置将key填入,一趟快速排序完成,就保证了key的左边都是比key小的,右边都是比key大的
对于该key值得单趟排序完成,接下来,再对左区间和右区间进行如上单趟排序即可,如此递归到结束。这就是挖坑法的所有逻辑。


快速单趟排序改进思路2:前后指针法


核心思想:依旧是将小的放在前面,大的放在后面(最推荐的写法)

单趟排序:
前指针perv,后指针cur,key是第一个位置的值,
首先cur向右边走,cur的目的在于找比key小的值,没找到,继续向右;
如果找到了,那么说明什么?
说明prev和cur之间的值都是比key大的值
然后,交换位置,把++prev 和 cur位置进行交换
此时,从视觉上看,就像推箱子,这个箱子就是比key大的值的区间
这个区间是【prev,cur】
区间的左边都比key小,右边都比key大
那么,当时cur走到最后的位置的时候,就是把大于key的值区间推到了最后
到此,将prev 和 key位置的值进行交换即可,整个单趟排序结束。

整体控制:
快速排序,只要把单趟写好了,剩下的就是控制整体的区间问题,整个很简单。
【begin,keyi - 1】keyi 【keyi + 1, end】
然后进行递归,先左区间,再右区间
结束条件是当begin >= cur
为什么结束条件是这个呢?
因为,当左边只有一个值时,begin == keyi
当右边没有值时,右边的end就是keyii,keyi + 1 > keyi 

非递归快速排序:


递归改非递归:借助栈
事实上,因为有了三数取中,就可以使得递归的深度大大降低,就可以没有必要再写一个非递归的快排。
但是,我们不能保证,有些时候,递归的深度实在是太深了,那么递归的方式就行不通,所以,还是要掌握非递归
那么,非递归要怎么处理呢?
很简单,用一个栈来处理
因为本质上对于快速排序来说,单趟的逻辑都是一样的,唯一的区别就是要处理其区间的值
由于,单趟排序的性质,会形成左区间、key、右区间的形式
所以,非常类似于二叉树的结构,非常适合用递归
而递归,本质上也是对keyi的值,也就是区间进行处理
例如说,每一个递归函数,是建立一个栈帧,在这个函数栈帧里面,单趟逻辑是一样的
但是,只是从上一层传进来的数据的区间不一样
先递归左区间,是处理左区间
再递归右区间,是处理右区间
一样的道理
用非递归,我们只要控制好这个区间的值就可以了。

所以,使用栈是一个绝佳的方法
首先,begin、end入栈,对这个区间进行单趟处理,返回keyi值
begin、end出栈
再将该keyi的左区间和右区间进栈
再处理栈内记录的左区间,和有区间
如此循环,直到栈为空,结束排序

所有排序的源码:

头文件

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"Stack.h"

//插入排序
void InsertSort(int* a, int i);
//希尔排序
void ShellSort(int* a, int n);
//冒泡排序
void BUbbleSort(int *a ,int n);
// 选择排序
void SelectSort(int* a, int n);
// 堆排序
void AdjustDwon(int* a, int size, int parent);
void HeapSort(int* a, int n);
//快速排序
void QuickSort(int* a,int begin,int end);

//非递归快速排序
void QuickSortNoneR(int* a, int begin, int end);

实现文件:
 

#include"sort.h"
#include"Stack.h"

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//插入排序
void InsertSort(int* a, int n)
{
	//[0,end]该区间内是有序,将end+1的数据插入到前面的区间内
	//再依次往后传递
	//小心越界的问题
	//数组最后的位置是n-1
	//我们排序的范围是,end + 1
	//要让end + 1是最后的位置n - 1
	//那么,最后的范围要等于n - 2
	for (int i = 0; i < n - 1; ++i)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;

	}
}



	//冒泡排序
	void BUbbleSort(int* a, int n)
	{
		for (int i = 0;i<n;++i)
		{
			for (int  j = 0; j< n - i - 1;++j)
			{
				if (a[j] > a[j + 1])
				{
					Swap(&a[j],&a[j+1]);
				}
			}
		}
	}


	//希尔排序
void ShellSort(int* a, int n)
{
		//预排序

	//int gap = n;
	//while (gap > 0)
	//{
	//	gap /= 3 + 1;
	//	for (int i = 0; i < n - gap; i += gap)
	//	{
	//		int end = i;
	//		int tmp = a[end + gap];
	//		while (end >= 0)
	//		{
	//			if (tmp < a[end])
	//			{
	//				a[end + gap] = a[end];
	//				end -= gap;
	//			}
	//			else
	//			{
	//				break;
	//			}
	//		}
	//		a[end + gap] = tmp;
	//	}
	//}

		int gap = n;
		while (gap > 1)
		{
			//这里的gap条件>1,当gap大于1的时候,会进入循环,此时,gap再进行处理,一定会变成1
			//这就保证了最后一次执行的插入排序一定是gap=1
			gap = gap / 3 + 1;

			for (int i = 0; i < n - gap; ++i) 
			{
				//控制单趟
				int end = i;
				int tmp = a[end + gap];
				while (end >= 0)
				{
					if (tmp < a[end])
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = tmp;
			}

		}
}

// 选择排序
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	int maxi = begin;
	int mini = begin;
	while (begin < end)
	{
		for (int i = begin; i < end; ++i)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
			if (a[i] < a[mini])
			{
				mini = i;
			}
			Swap(&a[begin], &a[mini]);
			if (a[mini] > a[maxi])
			{
				maxi = mini;
			}
			Swap(&a[end], &a[maxi]);
		}
		--end;
		++begin;
	}

}

// 堆排序(升序,建大堆)
void AdjustDwon(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size) 
	{
		if (child + 1 < size  && a[child] < a[child + 1])//保证右孩子存在
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2+ 1;
		}
		else
		{
			break;
		}
	}

}

void HeapSort(int* a, int n)
{
	//向下建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon(a,n,i);
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0],&a[end]);
		AdjustDwon(a, end, 0);
		--end;
	}

}


int GetMidi(int *a,int  begin, int end)
{
	int midi = (begin + end) / 2;
	if (a[begin] > a[end])
	{
		if (a[end] > a[midi])
			return end;
		else if(a[midi] > a[end])
			midi;
		else
			return begin;
	}
	else//a[end] < a[midi]
	{
		if (a[end] > a[midi])
			return end;
		else if (a[begin] > a[midi])
			return midi;
		else
			return begin;
	}
}

//快速排序:单趟排序-hore版本(增加区间优化)
int PartSort1(int* a, int begin, int end)
{
	//区间优化
	if (end - begin + 1 <= 10)
	{
		InsertSort(a + begin, end - begin + 1);
	}

	int midi = GetMidi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = begin;
	int left = begin;
	int right = end;

	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	keyi = left;
	return keyi;
}


//快速排序:单趟排序-挖坑法
int PartSort2(int* a, int begin, int end)
{

	int midi = GetMidi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int key = a[begin];
	int left = begin;
	int right = end;

	while (left < right)
	{
		while (left < right && a[right] > key)
		{
			--right;
		}
		Swap(&a[left],&a[right]);

		while (left < right && a[left] < key)
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	a[left] = key;
	return left;
}

//快速排序:单趟排序-前后指针法
int PartSort3(int* a, int begin, int end)
{
	
	int midi = GetMidi(a, begin, end);
	Swap(&a[begin], &a[midi]);

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

	初始版本,逻辑上比较好理解
	//while (cur <= end)
	//{
	//	if (a[cur] > a[keyi])
	//	{
	//		++cur;
	//	}
	//	else
	//	{
	//		if (++prev != cur)
	//		{
	//			Swap(&a[prev], &a[cur]);
	//		}
	//		++cur;
	//	}
	//}

	//简洁版本
	while (cur <= end)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev],&a[cur]);
		++cur;
	}
	Swap(&a[keyi],&a[prev]);
	return prev;
}

//快速排序
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int keyi = PartSort3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

//非递归实现快速排序
void QuickSortNoneR(int* a, int begin, int end)
{
	Stack st;
	StackInit(&st);

	StackPush(&st, begin);
	StackPush(&st, end);

	//一定要注意栈的进栈和出栈的规律
	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPop(&st);

		int keyi = PartSort3(a, left, right);
		//[left , keyi - 1] keyi [keyi + 1, right]
		
		if(left  < keyi - 1)
		{
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}
	
		if (keyi + 1 < right)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}

	}

	StackDestroy(&st);
}

测试文件:

#include"sort.h"
#include"Stack.h"

void printArray(int* a,int n)
{
	for (int i = 0; i< n;++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
int a[] = { 9,8,7,6,5,4,3,2,1,0 };

void TestInsertSort()
{
	printf("插入排序:");
	InsertSort(a, sizeof(a) / sizeof(int));
	printArray(a, sizeof(a) / sizeof(int));
}

void TestBUbbleSort()
{
	printf("冒泡排序:");
	BUbbleSort(a, sizeof(a) / sizeof(int));
	printArray(a, sizeof(a) / sizeof(int));
}

void TestshellSort()
{
	printf("希尔排序:");
	ShellSort(a, sizeof(a) / sizeof(int));
	printArray(a, sizeof(a) / sizeof(int));
}

void TestHeapSort()
{
	printf("堆排序:");
	HeapSort(a, sizeof(a) / sizeof(int));
	printArray(a, sizeof(a) / sizeof(int));
}


void TestSelectSort()
{
	printf("选择排序:");
	HeapSort(a, sizeof(a) / sizeof(int));
	printArray(a, sizeof(a) / sizeof(int));
}

void TestQuickSort()
{
	printf("快速排序:");
	QuickSort(a,0, sizeof(a) / sizeof(int) - 1);
	printArray(a, sizeof(a) / sizeof(int));
}


void TestQuickSortNoneR()
{
	printf("快速排序(非递归):");
	QuickSortNoneR(a, 0, sizeof(a) / sizeof(int) - 1);
	printArray(a, sizeof(a) / sizeof(int));
}

int main()
{
	//TestInsertSort();
	//TestBUbbleSort();
	//TestshellSort();
	TestHeapSort();
	TestSelectSort();
	//TestQuickSort();
	//TestQuickSortNoneR();

	return 0;
}

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

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

相关文章

Ubuntu上Jenkins自动化部署Gitee上VUE项目

文章目录 1.安装NodeJS插件2.配置全局工具配置-NodeJS环境变量3.新建自由风格的软件项目任务4.配置General配置丢弃旧的构建配置参数化构建过程 5.配置源码管理6.构建触发器7.设置构建环境8.配置构建步骤9.配置构建后操作10测试构建 前文链接&#xff1a; Ubuntu上Jenkins自动…

AI、AIGC、AGI、ChatGPT它们的区别?

今天咱们聊点热门话题&#xff0c;来点科普时间——AI、AIGC、AGI和ChatGPT到底是啥&#xff1f;这几个词听起来好像挺神秘的&#xff0c;但其实它们就在我们生活中。让我们一起探索这些术语的奥秘&#xff01; AI&#xff08;人工智能&#xff09;&#xff1a;先说说AI&#…

spring security oauth2 之GitHub应用注册

前言&#xff1a; 要想使用spring security oauth2 来实现GitHub授权登录&#xff0c;就必须先要有一个GitHub的应用。如果使用gitee也是同理。本文介绍如果注册GitHub应用。 step1:进入到注册应用的页面 注册地址&#xff1a;Sign in to GitHub GitHub step2:填写信息 图中…

MFC教程 -- Windows界面开发

MFC教程 -- Windows界面开发 Windows消息机制 初步认识MFC 要想熟练掌握 Windows 应用程序的开发&#xff0c; 首先需要理解 Windows 平台下程序运行的内部机制。如果想要更好的学习掌握 MFC&#xff0c;必须要先了解Windows 程序的内部运行机制&#xff0c;为我们扫清学习路…

中间件-Nginx加固(控制超时时间限制客户端下载速度并发连接数)

中间件-Nginx加固&#xff08;控制超时时间&限制客户端下载速度&并发连接数&#xff09; 1.1 Nginx 控制超时时间配置1.2 Nginx 限制客户端下载速度&并发连接数 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1.1 Nginx 控制超…

Ubuntu整系统迁移到另一个硬盘中

以ubuntu20.04为例&#xff0c;之前使用的是1T的移动硬盘&#xff0c;每次进入后性能不太稳定&#xff0c;所以最近买了块1T的固态硬盘给我的笔记本装上了&#xff0c;但是如果重新进行各种软件安装及环境配置就太麻烦了&#xff0c;所以采用了系统迁移 1.首先制作一个Ubuntu系…

[Android View] 可绘制形状 (Shape Xml)

一切以官方文档为主 官方文档https://developer.android.com/guide/topics/resources/drawable-resource?hlzh-cn#Shape 什么是可绘制形状 可以理解为用xml文件来描述一个简单的Drawable图形&#xff0c;比如说以下这段xml就可以用来描述一个白色的圆形&#xff1a; <?…

蓝桥杯:真题讲解3(C++版)附带解析

报纸页数 来自&#xff1a;2016年七届省赛大学C组真题&#xff08;共8道题) 分析&#xff1a; --画出报纸长的样子&#xff0c;如果我们在上面多画一张报纸&#xff0c;那么就符合题意的5&#xff0c;6&#xff0c;11&#xff0c;12。 观察这张图&#xff1a;观察3&#xf…

Google大模型Bard更名Gemini,现在实力如何?(VS gpt系列)

名人说&#xff1a;一花独放不是春&#xff0c;百花齐放花满园。——《增广贤文》 作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、简要介绍1、Gemini是什么&#xff1f;2、主要特点3、Gemini的版本4、应用潜力5、…

【Pytorch深度学习开发实践学习】【AlexNet】经典算法复现-Pytorch实现AlexNet神经网络(1)model.py

算法简介 AlexNet是人工智能深度学习在CV领域的开山之作&#xff0c;是最先把深度卷积神经网络应用于图像分类领域的研究成果&#xff0c;对后面的诸多研究起到了巨大的引领作用&#xff0c;因此有必要学习这个算法并能够实现它。 主要的创新点在于&#xff1a; 首次使用GPU…

PyTorch-Ignite的介绍与快速上手

PyTorch-Ignite 是一个用于 PyTorch 的高级库&#xff0c;旨在帮助开发者更快、更简洁地编写可复用的代码来进行深度学习实验。它由 PyTorch 社区开发&#xff0c;提供了一套灵活的抽象&#xff0c;用于构建和管理训练和验证循环&#xff0c;而无需牺牲 PyTorch 的灵活性和强大…

四、《任务列表案例》后端程序实现和测试

本章概要 准备工作功能实现前后联调 4.1 准备工作 数据库脚本 CREATE TABLE schedule (id INT NOT NULL AUTO_INCREMENT,title VARCHAR(255) NOT NULL,completed BOOLEAN NOT NULL,PRIMARY KEY (id) );INSERT INTO schedule (title, completed) VALUES(学习java, true),(学…

电力运维是做什么的?电力行业智能运维工作内容?

电力行业智能运维工作内容具体涉及哪些关键任务&#xff1f;实施智能运维过程中&#xff0c;如何利用现代信息技术、人工智能和大数据分析来提升电力系统的运行效率与维护响应速度?在电力行业中引入智能运维后&#xff0c;对于预防性维护、故障诊断、设备寿命预测以及成本控制…

react native中如何实现tab切换页面以及页面可以左右滑动效果

react native中如何实现tab切换页面以及页面可以左右滑动效果 效果示例图主体代码 效果示例图 主体代码 import React, {useRef, useState} from react; import {View,ScrollView,Text,StyleSheet,Dimensions,Animated, } from react-native; import {pxToPd} from ../../comm…

Linux系统——LAMP架构

目录 一、LAMP架构组成 1.LAMP定义 2.各组件的主要作用 3.CGI和FastCGI 3.1CGI 3.3CGI和FastCGI比较 4.PHP 4.1PHP简介 4.2PHP的Opcode语言 4.3PHP设置 二、LAMP架构实现 1.编译安装Apache httpd服务 2.编译安装Mysql 3.编译安装PHP 4.安装论坛 5.搭建博客 W…

力扣区间题:合并区间、插入区间

我们可以将区间按照左端点升序排列&#xff0c;然后遍历区间进行合并操作。 我们先将第一个区间加入答案&#xff0c;然后依次考虑之后的每个区间&#xff1a; 如果答案数组中最后一个区间的右端点小于当前考虑区间的左端点&#xff0c;说明两个区间不会重合&#xff0c;因此…

当大语言模型遇到AI绘画-google gemma与stable diffusion webui融合方法-矿卡40hx的AI一体机

你有想过建一台主机&#xff0c;又能AI聊天又能AI绘画&#xff0c;还可以直接把聊天内容直接画出来的机器吗&#xff1f; 当Google最新的大语言模型Gemma碰到stable diffusion webui会怎么样&#xff1f; 首先我们安装stable diffusion webui(automatic1111开源项目&#xff…

【基于ChatGPT大模型】GIS应用、数据清洗、统计分析、论文助手、项目基金助手、科研绘图、AI绘图

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

Google Genie:创意互动环境

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

渗透测试靶场环境搭建

1.DVWA靶场 DVWA&#xff08;Damn Vulnerable Web Application&#xff09;是一个用来进行安全脆弱性鉴定的PHP/MySQL Web应用&#xff0c;包含了OWASP TOP10的所有攻击漏洞的练习环境&#xff0c;旨在为安全专业人员测试自己的专业技能和工具提供合法的环境&#xff0c;同时…