《八大排序算法》

相关概念

  • 排序:使一串记录,按照其中某个或某些关键字的大小,递增或递减的排列起来。
  • 稳定性:它描述了在排序过程中,相等元素的相对顺序是否保持不变。假设在待排序的序列中,有两个元素a和b,它们的值相等,并且在排序前a在b前面。如果在排序后,a仍然在b前面,那么就称该排序算法是稳定的;反之,如果排序后a跑到了b后面,那么这个排序算法就是不稳定的。
  • 内部排序:当排序的数据量非常小时,待排序的数据全部存放在计算机的内存中,并且排序操作也在内存里完成。
  • 外部排序:当待排序的数据量非常大,无法全部存入内存时,就需要借助外部存储设备(如硬盘、磁带等)来辅助完成排序。外部排序通常将数据分成多个较小的部分,先把这些部分依次读入内存进行内部排序,生成有序的子文件,再把这些有序子文件合并成一个最终的有序文件。

任何排序都可以分为单趟排序和多趟排序。将排序拆解开来,更方便理解。

插入排序

直接插入排序

直接插入排序的思想是:将插入的元素按大小逐一插入到已经排序好的有序序列中,直到所有的元素都插入完成,得到一个新的有序序列。

// 打印
void PrintArr(int* a, int n)
{assert(a);for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}

单趟排序的思路:假设一个序列已经排序好了,现在要插入一个元素,怎么插入呢?看下面的过程。

// 单趟
// 要比较的最后一个元素的下标
int end;
// 要插入的新元素,先保存起来
int temp = a[end+1];
while(end >= 0)
{// 如果比要比较的元素小,则将这个元素后移if(tmp < a[end]){a[end+1] = a[end];// 继续跟前一个元素比较end--;}else{break;}
}
// 走到这,1.tmp比要比较的元素大 2.比较完了,tmp比第一个元素还小,end为-1
a[end+1] = tmp;

多趟排序的思路:那么,我们就可以将乱序序列的第一个元素看做是一个已经排序好的序列,将它的后一个元素插入到这个排序好的序列中,这两个元素就是新的排序好的序列,再将后一个元素插入到这个已经排序好的序列,依次类比。直到最后一个元素插入到已经排序好的序列中,这整个乱序序列就变成有序序列了。

void InsertSort(int* a, int n)
{assert(a);//多趟排序for (int i = 0; i < n - 1; i++) // 注意:i结束条件为最后一个元素的前一个元素下标{// 单趟// 要比较的最后一个元素的下标int end = i;// 要插入的新元素,先保存起来int tmp = a[end + 1];// 把end+1的元素插入到[0,end]while (end >= 0){// 如果比最后一个元素小,则将最后一个元素后移if (tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}// 走到这,1.tmp比要比较的元素大 2.比较完了,比第一个元素还小,end为-1a[end + 1] = tmp;}
}

我们来测试一下。

void TestInsertSort()
{int a[] = {2, 1, 4, 3, 6, 7, 0, 5, 10, 8, 9};PrintArr(a, sizeof(a) / sizeof(a[0]));InsertSort(a, sizeof(a) / sizeof(a[0]));PrintArr(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestInsertSort();return 0;
}

特性总结

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高。
  2. 时间复杂度:O(N^2)。
  3. 空间复杂度:O(1)。
  4. 稳定性:稳定。

希尔排序

也叫缩小增量排序。对直接插入排序的优化。直接插入排序在序列有序或接近有序的情况下效率可以达到O(N),而在逆序或接近逆序的情况下效率就是O(N^2),所以效率时好时坏。

希尔排序是如何对直接插入排序进行优化的呢?

  1. 预排序。先将数组接近有序。
  2. 直接插入排序。数组接近有序,再直接插入排序。

预排序:将间距为gap的值分为一组,分别对每个组进行插入排序。

我们先实现一组的直接插入排序。

int gap;
int end;
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;

其实,就相当于是插入排序,只不过插入排序gap为1。

多组是怎么排序的呢?一组一组排吗?不是的,多组并排,非常巧妙。

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

gap越大,前面大的数据可以越快到后面,后面小的数,可以越快到前面,但是gap越大,越不接近有序。gap=1时,就是直接插入排序。

那gap设置为几呢?希尔是这样设计的:gap=n;gap  = n / gap + 1;只要gap大于1就是预排序,gap等于1为直接插入排序。

void ShellSort(int* a, int n)
{assert(a);int gap = n;// gap大于1预排序 ==1直接插入排序while (gap > 1){//预排序:把间距为gap的值分为一组 进行插入排序。+1保证最后是直接插入排序gap = gap / 3 + 1;//多组并排for (int i = 0; i < n - gap; i++){int end = i;int tmp = end + gap;while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}// 测试用的打印PrintArr(a, n);}
}

我们来测试一下。

void TestInsertSort()
{int a[] = {2, 1, 4, 3, 6, 7, 0, 5, 10, 8, 9};PrintArr(a, sizeof(a) / sizeof(a[0]));InsertSort(a, sizeof(a) / sizeof(a[0]));PrintArr(a, sizeof(a) / sizeof(a[0]));
}void TestShellSort()
{int a[] = { 19,30,11,20,1,2,5,7,4,8,6,26,3,29 };PrintArr(a, sizeof(a) / sizeof(a[0]));ShellSort(a, sizeof(a) / sizeof(a[0]));PrintArr(a, sizeof(a) / sizeof(a[0]));
}

下面我们来测试一下直接插入排序和希尔排序的效率。

void TestOp()
{const int N = 100000;srand(time(0));int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; i++){a1[i] = rand();a2[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();printf("InsertSort time: %d milliseconds\n", end1 - begin1);printf("ShellSort time: %d milliseconds\n", end2 - begin2);free(a1);free(a2);
}int main()
{TestOp();return 0;
}

特性总结

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap>1时都是预排序,目的是让序列接近有序。当gap==1时,数组已经接近有序了,直接插入排序,可以达到优化效果。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法有很多。大概为O(N^1.25)~O(1.6 * N^1.25)。
  4. 稳定性:不稳定。

选择排序

直接选择排序

每一次都从待排序序列中找出最小元素与最大元素,放在序列的起始位置和末尾位置,直到最后所有元素排完。

void SelectSort(int* a, int n)
{assert(a);int begin = 0;int end = n-1;while (begin < end){int mini = begin; // 最小元素的下标int maxi = begin; // 最小元素的下标for (int i = begin + 1; i <= end; i++){//在[begin,end]找最小值和最大值的下标if (a[i] < a[mini]){mini = i;}if (a[i] > a[maxi]){maxi = i;}}Swap(&a[begin], &a[mini]);// 如果begin和maxi的位置重叠,最大值已经被换走了,所以maxi的值需要修正if (begin == maxi){maxi = mini;}Swap(&a[end], &a[maxi]);begin++;end--;}
}

来测试一下。

void TestSelectSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5 };PrintArr(a, sizeof(a) / sizeof(a[0]));SelectSort(a, sizeof(a) / sizeof(a[0]));PrintArr(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestSelectSort();return 0;
}

特性总结

  1. 效率不高。
  2. 时间复杂度:O(N^2)。
  3. 空间复杂度:O(1)。
  4. 稳定性:不稳定。

堆排序

利用堆来进行选择排序,排升序,建大堆;排降序,建小堆。关于堆排序可以看下面这篇文章。

《二叉树:二叉树的顺序结构->堆》

// 大堆向下调整算法 
// 注意:调整的树的左右子树必须为大堆
void AdjustDown(int* a, int n, int root)
{// 父亲int parent = root;// 1.选出左右孩子较大的孩子跟父亲比较// 默认较大的孩子为左孩子int child = parent * 2 + 1;// 终止条件孩子到叶子结点最后跟父亲比一次while (child < n){// 2.如果右孩子大于左孩子,则较大的孩子为右孩子 if (child + 1 < n && a[child + 1] > a[child]){child++;}// 3.如果孩子大于父亲,则跟父亲交换if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{assert(a);//建堆 排升序 建大堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}int end = n - 1;while (end >= 1){// 交换堆顶与最后一个元素Swap(&a[0], &a[end]);// 向下调整AdjustDown(a, end, 0);end--;}
}

来测试一下。

void TestHeapSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5 };PrintArr(a, sizeof(a) / sizeof(a[0]));HeapSort(a, sizeof(a) / sizeof(a[0]));PrintArr(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestHeapSort();return 0;
}

特性总结

  1. 使用堆来选数,效率高。
  2. 时间复杂度:O(NlogN)。
  3. 空间复杂度:O(1)。
  4. 稳定性:不稳定。

交换排序

根据序列中两个键值的比较结果来对换这两个记录在序列中的位置。特点是:键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序

void BubbleSort(int* a, int n)
{assert(a);int i = 0;// 整体for (int j = 0; j < n; j++){// 单趟// 判断是否有序,如果有序,不用比较了,直接退出int flag = 0;for (i = 0; i < n-1-j; i++){if (a[i] > a[i + 1]){// 交换Swap(&a[i], &a[i + 1]);flag = 1;}}if (flag == 0){break;}}
}

来测试一下。

void TestBubbleSort()
{//int a[] = { 0,5,7,6,8,9,3,2,4,1 };//int a[] = { 0,9,1,2,3,4,5,6,7,8 };int a[] = { 9,8,7,6,5,4,3,2,1,0 };PrintArr(a, sizeof(a) / sizeof(a[0]));BubbleSort(a, sizeof(a) / sizeof(a[0]));PrintArr(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestSelectSort();return 0;
}

特性总结

  1. 非常容易理解。
  2. 时间复杂度:O(N^2)。
  3. 空间复杂度:O(1)。
  4. 稳定性:稳定。

快速排序

是一种二叉树结构的交换排序方法。取待排序元素序列中的某元素key作为基准值,按照该key,将排序集合分为两个子序列,左子序列中所有元素都小于key,右子序列所有元素都大于key。然后左子序列和右子序列分别重复该过程,直到所有的元素都到相应的位置上。

通常取key为最右边的值,或最左边的值。

左右指针法

这样,比key小的值,都换到前面了,比key大的值都换到后面了,而key在正确位置。

来看单趟排序是怎么排的。

// 单趟
int PartSort(int* a, int begin, int end)
{// key下标// 选最后一个值作为keyint keyIndex = end;while (begin < end){// 让左边先走// 左边找比key大的while (begin < end && a[begin] <= a[keyIndex]) // 注意:为>=,不然会造成死循环,例如 3 5 4 7 1 2 8 5 7 5{begin++;}// 右边再走// 右边找比key小的while (begin < end && a[end] >= a[keyIndex]) // 注意:为>=,不然会造成死循环,例如 3 5 4 7 1 2 8 5 7 5{end--;}// 交换// 左边的大值就交换到后边了,右边的小值就交换到前面了Swap(&a[begin], &a[end]);}// 走到这,代表相遇了,相遇的位置的值和key交换// 交换完之后,不管左边和右边是否是有序的,总之,key的位置到了正确的位置// 如果选最后一个值作为key,让左边先走可以保证相遇的位置的值是比key大的Swap(&a[begin], &a[keyIndex]);// 返回相遇位置return begin;
}

多趟排序,将左子序列和右子序列递归重复该过程即可完成整个排序。

// 多趟
void QuickSort(int* a, int left, int right)
{// 递归结束条件if (left >= right)return;// 此时相遇位置的值已经是正确的位置int div = PartSort(a, left, right);PrintArr(a + left, right - left + 1);// 用于打印测试的printf("[%d, %d]%d[%d, %d]\n", left, div - 1, div, div + 1, right);// 递归的思想,让该位置的左边和右边按照同样的思想进行排序QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}

 来测试一下。

void TestQuickSort()
{int a[] = { 6,1,2,7,9,3,4,8,10,5 };// 如果序列本来就有序// int a[] = { 1,2,3,4,5,6,7,8,9};PrintArr(a, sizeof(a) / sizeof(a[0]));QuickSort(a, 0,sizeof(a) / sizeof(a[0])-1);PrintArr(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestQuickSort();return 0;
}

可以看到递归的整个过程。 

再来测试一下。

void TestQuickSort()
{// int a[] = { 6,1,2,7,9,3,4,8,10,5 };// 如果序列本来就有序int a[] = { 1,2,3,4,5,6,7,8,9};PrintArr(a, sizeof(a) / sizeof(a[0]));QuickSort(a, 0,sizeof(a) / sizeof(a[0])-1);PrintArr(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestQuickSort();return 0;
}

快速排序的最好情况:如果每次递归取key值都能取到中位数,排序的效率是最高的,因为每次都能平均将左子序列和右子序列划分开。时间复杂度为O(NlogN)。

快速排序的最坏情况:每次递归取key值都是最大值或最小值,排序的效率是最差的,也就是序列在有序或接近有序的情况下,每次划分都会产生一个空的子序列和一个长度仅比原序列少 1 的子序列。时间复杂度为O(N^2)。

那么,实际中,我们无法保证取到的key是中位数,但是,是不是可以考虑不要取到最大的或最小的。

三位数取中

三位数取中,能避免取到最大值和最小值。如果在最坏的情况有序的情况下,取序列中间的元素作为key值,与最后的元素交换,还是最右边的值作为key值,此时就变成了最好的情况。如果是在其他,也能避免取到最大值和最小值。也就是说三位数取中,让最坏的情况不再出现,时间复杂度综合为O(NlogN)。

// 三位数选中
int GetMidIndex(int* a,int begin,int end)
{int midIndex = (begin + end) / 2;if (a[begin] > a[midIndex]){// begin > mid  mid > endif (a[midIndex] > a[end]){return midIndex;}// begin > mid   mid<end  else{if (a[begin] < a[end]){return begin;}else{return midIndex;}}}// begin <midelse{if (a[midIndex] < a[end]){return midIndex;}// begin<mid  mid>endelse{if (a[begin] < a[end]){return end;}else{return begin;}}}
}// 单趟
int PartSort1(int* a, int begin, int end)
{// 三位数选中 避免最坏情况int midIndex = GetMidIndex(a, begin, end);Swap(&a[midIndex], &a[end]);// key下标// 选最后一个值作为keyint keyIndex = end;while (begin < end){// 让左边先走// 左边找比key大的while (begin < end && a[begin] <= a[keyIndex]) // 注意:为>=,不然会造成死循环,例如 3 5 4 7 1 2 8 5 7 5{begin++;}// 右边再走// 右边找比key小的while (begin < end && a[end] >= a[keyIndex]) // 注意:为>=,不然会造成死循环,例如 3 5 4 7 1 2 8 5 7 5{end--;}// 交换// 左边的大值就交换到后边了,右边的小值就交换到前面了Swap(&a[begin], &a[end]);}// 走到这,代表相遇了,相遇的位置的值和key交换// 交换完之后,不管左边和右边是否是有序的,总之,key的位置到了正确的位置// 如果选最后一个值作为key,让左边先走可以保证相遇的位置的值是比key大的Swap(&a[begin], &a[keyIndex]);// 返回相遇位置return begin;
}// 多趟
void QuickSort(int* a, int left, int right)
{// 递归结束条件if (left >= right)return;// 此时相遇位置的值已经是正确的位置int div = PartSort1(a, left, right);// PrintArr(a + left, right - left + 1);// 用于打印测试的// printf("[%d, %d]%d[%d, %d]\n", left, div - 1, div, div + 1, right);// 递归的思想,让该位置的左边和右边按照同样的思想进行排序QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}

挖坑法

取最右边(也可以左边的)的值作为key。该位置为坑,左边找比key大的元素,填入到坑里,该元素的位置成为新的坑;右边找比坑小的元素,填入到新坑里,该元素位置成为新的坑,直到相遇,将key填入到新坑。此时key的左边就是比key小的,右边就是比key大的。再将该key左边的序列和右边的序列迭代重复此操作即可。

// 单趟
int PartSort2(int* a, int begin, int end)
{assert(a);// 三位数选中 避免最坏情况int midIndex = GetMidIndex(a, begin, end);Swap(&a[midIndex], &a[end]);// 最后一个元素为坑// 坑的意思就是这的值被拿走了,可以覆盖新的值int key = a[end];while (begin < end){// begin找大的放进坑 begin成为新坑while (begin < end && a[begin] <= key){begin++;}a[end] = a[begin];// end找小的放进新坑 end成为新坑while (begin < end && a[end] >= key){end--;}a[begin] = a[end];}//把最后一个元素放到相遇位置a[begin] = key;return begin;
}
// 多趟
void QuickSort(int* a, int left, int right)
{// 递归结束条件if (left >= right)return;// 此时相遇位置的值已经是正确的位置// 左右指针法// int div = PartSort1(a, left, right);// 挖坑法int div = PartSort2(a, left, right);// PrintArr(a + left, right - left + 1);// 用于打印测试的// printf("[%d, %d]%d[%d, %d]\n", left, div - 1, div, div + 1, right);// 递归的思想,让该位置的左边和右边按照同样的思想进行排序QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}

前后指针法 

// 单趟
int PartSort3(int* a, int begin, int end)
{assert(a);//三位数选中 避免最坏情况int midIndex = GetMidIndex(a, begin, end);Swap(&a[midIndex], &a[end]);int keyIndex = end;int prev = begin - 1;int cur = begin;while (cur < end){if (a[cur] < a[keyIndex] && ++prev != a[cur]) // ++prev如果和cur相等 自己跟自己交换不用交换{//交换Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[++prev], &a[keyIndex]);return prev;
}
// 多趟
void QuickSort(int* a, int left, int right)
{// 递归结束条件if (left >= right)return;// 此时相遇位置的值已经是正确的位置// 左右指针法// int div = PartSort1(a, left, right);// 挖坑法// int div = PartSort2(a, left, right);// 前后指针法int div = PartSort3(a, left, right);// PrintArr(a + left, right - left + 1);// 用于打印测试的// printf("[%d, %d]%d[%d, %d]\n", left, div - 1, div, div + 1, right);// 递归的思想,让该位置的左边和右边按照同样的思想进行排序QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}

快速排序就像一颗二叉树一样,选出一个key分出左区间和右区间,左区间的值都比key小,右区间的值都比key大,再再左区间中找出一个key分出左区间和右区间,在右区间中找出一个key分出左区间和右区间......

快速排序还有可以优化的地方,当不断的递归划分区间,区间已经数据很少时,不用再递归方式划分区间排序了,使用插入排序去排序,减少整体的递归次数。

// 多趟
void QuickSort(int* a, int left, int right)
{// 递归结束条件if (left >= right)return;// 此时相遇位置的值已经是正确的位置// 左右指针法// int div = PartSort1(a, left, right);// 挖坑法// int div = PartSort2(a, left, right);// 前后指针法int div = PartSort3(a, left, right);// PrintArr(a + left, right - left + 1);// 用于打印测试的// printf("[%d, %d]%d[%d, %d]\n", left, div - 1, div, div + 1, right);// 区间大于十个元素用快速排序if ((right - left + 1) > 10){int div = PartSort3(a, left, right);// 递归的思想,让该位置的左边和右边按照同样的思想进行排序QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);}// 小于十个元素用插入排序else{InsertSort(a + left, right - left + 1);}
}

特性总结

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序。
  2. 时间复杂度:O(NlogN)。
  3. 空间复杂度:O(1),使用栈模拟则为O(logN)。
  4. 稳定性:不稳定。

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

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

相关文章

深度学习篇---paddleocr正则化提取

文章目录 前言一、代码总述&介绍1.1导入必要的库1.1.1cv21.1.2re1.1.3paddleocr 1.2初始化PaddleOCR1.3打开摄像头1.4使用 PaddleOCR 进行识别1.5定义正则表达式模式1.6打印提取结果1.7异常处理 二、正则表达式2.1简介2.2常用正则表达式模式及原理2.2.1. 快递单号模式2.2.2…

JavaScript DOM与元素操作

目录 DOM 树、DOM 对象、元素操作 一、DOM 树与 DOM 对象 二、获取 DOM 元素 1. 基础方法 2. 现代方法&#xff08;ES6&#xff09; 三、修改元素内容 四、修改元素常见属性 1. 标准属性 2. 通用方法 五、通过 style 修改样式 六、通过类名修改样式 1. className 属…

单元测试的编写

Python 单元测试示例 在 Python 中&#xff0c;通常使用 unittest 模块来编写单元测试。以下是一个简单的示例&#xff1a; 示例代码&#xff1a;calculator.py # calculator.py def add(a, b):return a bdef subtract(a, b):return a - b 单元测试代码&#xff1a;test_c…

大模型学习:从零到一实现一个BERT微调

目录 一、准备阶段 1.导入模块 2.指定使用的是GPU还是CPU 3.加载数据集 二、对数据添加词元和分词 1.根据BERT的预训练&#xff0c;我们要将一个句子的句头添加[CLS]句尾添加[SEP] 2.激活BERT词元分析器 3.填充句子为固定长度 代码解释&#xff1a; 三、数据处理 1.…

10组时尚复古美学自然冷色调肖像电影照片调色Lightroom预设 De La Mer – Nautical Lightroom Presets

De La Mer 预设系列包含 10 种真实的调色预设&#xff0c;适用于肖像、时尚和美术。为您的肖像摄影带来电影美学和个性&#xff01; De La Mer 预设非常适合专业人士和业余爱好者&#xff0c;可在桌面或移动设备上使用&#xff0c;为您的摄影项目提供轻松的工作流程。这套包括…

SDL多窗口多线程渲染技术解析

SDL多窗口多线程渲染技术解析 技术原理 SDL多线程模型与窗口管理 SDL通过SDL_Thread结构体实现跨平台线程管理。在多窗口场景中,每个窗口需关联独立的渲染器,且建议遵循以下原则: 窗口与渲染器绑定:每个窗口创建时生成专属渲染器(SDL_CreateRenderer),避免跨线程操作…

QT 跨平台发布指南

一、Windows 平台发布 1. 使用 windeployqt 工具 windeployqt --release --no-compiler-runtime your_app.exe 2. 需要包含的文件 应用程序 .exe 文件 Qt5Core.dll, Qt5Gui.dll, Qt5Widgets.dll 等 Qt 库 platforms/qwindows.dll 插件 styles/qwindowsvistastyle.dll (如果使…

L2-037 包装机 (分数25)(详解)

题目链接——L2-037 包装机 问题分析 这个题目就是模拟了物品在传送带和筐之间的传送过程。传送带用队列模拟&#xff0c;筐用栈模拟。 输入 3 4 4 GPLT PATA OMSA 3 2 3 0 1 2 0 2 2 0 -1输出 根据上述操作&#xff0c;输出的物品顺序是&#xff1a; MATA样例分析 初始…

机器学习的一百个概念(4)下采样

前言 本文隶属于专栏《机器学习的一百个概念》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索&…

qt6下配置qopengl

qt部件选择 Qt 6&#xff1a;需要手动选择 Qt Shader Tools 和 Qt 5 Compatibility Module&#xff08;如果需要兼容旧代码&#xff09; cmake文件 cmake_minimum_required(VERSION 3.16) # Qt6 推荐最低 CMake 3.16 project(myself VERSION 0.1 LANGUAGES CXX)set(CMAKE_A…

数据安全系列4:密码技术的应用-接口调用的身份识别

传送门 数据安全系列1&#xff1a;开篇 数据安全系列2&#xff1a;单向散列函数概念 数据安全系列3&#xff1a;密码技术概述 什么是认证&#xff1f; 一谈到认证&#xff0c;多数人的反应可能就是"用户认证" 。就是应用系统如何识别用户的身份&#xff0c;直接…

STL之map和set

1. 关联式容器 vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其里面存储的是结…

Vue3 其它API Teleport 传送门

Vue3 其它API Teleport 传送门 在定义一个模态框时&#xff0c;父组件的filter属性会影响子组件的position属性&#xff0c;导致模态框定位错误使用Teleport解决这个问题把模态框代码传送到body标签下

C++练习

1.将File练习题&#xff0c;内部的FILE*描述符&#xff0c;改成int描述符 2。写一个类Fifo管道类。提高难度&#xff0c;什么都不提示。只要求&#xff1a;使用自己编写的Fifo类对象&#xff0c;实现2个终端之间互相聊天 file.cpp #include <iostream> #include <c…

《Python Web网站部署应知应会》No4:基于Flask的调用AI大模型的高性能博客网站的设计思路和实战(上)

基于Flask的调用AI大模型的高性能博客网站的设计思路和实战&#xff08;上&#xff09; 摘要 本文详细探讨了一个基于Flask框架的高性能博客系统的设计与实现&#xff0c;该系统集成了本地AI大模型生成内容的功能。我们重点关注如何在高并发、高负载状态下保持系统的高性能和…

实现一个简易版的前端监控 SDK

【简易版的前端监控系统】 1、Promise的错误如何监控&#xff1f;–promise不是所有都是接口请求 2、接口的报错如何监控&#xff1f;–全局监控sdk&#xff0c;不改动公共的请求方法、不改动业务代码&#xff1b;一般接口使用axios请求 3、资源的报错如何监控&#xff1f; 4、…

【操作系统】软中断vs硬中断

在操作系统中&#xff0c;中断&#xff08;Interrupt&#xff09; 是 CPU 响应外部事件的重要机制&#xff0c;分为 硬中断&#xff08;Hardware Interrupt&#xff09; 和 软中断&#xff08;Software Interrupt&#xff09;。它们的核心区别在于 触发方式 和 处理机制。 1. 硬…

力扣刷题-热题100题-第27题(c++、python)

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 创建一个新链表&#xff0c;遍历list1与list2&#xff0c;将新链表指向list1与list2…

Python包下载路径 Chrome用户数据 修改到非C盘

查看 site-packages 是否能通过命令行完成&#xff1f; 可以&#xff0c;使用以下命令&#xff08;不需写脚本&#xff09;&#xff1a; python -m site输出包含&#xff1a; sys.path site-packages 路径&#xff08;全局和用户级&#xff09; 如果只想看安装路径&#…

【鸿蒙5.0】鸿蒙登录界面 web嵌入(隐私页面加载)

在鸿蒙应用中嵌入 Web 页面并加载隐私页面&#xff0c;可借助 WebView 组件来实现。以下是一个完整示例&#xff0c;展示如何在鸿蒙 ArkTS 里嵌入 Web 页面并加载隐私政策页面。 在 HarmonyOS 应用开发中&#xff0c;如果你希望嵌入一个网页&#xff0c;并且特别关注隐私页面加…