认识数据结构之——排序

 一、 插入排序:

直接插入排序(以排升序为例):

排序思想:

  1.   单趟:记录某个位置的值,一个一个和前面的值比较,碰到更大的就往后覆盖,碰到更小的或者相等的就结束,最后将记录的值插入到正确的位置。
  2.   多趟:让索引从第二个元素的位置开始往后迭代(因为一个元素本身不用讨论有序性),记录当前索引的值,一个一个和前面的值比较,碰到更大的就让他往后覆盖,碰到相等的或者更小的就结束循环并将之前记录的值覆盖当前位置。也就是说:每趟排序最后都能确保当前位置以及之前的序列是有序的,而当完成了最后一趟排序之后,最后一个位置以及之前的序列是有序的,这样也就完成的排序。这是一个始终把数组划分为两部分-----左边有序,右边无序-----的过程。

代码实现的细节:

  1. 最外层的for循环从1开始,因为下标为0的第一个元素不用单独关注有序性。
  2. 在每一趟里,和之前的数比较的过程中要使用最开始记录的值而不是下标访问数组,因为起始下标end会改变,而起始下标所指的值val不会变。

代码实现:

//直接插入排序
void InsertSort(int* arr, int size)
{for (int i = 1; i < size; i++) //单个元素不讨论有序性,所以从第二个数开始{int end = i; //起始索引int val = arr[end]; //记录起始索引指向的值while (end > 0){if (val < arr[end - 1])  //用val而不是arr[end]来比较,因为arr[end]//在后续会被覆盖{arr[end] = arr[end - 1];end--;}else{break;}}arr[end] = val;  //此时end来到了正确的位置,直接覆盖}
}

希尔排序(以排升序为例):

 希尔排序产生的必要性:

  思考这样一种情况:我们使用插入排序,期望把一组数排为升序,可这组数本身是降序或者接近逆序的,就会导致其效率直勾勾的达到O(N^2),如下图的极端情况:由于每次end都要遍历到第一个数据才能停下,所执行的次数是1+2+3+......+n,用等差数列的求和公式计算出执行次数的量级是N^2,这时的效率就比较底下了。

排序思想:

  尽管直接插入排序存在上述的不足,也并不妨碍它的排序思想是优秀的,因此在它的增强版---希尔排序诞生了,从名字不难看出,这是一个名叫希尔的人发明的。希尔排序的核心思想就是在进行直接插入排序之前先进行预排序,使序列接近有序,这样就可以保证直接插入排序的效率稳定在O(N)~O(N^2)之间。

详解预排序:

  预排序就是把待排序列分为几组,分别进行直接插入排序。这里所说的分组并不是真的把数给拆开了,而是利用增量来决定直接插入排序逻辑里数组中两个元素比较和交换的间隔。

   因此插入可以看作增量为1的希尔排序,而在这之前的增量不为1的排序就都是预排序,经过特定的逻辑,使得增量最后总能回到1。简单粗暴一点理解:希尔排序 = 一趟或多趟预排序 + 一趟直接插入排序。

代码实现(四层循环):

//希尔排序(四层循环的)
void ShellSort(int* arr, int size)
{int gap = size;while (gap > 1){gap = gap / 3 + 1; //这样可以保证增量gap最后一定是1for (int j = gap; j < size; j++) //确立每一组的起始排序元素{for (int i = j; i <= size-gap; i += gap) //通过增量gap来达到对以j为起始的一组数据的直接插入排序地排序{int end = i;int val = arr[end];while (end >= gap){if (val < arr[end - gap]){arr[end] = arr[end - gap];end -= gap;}else{break;}}arr[end] = val;}}}
}

代码实现(三层循环)

//希尔排序(三层循环的)
void ShellSort(int* arr, int size)
{int gap = size;while (gap>1){gap = gap / 3 + 1; //这样可以保证增量gap最后一定是1for (int i = gap; i < size; i++){int end = i;int val = arr[end];while (end >= gap){if (val < arr[end - gap]){arr[end] = arr[end - gap];end -= gap;}else{break;}}arr[end] = val;}}
}

二、交换排序:

冒泡排序(以排升序为例):

排序思想:

  1. 比较和交换:依次比较一组序列中的一对数,若不符合预期的大小次序(这取决于是期望升序还是降序),则交换他们的值,否则正常的依次往后比较。
  2. 缩小范围:经过第一轮比较后,所期望的最值已经冒到了末尾,下一趟就不用再比较这个位置的值,也就是缩小的比较的范围。
  3. 多次重复第一和第二步,剩下来的第一个数就已经和之后的序列共同构成有序序列了。

代码实现的细节:

  冒泡排序的代码实现实现比较简单,需要注意的只是一点小优化:在序列已经有序的情况下要让结束循环,减少不必要的性能开销。具体实现我放在下面的代码实现里。

代码实现:

//冒泡排序
void BubbleSort(int* arr, int size)
{for (int i = 0; i < size; i++){int flag = 0;for (int j = 0; j < size - i-1; j++)  //j<size-i i每一趟都会加一,//因此j遍历的范围也会缩小{if (arr[j] > arr[j + 1]) //升序逻辑{int mid = arr[j];arr[j] = arr[j + 1];arr[j + 1] = mid;flag = 1;  //如果发生元素交换,记录下来}}if (flag == 0) //优化:如果某一趟排序没有元素发生交换,说明序列有序,不用在继续比较了{break;}}
}

快速排序(以排降序为例):

排序思想(递归):

  1.  选key:选一个关键值key,记录它的下标为keyi(一定得是下标,方便后续递归)。通常选择最左边的值为key。
  2. 左右双指针:定义一个左指针在最左端和一个右指针在最右端。
  3. 双指针的遍历顺序:如果第一步的key选的是最左边得的值,那右指针先动,反之左指针先动。
  4. 双指针的遍历逻辑(以排排降序序列为例):由于最终目的是大的值在左边,小的在右边,因此:右指针先往左遍历,在值大于key处的才停下;左指针随后往右遍历,在值小于key处才停下。此时交换左右指针所指向的值,这样较大的数就到了左边,较小得数就到了右边。一直循环这样的过程直到左右指针相遇才停下。
  5. 和key值交换:第四步结束后,左右指针相遇,在将这个位置的值和keyi处的值交换。
  6. 分割区间来递归:以左右指针相遇的位置为分隔,将待排序序列分为两部分,对这两部分分别重复1---5步,也就递归调用自己这个快速排序的函数,只是每次都会传递更小的两个区间,递归的结束条件是当左指针大于等于右指针。

代码实现的细节(递归):

代码实现(递归):

//快速排序
void _QuickSort(int* arr, int begin, int end)
{//递归的终止条件if (begin >= end){return;}int keyi = begin;  //记录关键值的下标,方便之后的递归传参int left = begin,right = end;while (left < right){while (left < right && arr[right] <= arr[keyi]) //选取左边的值为key,则右指针right先移动{right--;}while (left < right && arr[left] >= arr[keyi]){left++;}//此时right所指的值大于keyi所指的,left所指的小于keyi所指的if (left < right){int mid = arr[right];arr[right] = arr[left];arr[left] = mid;}}//此时right和left相遇,和keyi位置的值交换int mid = arr[left];arr[left] = arr[keyi];arr[keyi] = mid;//此时left和right处的值已经确定,以此将数据划分为两个部分,进行递归_QuickSort(arr, begin, left - 1);_QuickSort(arr, left + 1, end);}

排序思想(非递归):

  1,递归的劣势:快速排序的常规写法是用递归,涉及到函数不断地调用自己,而每次调用函数都要建立函数栈帧,会占用栈区的空间,如果要排序的数据量太大,递归调用的深度太深,会有栈溢出的风险。

   2,使用数据结构的栈:操作系统分配给栈区的空间很小(比如在32位的Linux环境下,栈只有8MB大小),因此不妨使用咱自己数据结构的栈(我们的栈从堆区申请空间,堆区上G的空间老大了哈哈哈哈哈,使劲儿霍霍),模拟排序递归的过程。

  3,快速排序的递归就是区间的划分:有了突破口后再来分析快速排序递归的本质,其实就是在每一趟确定一个数的最终位置后以它为基准再对同样逻辑的函数传递划分后的两个区间的区间值------也就是说,递归调用快速排序函数的过程中栈帧里存的就是一个个区间索引。

代码实现的细节(非递归):

  1,栈的特性是先进先出,后进后出,且只能从顶端取元素。所以左右区间的出栈和入栈时机时相反的----想要先拿到左区间,就得先入右区间,否则先入左区间。

代码实现(非递归):

//快速排序
void QuickSort(int* arr, int begin, int end)
{//普通的递归版本//_QuickSort(arr, begin, end - 1);//非递归_QuickSortUR(arr, begin, end - 1);
}//快速排序(非递归)
void _QuickSortUR(int* arr, int begin, int end)
{Stack st;StackInit(&st);StackPush(&st, end);  //栈先进先出,后进后出,//如果后续想先取到左区间的值,就要先入右区间的值StackPush(&st, begin);while (!StackEmpty(&st)){int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);int keyi = left;int lflag = left, rflag = right;while (left < right){while (left < right && arr[right] >= arr[keyi]){right--;}while (left < right && arr[left] <= arr[keyi]){left++;}if (left != right){int tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;}}if (keyi != left){int tmp = arr[keyi];arr[keyi] = arr[left];arr[left] = tmp;}// [lflag,left-1][left+1,rflag]if (left + 1 <= rflag){StackPush(&st, rflag);StackPush(&st, left+1);}if (lflag <= left - 1){StackPush(&st, left - 1);StackPush(&st, lflag);}}StackDestory(&st);
}//栈的相关实现
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>typedef struct Stack
{int* arr;int top;int capacity;
}Stack,*pst;
//初始化
void StackInit(pst p)
{assert(p);p->arr = NULL;p->top = -1;p->capacity = 0;
}
//尾插
void StackPush(pst p,int val)
{assert(p);if (p->top + 1 == p->capacity){int newCapacity = (p->capacity == 0) ? 4 : 2*p->capacity ;int* newArr = realloc(p->arr, newCapacity * sizeof(int));if (newArr == NULL){perror("realloc fail");exit(1);}p->arr = newArr;p->capacity = newCapacity;}p->arr[++(p->top)] = val;
}
//尾删
void StackPop(pst p)
{assert(p->arr);if (p->top + 1 > 0){p->top--;}}
//获取栈顶元素
int StackTop(pst p)
{assert(p->arr);return p->arr[p->top];
}
//判空
bool StackEmpty(pst p)
{assert(p->arr);return p->top == -1;
}
//销毁
void StackDestory(pst p)
{free(p->arr);p->arr = NULL;
}

三、选择排序:

  选择排序:

排序思想(以排升序为例):

  1. 假设第一个元素最小,遍历整个序列,找出未排序序列里最小的数,将其和未排序的首元素交换。
  2. 此时确立了一个数的最终位置,缩小未排序的序列范围,在到这个范围里遍历找出次小的数放到序列的首位置。
  3. 就这样不断地找出最值并放到正确的位置,不断地缩小范围,直到只剩下一个数,则序列变为有序了。

不难看出,之所以叫选择排序,就是因为每一趟遍历都可以选出当前遍历范围内的最值。

代码实现:

//选择排序
void SelectSort(int* arr, int size)
{for (int i = 0; i < size - 1; i++) //最后一个数不用排,自然地有序{int small_index = i;  //假设遍历范围内的第一个数是最小的for (int j = i+1; j < size; j++){if (arr[small_index] > arr[j]){small_index = j;}}int tmp = arr[small_index];arr[small_index] = arr[i];arr[i] = tmp;}
}

堆排序(以排升序为例):

排序思想:

  1. 排序前先通过至下而上的向下调整算法让数组具有大堆的性质。
  2. 交换堆顶元素到当前排序范围的尾部。
  3. 缩小排序范围。
  4. 通过向下调整保持大堆的属性,使得次大的数来到堆顶。
  5. 重复2~4步,直到排序范围缩小到一个元素,则排序完成。
  • 第一步的至下而上指的是从最后一个元素的父节点开始(如果数组大小为size,那最后一个元素的父节点的下标就是(size-1-1)/2)进行向下调整算法,就这样以此再对倒数第2个、3个...直到倒数第一个父节点(也就是堆顶元素)进行同样的向下调整。
  • 之所以需要至下往上的调整,是因为向下调整算法每次都只能确定相邻两层的元素的大小,倘若在某一次的调整过程中父节点的值大于左右孩子,那循环会提前结束(如果是要建立大堆的话),而这并不能保证这个父节点的值是否大于往下更深层次的节点的值。----所以向下调整建立大堆的关键在于保证父节点的左右节点都是大堆的根节点。
  • 每次都能将当前排序范围内的最值交换到末尾,也就是每次都能选出一个最大的数,因此堆排序属于选择排序是有道理的。

代码实现的细节:

  •  父节点有两个子节点,而想要有条不紊的找出较大的节点来和父节点比较。可以使用假设法:先假设左孩子节点的值更大,如果右孩子节点的值大于左孩子的,那更新最大的需要比较的子节点。(当然了,如果真的要更新为右孩子,也得先加上前置的判断条件来防止越界访问)
  • 在向下调整的代码逻辑里,在确立了较大的子节点后,不能直接无脑和父节点的值交换,还得大于父节点的值才可以交换,如果小于或等于父节点的值,则直接break退出循环不在进行调整。

代码实现(排升序):

//向下调整(大堆逻辑)
void AdjustDown(int* arr,int parent, int size)
{assert(arr);if (size <= 1) return;while (parent*2+1 < size){int child = parent * 2 + 1; //假设左孩子更大if (child+1<size && arr[child] < arr[parent * 2 + 2]){child++;}//交换父子节点的值 (只有当父节点的值大于子节点时才交换,否则结束循环)if (arr[parent] < arr[child]){int tmp = arr[parent];arr[parent] = arr[child];arr[child] = tmp;}else{break;}//继续往下调整parent = child;}
}//堆排序(升序)
void HeapSort(int* arr, int size)
{assert(arr);if (size <= 1) return;//建大堆for (int parent = (size - 2) / 2; parent >= 0; parent--){AdjustDown(arr, parent, size);}//排序,每次取出堆顶元素,放到末尾,缩小排序范围,如此往复。while (size>1){//首尾交换int tmp = arr[0];arr[0] = arr[size - 1];arr[size - 1] = tmp;//缩小排序范围size--;//用向下调整保持大堆特性AdjustDown(arr, 0, size);}
}

四、归并排序(以排升序为例):

排序思想(递归):

  1. 将一组序列划分为两组有序序列,利用双指针算法和一个额外的数组空间使得这组序列变得有序。
  2. 大多数情况下一组序列分为两组后不会直接就是两组有序序列,除非这两组序列分别都只有一个元素甚至是其中一组序列没有元素,所以通过递归不断地划分区间直到出现这种天然有序的小区间序列。
  3. 体现在代码上就是先递:不停的让一个区间划分为两个区间;再归:当两个区间都只剩下一个元素时,开始排序,然后不停的回到上一层函数接着排序。

代码实现的细节(递归):

  1. MergeSort对外提供尽量简单的函数接口,只需要接受待排序的数组和数组元素个数两个参数即可;里面套一层_MergeSort函数,传递我们自己申请的中间数组和区间,在这个函数内部实现递归的逻辑。
  2. 分隔区间时,mid应该成为左区间的右端点,右区间的右断点应该是mid+1,不这样分割的话会出现意想不到的错误。
  3. 分隔区间并不总是能两等分,也要考虑左右区间不同大小所能导致的特殊情况,分为三种情况。

      4.需要注意的是:每次对划分出来的两组区间排序的过程中,由于要确保每个数最后都被插入到中间额外的数组中,在写循环的终止条件时记得加上等号,否则每次都会漏掉两个元素。

代码实现(递归):

//归并排序(升序)
void MergeSort(int* arr, int size)
{assert(arr);int* tmp = (int*)malloc(sizeof(int) * size);if (tmp == NULL){perror("malloc fail");exit(1);}_MergeSort(arr,tmp,0, size - 1);free(tmp);tmp = NULL;
}void _MergeSort(int* arr,int* tmp, int begin, int end)
{//递归的终止条件if (begin >= end) return;//确定中间值和分好左右区间int mid = begin + (end - begin) / 2;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;//处理两组区间数据个数不同的情况if (end1 > end) return;if (end2 > end) end2 = end;//递归划分区间_MergeSort(arr, tmp, begin1, end1);_MergeSort(arr, tmp, begin2, end2);//对有序的两组元素排序int i = begin1;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2])tmp[i++] = arr[begin1++];elsetmp[i++] = arr[begin2++];}//begin1提前结束while (begin2 <= end2)tmp[i++] = arr[begin2++];//begin2提前结束while (begin1 <= end1)tmp[i++] = arr[begin1++];//将tmp里的值拷贝回原数组memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin + 1));
}

排序思想(非递归):

  1.开始排序的时机:要想实现非递归的归并排序,首先要弄清楚它在递归时是如何进行排序的,下面用一个规律一些的序列(每次都可以分成相同个元素的两组)来展示大致的递归逻辑:通过下图不难看出,在递的过程中并没有涉及到排序,而是在数组一分为二到每组只有一个元素的时候,才开始归,归的过程才是真正排序的过程。

  2.排序的规律:仔细观察下图中红色分割线及以下的部分,先是每组一个数,相邻两组排序,接着是每组两个数排,再然后是四个数排......在排序过程中每组的元素的个数以2的次方递增(2^0、 2^1、 2^2 ...)。所以只要使用循环控制好每次每组的元素个数,就可以很好的模拟出归并排序递归中的过程。

  

代码实现的细节(非递归):

  1. 外层for循环决定的是一组的元素个数,从1开始依次按照2的次方增加。
  2. 内层for循环是在每组元素个数一定的情况下,将数组中的元素全部两组两组的排序。其中begin代表了每次排序的起始位置。
  3. 在分割区间时要注意:gap是元素个数,因此区间的左边界下标加上gap后要减一才能得到右边界下标。(就和在size个元素的数组中,最后一个元素下标为size-1一样)

代码实现(非递归):

//归并排序(非递归)
void MergeSortUR(int* arr, int size)
{int* tmp = (int*)malloc(sizeof(int) * size);if (tmp == NULL){perror("malloc faild");exit(1);}for (int gap = 1; gap < size; gap *= 2) //每组元素的个数{for (int begin = 0;begin<size-gap;begin+=2*gap) //对每两组元素都执行排序逻辑{int begin1 = begin, end1 = begin1 + gap - 1;int begin2 = end1+1, end2 = begin2 + gap - 1;//处理两组区间数据个数不同的情况if (end1 > size-1)break;if (end2 > size - 1)end2 = size - 1;//对有序的两组元素排序int i = begin1;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2])tmp[i++] = arr[begin1++];elsetmp[i++] = arr[begin2++];}//begin1提前结束while (begin2 <= end2)tmp[i++] = arr[begin2++];//begin2提前结束while (begin1 <= end1)tmp[i++] = arr[begin1++];//将tmp里的值拷贝回原数组memcpy(arr + begin, tmp + begin, sizeof(int) * (end2-begin+1));}}free(tmp);tmp = NULL;
}

其他排序:

  计数排序(以排升序为例):

排序思想:

  1. 遍历一遍原数组,找出最大和最小的值,由(最大值-最小值+1)算出在数字连续的情况下的数据个数
  2. 定义一个数组,数组的大小取决于第一步里算的数据个数,通过遍历一遍原数组将每个数出现的次数记录在这个数组里。
  3. 此时额外定义的这个数组的下标就代表数据,这个数组每个位置的值就是对应下标在原数组中出现的次数。
  4. 只用遍历这个额外的数组,依次将数据插入到原数组中,根数据出现的次数就决定了一个数要插入几次,次数为0的数直接跳过即可。

文字的描述干巴巴的,下面话图来解释:

  理想很丰满,现实很骨感,实际情况里不见得要排序的数都这么小、这么的接近数组的0下标,如果那时还是让待排序的数和数组下标来对应的话,额外开辟的数组可就真的要浪费大把的空间。

 因此在遍历时让原数组中的值都减去待排序序列里最小值,从而就能正常的和数组从0开始的下标建立对应关系,之后在按照记录的每个元素出现的次数插入回原数组时再加上这个最小值,得到原来的数,就可以了。

代码实现:

//计数排序
void CountSort(int* arr, int size)
{//找出最大和最小值int small = arr[0], big = arr[1];if (arr[1] < arr[0]) small = arr[1], big = arr[0];for (int i = 2; i < size; i++){if (arr[i] > big) big = arr[i];if (arr[i] < small) small = arr[i];}//开辟额外的数组,并将所有元素初始化为0(calloc函数正好可以做到这一点)int* tmp = (int*)calloc(big-small+1, sizeof(int)); //假设待排序的数据包括了small到big,if (tmp == NULL){perror("calloc fail");exit(1);}//遍历数组,记录每个数据出现的次数for (int i = 0; i < size; i++){tmp[arr[i]-small]++;  //待排序数组里不见得最小的数是0,所以遍历时减去small得到每个数相对于0的偏移量}//根据tmp数组里保存的每个数的出现次数,覆盖到原数组int j = 0;//for (int i = 0; i < size; i++) //错for (int i = 0; i < big-small+1; i++){while (tmp[i]--) {arr[j++] = i+small;  //由于tmp里存的是每个数相对于0的偏移量,所以加上small才能得到原来的数字}}free(tmp);tmp = NULL;
}

未完待续......

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

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

相关文章

uniapp 微信小程序 功能入口

单行单独展示 效果图 html <view class"shopchoose flex jsb ac" click"routerTo(要跳转的页面)"><view class"flex ac"><image src"/static/dyd.png" mode"aspectFit" class"shopchooseimg"&g…

苍穹外卖-day05redis 缓存的学习

苍穹外卖-day05 课程内容 Redis入门Redis数据类型Redis常用命令在Java中操作Redis店铺营业状态设置 学习目标 了解Redis的作用和安装过程 掌握Redis常用的数据类型 掌握Redis常用命令的使用 能够使用Spring Data Redis相关API操作Redis 能够开发店铺营业状态功能代码 功能实…

Linux之系统管理

一、相关命令 筛选 grep&#xff0c;可以用来进行筛选&#xff0c;例如对目录筛选课写成 # 过滤出带serv的 ls /usr/sbin | grep serv2. 对服务的操作 2.1 centos6版本 service 服务名 start|stop|restart|status # start&#xff1a;开启 # stop&#xff1a;停止 # restart…

什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap

在刚刚过去的 FlutterInProduction 活动里&#xff0c;Flutter 官方除了介绍「历史进程」和「用户案例」之外&#xff0c;也着重提及了未来相关的 roadmap &#xff0c;其中就有 3.27 里的 Swift Package Manager 、 Widget 实时预览 和 Dart 与 native 平台原生语言直接互操作…

Unity录屏插件-使用Recorder录制视频

目录 1.Recorder的下载 2.Recorder面板 2.1常规录制属性 2.2录制器配置 2.2.1添加录制器 2.2.2配置Input属性 2.2.3配置 Output Format 属性 2.2.4配置 Output File 属性 3.Recorder的使用 3.1录制Game View视频 3.1.1Recorder配置与场景搭建 3.1.2开始录制 3.1.3…

Android Vendor Overlay机制

背景介绍&#xff1a; 看Android 15版本更新时&#xff0c;"Android 15 deprecates vendor overlay"。 猜想这个vendor overlay是之前用过的settings overlay&#xff0c; 不过具体是怎么回事呢&#xff1f; 目录 Vendor Overlay介绍 Vendor Overlay工作原理 Ven…

Python 绘图魔法:用turtle库开启你的编程艺术之旅

&#x1f3e0;大家好&#xff0c;我是Yui_&#xff0c;目标成为全栈工程师~&#x1f4ac; &#x1f351;如果文章知识点有错误的地方&#xff0c;请指正&#xff01;和大家一起学习&#xff0c;一起进步&#x1f440; &#x1f680;如有不懂&#xff0c;可以随时向我提问&#…

AI开发:使用支持向量机(SVM)进行文本情感分析训练 - Python

支持向量机是AI开发中最常见的一种算法。之前我们已经一起初步了解了它的概念和应用&#xff0c;今天我们用它来进行一次文本情感分析训练。 一、概念温习 支持向量机&#xff08;SVM&#xff09;是一种监督学习算法&#xff0c;广泛用于分类和回归问题。 它的核心思想是通过…

.net core在linux导出excel,System.Drawing.Common is not supported on this platform

使用框架 .NET7 导出组件 Aspose.Cells for .NET 5.3.1 asp.net core mvc 如果使用Aspose.Cells导出excel时&#xff0c;报错 &#xff1a; System.Drawing.Common is not supported on this platform 平台特定实现&#xff1a; 对于Windows平台&#xff0c;System.Drawing.C…

【Unity3D】实现可视化链式结构数据(节点数据)

关键词&#xff1a;UnityEditor、可视化节点编辑、Unity编辑器自定义窗口工具 使用Newtonsoft.Json、UnityEditor相关接口实现 主要代码&#xff1a; Handles.DrawBezier(起点&#xff0c;终点&#xff0c;起点切线向量&#xff0c;终点切线向量&#xff0c;颜色&#xff0c;n…

6UCPCI板卡设计方案:8-基于双TMS320C6678 + XC7K420T的6U CPCI Express高速数据处理平台

基于双TMS320C6678 XC7K420T的6U CPCI Express高速数据处理平台 1、板卡概述 板卡由我公司自主研发&#xff0c;基于6UCPCI架构&#xff0c;处理板包含双片TI DSP TMS320C6678芯片&#xff1b;一片Xilinx公司FPGA XC7K420T-1FFG1156 芯片&#xff1b;六个千兆网口&#xff…

Python + 深度学习从 0 到 1(01 / 99)

希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持&#xff01; ⭐ 深度学习之前&#xff1a;机器学习简史 什么要了解…

丹摩|丹摩助力selenium实现大麦网抢票

丹摩&#xff5c;丹摩助力selenium实现大麦网抢票 声明&#xff1a;非广告&#xff0c;为用户体验 1.引言 在人工智能飞速发展的今天&#xff0c;丹摩智算平台&#xff08;DAMODEL&#xff09;以其卓越的AI算力服务脱颖而出&#xff0c;为开发者提供了一个简化AI开发流程的强…

企业内训|高智能数据构建、Agent研发及AI测评技术内训-吉林省某汽车厂商

吉林省某汽车厂商为提升员工在AI大模型技术方面的知识和实践能力&#xff0c;举办本次为期8天的综合培训课程。本课程分为两大部分&#xff1a;面向全体团队成员的AI大模型技术结构与行业应用&#xff0c;以及针对技术团队的高智能数据构建与Agent研发。课程内容涵盖非结构化数…

LLaMA-Factory 单卡3080*2 deepspeed zero3 微调Qwen2.5-7B-Instruct

环境安装 git clone https://gitcode.com/gh_mirrors/ll/LLaMA-Factory.gitcd LLaMA-Factorypip install -e ".[torch,metrics]"pip install deepspeed 下载模型 pip install modelscope modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /roo…

uniapp blob格式转换为video .mp4文件使用ffmpeg工具

前言 介绍一下这三种对象使用场景 您前端一旦涉及到文件或图片上传Q到服务器&#xff0c;就势必离不了 Blob/File /base64 三种主流的类型它们之间 互转 也成了常态 Blob - FileBlob -Base64Base64 - BlobFile-Base64Base64 _ File uniapp 上传文件 现在已获取到了blob格式的…

【Rabbitmq篇】RabbitMQ⾼级特性----持久性,发送⽅确认,重试机制

目录 一.持久化 1 .交换机持久化 2 队列持久化 3.消息持久化 测试场景 二.发送⽅确认 1 .confirm确认模式 2 return退回模式 如何保证RabbitMQ消息的可靠传输&#xff1f;&#xff08;面试重点&#xff09; 三. 重试机制 一.持久化 我们在前⾯讲了消费端处理消息时,…

深度学习之目标检测——RCNN

Selective Search 背景:事先不知道需要检测哪个类别,且候选目标存在层级关系与尺度关系 常规解决方法&#xff1a;穷举法&#xff0c;在原始图片上进行不同尺度不同大小的滑窗&#xff0c;获取每个可能的位置 弊端&#xff1a;计算量大&#xff0c;且尺度不能兼顾 Selective …

Flutter环境搭建

1.Flutter 简介 1.1 Flutter 是什么 &#xff1f; Flutter 是一个 UI SDK&#xff08;Software Development Kit&#xff09;跨平台解决方案&#xff1a;可以实现一套代码发布移动端&#xff08;iOS、Android、HarmonyOS&#xff09;、Web端、桌面端目前很多公司都在用它&…

安全算法基础(一)

安全算法是算法的分支之一&#xff0c;还的依靠大量的数学基础进行计算&#xff0c;本文参照兜哥的AI安全样本对抗&#xff0c;做一个简单的算法安全概括&#xff0c;从零学习。 最新的安全算法对于我们常规的攻击样本检测&#xff0c;效果是不理想的&#xff0c;为了探究其原…