【数据结构】快排的详细讲解

news2024/11/24 14:52:15
目录:

介绍

一,递归快排确定基准值

二,递归遍历

三,非递归的快排

四,快排的效率


介绍

        快排是排序算法中效率是比较高的,快排的基本思想是运用二分思想,与二叉树的前序遍历类似,将数据划分,每次划分确定1个基准值(就是已经确定好有序后位置的数据),以升序为例,基准值左面的数据都比此值小,右面的数据都比此值大,然后以基准值为分界线,经过不断划分,最终基准值的个数达到n,数据即有序,因此,递归运用是不二选法,也可运用非递归,但是比较麻烦。

一,递归快排确定基准值

确定基准值的方法常用的有三种,普通法,挖坑法,前后指针法。

1,普通法,具体思想图如下(以升序为例):


        上面说过,基准值的确定过程要保证左面的数据都比此值小或大,右面的数据都要比此值大或小,因此,此基准值就确定了在整个数据中的位置。以升序为例,我们可以从开头和末尾遍历数据,比开头(以首元素为基准)元素大的放在最后,比开头元素小的数据放在前面,最终当两者相遇后再与开头元素交换即可确定基准值(注意:此步骤有细节,具体后面会说明)。如下图:

基准值过程图

        现在有个注意要素套提一下,当上图中的前面L和后面R相遇时,如何保证此值一定比首元素小呢?这里我们需要控制好L的走向即可,即让R先走,当遇见比首元素小时退出,然后让L走,最后让两者进行交换,这样一来无论出现什么情况,当L与R相遇时对应的数据将一定比首元素(即以第一个元素为基准)小,此步骤称为预排序。

基准值的确定代码如下:

void Swap(int* x1, int* x2) {
    int t = *x1;
    *x1 = *x2;
    *x2 = t;
}

int PartSort1(int* a, int begin, int end) {
    int key = begin;
    while (begin < end) {
        //注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
        //因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.

        while (begin < end && a[end] >= a[key]) {
            end--;
        }
        //当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
        while (begin < end && a[begin] <= a[key]) {
            begin++;
        }
        Swap(a + end, a + begin);
    }
    Swap(a + key, a + begin);
    return begin;
}


2,挖坑法,原理图如下(以升序为例):

        挖坑发大致思想与普通法一样,不同的是挖坑发有了坑位。挖坑发是先将首元素保存,将此位置形成坑位(其实坑位上有数据,但坑位的数据不影响,为了方便理解,所以在上图中的坑位就没写上去),然后开始首尾遍历(尾要先遍历,原理同上),比key大的元素放在后面,比key小的元素放在前面,一旦不满足此情况,这个数据将给到位置L或位置R,原本的位置将会形成坑位,直到两者相遇为止,结束遍历,最后把key的值放入坑位即可。代码如下:

int PartSort2(int* a, int begin, int end) {
    int key = a[begin];
    int hole = begin;//开头先形成坑位
    while (begin < end) {
        // 右边先走(原理与PartSort1原理一样),找小,填到左边的坑,右边形成新的坑位
        while (begin < end && a[end] >= key) {
            end--;
        }
        a[hole] = a[end];
        hole = end;
        // 左边再走,找大,填到右边的坑,左边形成新的坑位
        while (begin < end && a[begin] <= key) {
            begin++;
        }
        a[hole] = a[begin];
        hole = begin;
    }
    a[hole] = key;
    return hole;
}


3,前后指针法(prev是前指针,cur是后指针,此指针是位置指针,不是我们所常说的指针型),原理图如下:

        前后指针法跟上面两种方法有很大不同,如上,以第一个元素为基准,即定义key值为首元素,cur往前遍历,prev随之跟上cur的步伐,当prev遇到的数据比key小,prve向前移动;当prev遇到的数据比key大,prev停止移动,此时,cur不断向前移动,一旦找到比key小的数据就会跟prev指向的数据进行交换,最后,当cur遍历完整个数据后cur与key会进行交换,确定此时key所对应的值比左边数据大,比右边数据小。代码如下:

void Swap(int* x1, int* x2) {
    int t = *x1;
    *x1 = *x2;
    *x2 = t;
}
int PartSort3(int* a, int begin, int end) {
    int front = begin + 1;
    int back = begin;
    while (front <= end) {
        if (a[front] <= a[begin]) {
            back++;
            Swap(a + back, a + front);
        }
        front++;
    }
    //因为后指针控制,所以当程序结束后back所指向的数据都比keyi所指向的数据小
    Swap(a + begin, a + back);
    return back;
}


总:以上三种遍历确定基准值的方法在快排称为预排序,每一趟预排序都可确定数据中一个元素的排序位置,每当确定一个数据后相对位置后,我们只需要不断以上次遍历时确定的基准值为界,递归遍历数据,即可确定最终确定序列。


二,递归遍历

        当我们明白如何确定基准值后,接下来就是程序的结构搭建了,上面说过,快排递归跟二叉树的前序遍历一样,并且还需要以基准值为分界线,不断确定基准值,具体思路导图如下:

        当确定好基准值key后,以区间[begin, key - 1]和区间[key + 1, end]进行划分(begin是要进行遍历时,开头元素的坐标,end是要遍历时,结尾元素的坐标,如上图),以次区间不断进行与二叉树前序遍历相同的递归,根据上图所示,很明显,当begin>=end时结束下一层递归。代码如下:

void QuickSort(int* a, int begin, int end)
{
    //即当不存在区间时结束,即就排好了一个数
    if (begin >= end)
        return;
    //运用普通法PartSort1,此算法是返回一个顺序表中中间值的坐标,在坐标左边都小于此数,在坐标的右边都大于此数
    int keyi = PartSort1(a, begin, end);//也可用挖坑法和前后指针法
    // 区间递归: 以keyi为界,左[begin, keyi-1],右[keyi+1, end],一直缩小,最终会逐渐会缩小成有序
    QuickSort(a, begin, keyi - 1);//在keyi的左面进行遍历
    QuickSort(a, keyi + 1, end);//在keyi的右面进行遍历
}

下面是总代码:

#include <stdio.h>
void Swap(int* x1, int* x2) {
	int t = *x1;
	*x1 = *x2;
	*x2 = t;
}
int PartSort1(int* a, int begin, int end) {
	int key = begin;
	while (begin < end) {
		//注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
		//因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.
		while (begin < end && a[end] >= a[key]) {
			end--;
		}
		//当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
		while (begin < end && a[begin] <= a[key]) {
			begin++;
		}
		Swap(a + end, a + begin);
	}
	Swap(a + key, a + begin);
	return begin;
}
void QuickSort(int* a, int begin, int end)
{
	//即当不存在区间时结束,即就排好了一个数
	if (begin >= end)
		return;
	//PartSort1算法是返回一个顺序表中中间值的坐标,在坐标左边都小于此数,在坐标的右边都大于此数
	int keyi = PartSort1(a, begin, end);
	// 区间递归: 左[begin, keyi-1] keyi 右[keyi+1, end],一直缩小,最终会逐渐会缩小成排序
	QuickSort(a, begin, keyi - 1);//在keyi的左面进行遍历
	QuickSort(a, keyi + 1, end);//在keyi的右面进行遍历
}
void Print(int* a, int n) {
	for (int i = 0; i < n; i++) {
		fprintf(stdout, "%d ", a[i]);
	}
	puts("");
}
void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	Print(a, sizeof(a) / sizeof(int));
}
int main() {
	TestQuickSort();
	return 0;
}

运行图:


三,非递归的快排

        运用非递归,大多数要运用栈结构,因为递归本身其实就是不断入栈和出栈,递归过程跟栈结构一样,进入递归就是入栈,出函数就是出栈,都是先进后出。快排的非递归实现,我们也可用栈来实现。根据前面递归的运用,递归是不断进行区间分割,我们可将此区间放入栈中,然后进行不断循环遍历,每当遍历时就将区间放入栈中,一旦用完此区间就释放,跟递归中函数栈帧的创建与销毁一样。非递归结构代码如下:

1,栈的建立

typedef struct stack {
    int* Data;
    int Capacity;
    int Top;
}Stack;
 //以下三个是要运用栈结构的算法
void StackInit(Stack* S);
void StackPop(Stack* S);
void StackPush(Stack* S, int X);
//栈功能的实现
void StackInit(Stack* S) {//初始化栈
    assert(S);
    S->Data = 0;
    S->Capacity = 0;
    S->Top = -1;
}
void StackPop(Stack* S) {//出栈
    assert(S || S->Data || S->Top != -1);
    S->Top--;
}
void StackPush(Stack* S, int X) {//入栈
    assert(S);
    if (!S->Data) {
        S->Data = (int*)malloc(sizeof(int) * 4);
        assert(S->Data);
        S->Capacity = 4;
    }
    else if (S->Top == S->Capacity - 1) {
        S->Data = (int*)realloc(S->Data, (sizeof(int) * S->Capacity) * 2);
        assert(S->Data);
        S->Capacity *= 2;
    }
    S->Data[++S->Top] = X;
}

2,非递归的结构

void QuickSort(int* a, int left, int right) {
 //创建栈结构S,以栈来模仿递归过程
    Stack* S = (Stack*)malloc(sizeof(Stack));
    StackInit(S);
    StackPush(S, right);
    StackPush(S, left);
    while (S->Top != -1) {
        //确定左右区间,每当遍历完一次时要及时更换,即从栈中去除操作
        int begin = S->Data[S->Top];
        StackPop(S);
        int end = S->Data[S->Top];
        StackPop(S);
        //用指定好的区间进行预排序,即一次遍历
        int key = PartSort1(a, begin, end);
        //进行左区间的遍历
        if (end - 1 > begin) {
        //注意栈结构先进后出的特点,要先把end装进去
            StackPush(S, end - 1);
            StackPush(S, begin);
        }
        //进行右区间的遍历
        if (begin + 1 < end) {
        //同理,要先把end装进去
            StackPush(S, end);
            StackPush(S, begin + 1);
        }
    }
    free(S);
    //注意,不能在此算法内这样写,因为这是的a是首元素地址,即指针,sizeof(a)为地址的大小
    //Print(a, sizeof(a) / sizeof(int));

}

        以上是非递归过程中的逻辑代码,除此两大步,其它的逻辑运用与递归无任何区别,总代码如下:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef struct stack {
	int* Data;
	int Capacity;
	int Top;
}Stack;
 //以下三个是要运用栈结构的算法
void StackInit(Stack* S);
void StackPop(Stack* S);
void StackPush(Stack* S, int X);
//栈功能的实现
void StackInit(Stack* S) {//初始化栈
	assert(S);
	S->Data = 0;
	S->Capacity = 0;
	S->Top = -1;
}
void StackPop(Stack* S) {//出栈
	assert(S || S->Data || S->Top != -1);
	S->Top--;
}
void StackPush(Stack* S, int X) {//入栈
	assert(S);
	if (!S->Data) {
		S->Data = (int*)malloc(sizeof(int) * 4);
		assert(S->Data);
		S->Capacity = 4;
	}
	else if (S->Top == S->Capacity - 1) {
		S->Data = (int*)realloc(S->Data, (sizeof(int) * S->Capacity) * 2);
		assert(S->Data);
		S->Capacity *= 2;
	}
	S->Data[++S->Top] = X;
}
void Swap(int* x1, int* x2) {
	int t = *x1;
	*x1 = *x2;
	*x2 = t;
}
int PartSort1(int* a, int begin, int end) {
	int key = begin;
	while (begin < end) {
		//注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
		//因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.
		while (begin < end && a[end] >= a[key]) {
			end--;
		}
		//当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
		while (begin < end && a[begin] <= a[key]) {
			begin++;
		}
		Swap(a + end, a + begin);
	}
	Swap(a + key, a + begin);
	return begin;
}
void QuickSort(int* a, int left, int right) {
 //创建栈结构S,以栈来模仿递归过程
	Stack* S = (Stack*)malloc(sizeof(Stack));
	StackInit(S);
	StackPush(S, right);
	StackPush(S, left);
	while (S->Top != -1) {
		//确定左右区间,每当遍历完一次时要及时更换,即从栈中去除操作
		int begin = S->Data[S->Top];
		StackPop(S);
		int end = S->Data[S->Top];
		StackPop(S);
		//用指定好的区间进行预排序,即一次遍历
		int key = PartSort1(a, begin, end);
		//进行左区间的遍历
		if (end - 1 > begin) {
		//注意栈结构先进后出的特点,要先把end装进去
			StackPush(S, end - 1);
			StackPush(S, begin);
		}
		//进行右区间的遍历
		if (begin + 1 < end) {
		//同理,要先把end装进去
			StackPush(S, end);
			StackPush(S, begin + 1);
		}
	}
	free(S);
	//注意,不能在此算法内这样写,因为这是的a是首元素地址,即指针,sizeof(a)为地址的大小
	//Print(a, sizeof(a) / sizeof(int));
}
void Print(int* a, int n) {
	for (int i = 0; i < n; i++) {
		fprintf(stdout, "%d ", a[i]);
	}
	puts("");
}
int main() {
	int a[] = { 0,5,7,9,3,4,1,6,2,8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	Print(a, sizeof(a) / sizeof(int));
	return 0;
}

运行图:


四,快排的效率

1,快排的效率分析

        快排效率在平常说是效率比较高的,大致根据二叉树原理计算,快排时间复杂度为O(nlogn),空间复杂度为O(logn),但这只是对于大多时候,其实快排的时间效率是很不确定的,快排的效率跟数据的原有序列有关,序列越接近有序,快排效率越低。我们先观察以下图:

快排效率最好情况


快排效率最坏情况

        可知,快排预排序的时间效率在区间[logn,n],当原有序列越有序时,无论是递归还是非递归时间效率都很低,有序时效率最低,而遍历元素的时间复杂度不变,一直是O(n),因此快排的时间效率在区间[nlogn,n^2]。


2,三数取中

        当快排在有序时(升序为例),数据会靠近左边进行排序,而要想提高快排的效率,就必须尽量让基准值尽量往中间靠拢,但这样很难控制,因为这与数据原有的序列有关。虽然说我们不能直接控制,但是我们可控制最坏情况来进而控制时间效率,即序列有序时的情况。

        通常,我们是选取首元素为基准值的,因此,只要控制好首元素不为基准值的情况即可,也就是三数取中。

        三数取中是将判断首元素,尾元素,中间元素三者之间的大小,将中间大的数据与首元素交换,使首元素不可能为基准值。代码如下:

int GetMidi(int* a, int left, int right)
{
    int mid = (left + right) / 2;//中间数mid
    //下面比较 left mid right 三者之间大小,将中间大的数据的下标返回过去
    //先人left与mid比较,然后进一步判断right

    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
        {
            return mid;
        }
        else if (a[left] > a[right])  // mid是最大值
        {
            return left;
        }
        else
        {
            return right;
        }
    }
    else
    {
        if (a[mid] > a[right])
        {
            return mid;
        }
        else if (a[left] < a[right]) // mid是最小
        {
            return left;
        }
        else
        {
            return right;
        }
    }
}

        具体思想就是先两两比较,然后进一步与第三者比较,上面代码中选举了left与mid两者之间比较,然后再跟第三者right比较,满足中间大的数据返回其下标。


3,改善后算法的运用

        有了三数取中,快排将不会出现最坏情况,虽说有可能会出现次坏情况,但基本是不可能的,因为这种情况很是要求原序列的次序和三数取中的交换,因次,如若在算法中加上三数取中后算法的时间复杂度基本为O(nlogn)。下面是改进后运用的代码:

int GetMidi(int* a, int left, int right)
{
    int mid = (left + right) / 2;//中间数mid
    //下面比较 left mid right 三者之间大小,将中间大的数据的下标返回过去
    //先人left与mid比较,然后进一步判断right

    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
        {
            return mid;
        }
        else if (a[left] > a[right])  // mid是最大值
        {
            return left;
        }
        else
        {
            return right;
        }
    }
    else
    {
        if (a[mid] > a[right])
        {
            return mid;
        }
        else if (a[left] < a[right]) // mid是最小
        {
            return left;
        }
        else
        {
            return right;
        }
    }
}
void Swap(int* x1, int* x2) {
    int t = *x1;
    *x1 = *x2;
    *x2 = t;
}
//普通法
int PartSort1(int* a, int begin, int end) {
    //运用三数取中,与每次预排序的区间首元素交换,防止出现最坏情况
    int midi = GetMidi(a, begin, end);
    Swap(a + begin, a + midi);
    //以下代码正常不变
    int key = begin;
    while (begin < end) {
        //注意此步骤,end必须先开始(即当左边开始行走右边一定有比key小的值),
        //因为要控制最后的begin最终比key(开头元素)小,因此,右边必须先走.

        while (begin < end && a[end] >= a[key]) {
            end--;
        }
        //当走左边时,最终会Swap(a + end, a + begin);交换后begin的最终比key小
        while (begin < end && a[begin] <= a[key]) {
            begin++;
        }
        Swap(a + end, a + begin);
    }
    Swap(a + key, a + begin);
    return begin;
}
//挖坑发
int PartSort2(int* a, int begin, int end) {
    //运用三数取中,与每次预排序的区间首元素交换,防止出现最坏情况
    int midi = GetMidi(a, begin, end);
    Swap(a + begin, a + midi);
    //以下代码正常不变
    int key = a[begin];
    int hole = begin;//开头先形成坑位
    while (begin < end) {
        // 右边先走(原理与PartSort1原理一样),找小,填到左边的坑,右边形成新的坑位
        while (begin < end && a[end] >= key) {
            end--;
        }
        a[hole] = a[end];
        hole = end;
        // 左边再走,找大,填到右边的坑,左边形成新的坑位
        while (begin < end && a[begin] <= key) {
            begin++;
        }
        a[hole] = a[begin];
        hole = begin;
    }
    a[hole] = key;
    return hole;
}
//前后指针法
int PartSort3(int* a, int begin, int end) {
    //运用三数取中,与每次预排序的区间首元素交换,防止出现最坏情况
    int midi = GetMidi(a, begin, end);
    Swap(a + begin, a + midi);
    //以下代码正常不变
    int front = begin + 1;
    int back = begin;
    while (front <= end) {
        if (a[front] <= a[begin]) {
            back++;
            Swap(a + back, a + front);
        }
        front++;
    }
    //因为后指针控制,所以当程序结束后back所指向的数据都比keyi所指向的数据小
    Swap(a + begin, a + back);
    return back;
}

        在以上中,除了预排序算法需要改进,其它的都不用动即可实现高效的快排。最后,跟大家再次强调一下快排的效率,快排在大多数情况下确实效率很高,但快排的效率受原数据的序列影响比较大,当序列越接近有序时,快排的效率可能还没有其它算法高,在以后的运用中要不要用快排还需根据原数据的情况而定。

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

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

相关文章

C++ 之如何将数组传递给函数?

在本文中&#xff0c;您将学习将数组传递给C 中的函数。您将学习如何传递一维和多维数组。 数组可以作为参数传递给函数。也可以从函数返回数组。考虑以下示例&#xff0c;将一维数组传递给函数&#xff1a; 示例1&#xff1a;将一维数组传递给函数 C 程序通过将一维数组传递…

从事嵌入式工作有哪些优势?

随着物联网和人工智能的发展&#xff0c;嵌入式技术越来越值钱&#xff0c;学嵌入式的人也越来越多&#xff0c;现在开始入行嵌入式。根据一些权威部门统计&#xff0c;我国目前嵌入式软件人才缺口每年为40万人左右&#xff0c;嵌入式人才供给一直处于供不应求的状态。 那么从…

Docker私有仓库打开2375端口(linux)

前言 在我们开发测试过程中&#xff0c;需要频繁的更新docker镜像&#xff0c;然而默认情况下&#xff0c;docker的2375端口是关闭的&#xff0c;下面介绍如何打开端口。 1、打开步骤 1.1、修改配置 登录docker所在服务器&#xff0c;修改docker.service文件 vi /usr/lib/sys…

Mac解压缩软件BetterZip免费版注册码下载

软件介绍 BetterZip免费版是一款适用于Mac系统的解压缩软件&#xff0c;软件具备了专业、实用、简单等特点&#xff0c;它可以让用户更快捷的向压缩文件中添加和删除文件&#xff0c;同时兼容性也十分优秀&#xff0c;支持ZIP &#xff0c; SIT &#xff0c; TAR、BZIP2 &…

Linux 系统日常运维经典技能

【微|信|公|众|号&#xff1a;厦门微思网络】 【微思网络www.xmws.cn&#xff0c;成立于2002年&#xff0c;专业培训21年&#xff0c;思科、华为、红帽、ORACLE、VMware等厂商认证及考试&#xff0c;以及其他认证PMP、CISP、ITIL等】 linux运维必备认证-RHCE&#xff0c;学RHC…

mysql 物理备份及恢复

一、物理复制的基本概念 物理备份:直接复制数据库文件&#xff0c;适用于大型的数据库环境&#xff0c;不受存储引擎的限制&#xff0c;但不能恢复到不同的mysql版本 完整备份&#xff1a;也叫完全备份&#xff0c;每次将所有数据&#xff08;不管自第一次备份有没有修改过&…

盘点日本IT行业到底怎么样?赴日IT分析

相比其它行业&#xff0c;日本的IT行业对于人才需求旺盛、就业前景比较光明。一方面&#xff0c;日本IT企业在创新和开发方面寻求领先&#xff0c;需要大量的IT人才为其所用。另一方面&#xff0c;随着全球数字化趋势的加速和政府信息化的不断推进&#xff0c;各个领域都在不断…

力扣第257题 二叉树的所有路径 c++ 树 深度优先搜索 字符串 回溯 二叉树

题目 257. 二叉树的所有路径 简单 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,null,5] 输出&#xff1a;["1->2-&g…

Win11右键恢复Win10老版本

Win11右键恢复Win10老版本 最近自己更新了windows11的OS,整体感觉都是不错的,但是就是每次右键菜单我都要再次点击下展开更多选项,这对追求极简主义的我,就是不爽, 手动恢复win10操作吧! 第一种:创建文件(简单快速) 1.新建一个resoreRightKey.reg文件,并在里面填入如下代码 W…

阿里云服务器乌兰察布带宽收费价格表

阿里云服务器华北6&#xff08;乌兰察布&#xff09;地域公网带宽价格表&#xff0c;1M带宽价格是23元/月&#xff0c;按使用流量价格是0.8元每GB&#xff0c;阿里云服务器网来详细说下1M带宽、5M带宽、6M带宽、10M带宽、20M带宽、50M带宽和100M等带宽收费价格表&#xff1a; …

课题学习(四)----四元数解法

一、四元数解法 为了求解惯性导航的力学方程&#xff0c;姿态矩阵 R b b R^b_{b} Rbb​可以有姿态微分方程得到。其中&#xff0c;四元数是常用的方法&#xff0c;如下图所示&#xff0c;假设刚体在原点旋转&#xff0c;根据欧拉定理&#xff0c;运动坐标系(b系列)相对于导航坐…

计算机竞赛 题目:基于深度学习的人脸表情识别 - 卷积神经网络 竞赛项目 代码

文章目录 0 简介1 项目说明2 数据集介绍&#xff1a;3 思路分析及代码实现3.1 数据可视化3.2 数据分离3.3 数据可视化3.4 在pytorch下创建数据集3.4.1 创建data-label对照表3.4.2 重写Dataset类3.4.3 数据集的使用 4 网络模型搭建4.1 训练模型4.2 模型的保存与加载 5 相关源码6…

vue3 vscode no tsconfig与找不到名称“ref”。ts(2304)

如题&#xff0c;这两个问题都与tsconfig的配置有关&#xff0c;先看下问题表现&#xff1a; 解决方法&#xff0c;应当正确配置如下&#xff0c;之后保存或重启vscode&#xff1a;

Stm32_标准库_7_光敏传感器

AO端口&#xff1a;通俗的讲大概是根据环境亮度的不同导致电阻的阻值不同&#xff0c;最后AO口输出的模拟量也不同&#xff0c;这个端口是用来测量环境光照的具体强度 DO端口&#xff1a;光敏电阻默认设置了一个阈值&#xff0c;当光照强度高于这个阈值本端口输出低电平&#…

C#使用ICSharpCode.TextEditor制作代码编辑器

效果 类似于vs里的代码风格 准备工作 1 创建Winform项目 2 NuGet下载ICSharpCode.TextEditor 显示控件ICSharpCode.TextEditor 将Debug目录下的ICSharpCode.TextEditor.dll,通过工具栏显示控件,并拖拽到Form窗口代码 1 Form 代码 using System; using S

Midjourney第四篇:9大风格头像

获取图像生成提示词&#xff08;咒语&#xff09;&#xff0c;公众号&#xff1a;科技探幽&#xff0c;回复“mid”&#xff0c;获取详细教程 迪士尼风格 关键词&#xff1a;art by disney ,Disney style,3d character from Disney 皮克斯风格 关键词&#xff1a;art by pix…

跟着播客学英语-Why I use vim ? part one.

why-use-vim-01.png 最近这段时间在学英语&#xff0c;在网上看到有网友推荐可以听英文播客提高听力水平。 正好我自己也有听播客的习惯&#xff0c;只不过几乎都是中文&#xff0c;但现在我已经尝试听了一段时间的英文播客&#xff0c;觉得效果还不错。 大部分都是和 IT 相关的…

MyBatis(JavaEE进阶系列4)

目录 前言&#xff1a; 1.MyBatis是什么 2.为什么要学习MyBatis框架 3.MyBatis框架的搭建 3.1添加MyBatis框架 3.2设置MyBatis配置 4.根据MyBatis写法完成数据库的操作 5.MyBatis里面的增删改查操作 5.1插入语句 5.2修改语句 5.3delete语句 5.4查询语句 5.5like查…

【广州华锐互动】动物解剖学AR互动学习平台

增强现实&#xff08;AR&#xff09;是一种将虚拟信息叠加到现实世界中的技术。通过智能手机、平板电脑或AR眼镜等设备&#xff0c;AR技术可以创建出逼真的虚拟物体&#xff0c;这些物体可以与现实世界的环境相互交互。 AR技术在教育领域的应用非常广泛&#xff0c;包括历史、科…

【redis学习笔记】分布式锁

什么是分布式锁 以往的锁都是只能在当前进程中⽣效, 在分布式的这 种多个进程多个主机的场景下就⽆能为⼒了。 因此提供分布式锁&#xff0c;加锁就是往redis上设置一个特殊的key:value&#xff0c;完成操作后&#xff0c;释放锁就是删除这个key:value&#xff1b;其他服务器尝…