当前位置: 首页 > news >正文

一个web网站开发的整个流程/友情链接交换的意义是什么

一个web网站开发的整个流程,友情链接交换的意义是什么,网站收录后然后怎么做,东莞免费公司网站建设文章目录 插入排序直接插入排序希尔排序 选择排序选择排序堆排序升序 交换排序冒泡排序快速排序递归hoare版本挖坑法前后指针版本 非递归Hoare挖坑法前后指针 快排的优化三数取中法选key递归到小的子区间时,可以考虑使用插入排序 归并排序递归实现非递归实现 排序算…

文章目录

  • 插入排序
    • 直接插入排序
    • 希尔排序
  • 选择排序
    • 选择排序
    • 堆排序
        • 升序
  • 交换排序
    • 冒泡排序
    • 快速排序
      • 递归
        • hoare版本
        • 挖坑法
        • 前后指针版本
      • 非递归
        • Hoare
        • 挖坑法
        • 前后指针
      • 快排的优化
        • 三数取中法选key
        • 递归到小的子区间时,可以考虑使用插入排序
  • 归并排序
    • 递归实现
    • 非递归实现
  • 排序算法复杂度以及稳定性

在这里插入图片描述

插入排序

直接插入排序

直接插入排序是一种简单的插入排序法,其基本思想:是把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列
可以理解为一遍摸扑克牌,一边进行排序

在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。
按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序

  • 当插入第n个元素时,前面的n-1个数已经有序
  • 用第n个数与前面的n-1个数比较,我们将第n个数假设为tmp ,也就是说将tmp插入到[0 ,end] 的区间中
    第一种情况:如果tmp比end大就放在end后面
    第二种情况:如果tmp比end小但是比数组前面的几个数据大,插入之后,tmp之后的数据就往后挪动
    第三种情况:如果tmp比数组所有元素都小,所有数据都需要挪动,end就得挪动到数组下标为-1的位置才结束

在这里插入图片描述

在这里插入图片描述

动图演示:
在这里插入图片描述

void InsertSort(int* a , int n )
{for (int i = 1; i <n ; i++){//一趟插入排序 int tmp = a[i];int end = i-1; //end是数组下标while (end >= 0){//如果tmp比end小但是比数组前面的几个数据大,插入之后,tmp之后的数据就往后挪动if (tmp < a[end]){a[end + 1] = a[end];//往后挪动end--;}else{break;}}//如果tmp比数组所有元素都小,所有数据都需要挪动,end就得挪动到数组下标为-1的位置才结束//如果tmp比end大就放在end后面a[end + 1] = tmp;//包含上述两种情况}
}

时间复杂度:O(N^2)
空间复杂度:O(1)

希尔排序

第一步先选定个小于n的数字作为gap,将所有距离为gap的数分为一组进行预排序,预排序的目的就是使数组接近有序,与直接插入排序相同,直接插入排序的间隔为1,预排序的间隔变为gap了
再选一个小于gap的数作为新的gap,重复第一步的操作
当gap的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成

举例分析一下:

在这里插入图片描述

我们用序列长度的一半作为第一次排序时gap的值,此时相隔距离为5的元素被分为一组(共分了5组,每组有2个元素),然后分别对每一组进行直接插入排序

在这里插入图片描述

gap的值折半,此时相隔距离为2的元素被分为一组(共分了2组,每组有5个元素),然后再分别对每一组进行直接插入排序

在这里插入图片描述

gap的值再次减半,此时gap减为1,即整个序列被分为一组,进行一次直接插入排序。

为什么是选一个小于gap的数作为新的gap,也就是要gap由大变小?
gap越大,数据跳跃的幅度越大,数据挪动得越快;gap越小,数据跳跃的幅度越小,数据挪动得越慢。前期让gap较大,可以让数据更快得移动到自己对应的位置附近,减少挪动次数,提高算法效率

void ShellSort(int* a, int n) // 希尔排序 
{int gap = n;while (gap > 1){//一趟排序gap /= 2;for (int i = gap; i < n; i += gap){int end = i - gap;  //有序序列最后一个元素的下标int tmp = a[end + gap];while (end >= 0){//如果tmp比end小但是比数组前面的几个数据大,插入之后,tmp之后的数据就往后挪动if (tmp < a[end]){a[end + gap] = a[end]; //往后挪动 end -= gap;}else{break;}}//tmp比数组中所有数据都小  ,一直往后挪动 ,end挪到了-1//tmp >a[end] a[end + gap] = tmp;}}
}

选择排序

选择排序

基本思想 : 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置;直到全部待排序的数据元素排完

在元素集合array[i]到array[n-1]中选择关键码最大(小)的数据元素;

若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换;

在剩余的array[i] 到 array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

注意特殊情况 :如果Maxi在left位置,当a [ Mini ] 和 a [ left ] 互换时,此时Maxi的位置就变成了Mini, 我们添加一个判断条件 ,判断left == Maxi , 修正Maxi ,防止Maxi更改
在这里插入图片描述

以升序为例做分析:

在这里插入图片描述

经过第一躺交换,最小的元素排在了数组的第一个位置

在这里插入图片描述

经过第二趟交换 ,第二小的元素已经到了数组第二个元素位置

在这里插入图片描述

经过第三趟排序 ,第三小的元素已经排在了第三个位置

在这里插入图片描述

最后一趟排序 ,数组已经有序了

动图演示 :
在这里插入图片描述

代码实现:

void SelectSort(int* a, int n)//选择排序
{//升序 int left = 0, right = n - 1;while (left < right){//选出最大最小数的下标 int Mini = left;int Maxi = left;//一趟 for (int i = left+1 ; i <= right; i++){if (a[i] < a[Mini]){Mini = i; // 更新Mini}if (a[i] > a[Maxi]){Maxi = i; //更新Maxi }}// Mini和左边交换Swap(&a[left], &a[Mini]);// Maxi 在左边  ,Maxi 被换成Mini, 修正Maxiif (left == Maxi){Maxi = Mini;  }//Maxi与右边交换Swap(&a[right], &a[Maxi]);left++;right--;}
}

选择排序效率较低,实际中很少使用
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定

堆排序

排降序 建立小堆
排升序 建立大堆

升序

向上调整建堆,时间复杂度为O(N* longN)

使用向上调整算法建大堆,将数组建成大堆后,此时堆顶元素是最大的 ,将堆顶元素和最后一个元素进行交换,这样最大的元素就到了数组最后一个元素,对剩下的元素使用向下调整 , 当下一次向下调整时,我们不管这个处在数组最后一个位置的最大元素(有点类似堆的删除 ),此时第二大的元素来到的堆顶 ,堆顶元素继续与最后一个元素进行交换,(注意第一个交换过去的最大的元素已经不在范围内了) ,依次类推 ,升序就完成了

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

 void HeapSort(int* a, int n){//向上调整建堆for (int i = 0; i < n; i++){AdjustUp(a, i);}//向下调整排序int end = n - 1;// end 是最后一个元素的下标while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}}int main(){int a[10] = { 2, 1, 5, 7, 6, 8, 0, 9, 4, 3 };HeapSort(a, 10);return 0; }

向下调整建堆的前提是左右子树都是堆 ,从倒数第一个非叶子节点开始倒着调整,如何找到倒数第一个非叶子节点?通过最后一个节点的父节点来找到 , 那为什么要找倒数第一个非叶子节点? 因为倒数第一个非叶子节点的左右子树都满足大堆或小堆的条件

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

 void HeapSort(int* a, int n){//向下调整建堆for (int i = (n-1-1)/ 2; i >= 0; i--) // n-1是最后一个节点的下标,(n-1-1)/2 通过下标找到最后一个节点的父节点{AdjustDown(a,n , i);}//向下调整排序int end = n - 1; //end 是最后一个元素的下标 while (end >=0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}}int main(){int a[10] = { 2, 1, 5, 7, 6, 8, 0, 9, 4, 3 };HeapSort(a, 10);return 0; }

交换排序

冒泡排序

以升序为例

冒泡排序,该排序的命名非常形象,即一个个将气泡冒出。冒泡排序一趟冒出一个最大(或最小)值
如果前一位的数字大于后一位的,那么这两个数字交换位置,因此,最大的数字在第一轮循环中不断像一个气泡一样向上冒

在这里插入图片描述

动图演示:
在这里插入图片描述
代码实现 :

void BubbleSort(int* a, int n)
{for (int i = 0; i < n-1; i++){//一趟 for (int j = 0; j < n-1 - i; j++){assert(j < n-1);if (a[j] > a[j + 1]){assert(j + 1 < n);Swap(&a[j], &a[j + 1]);}}}
}

冒泡排序:
时间复杂度 :O(N^2)

快速排序

递归

hoare版本

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

单趟排序 : 一趟下来的结果是让Key左边的数据全部都小于Key,Key右边的数据全部都大于Key, 就相当于Key这个基准值被排好序了,快速排序的单趟排序本质上就是在排一个数的顺序

步骤:

1 选最左边的或者最右边的当作基准值Key
2 定义一个Left和一个Right,Right从右向左走, 若Right遇到小于Keyi的数,则停下,Left从左向右开始走,直到Left遇到一个大于Keyi的数时,将Left和Right的内容交换 ,Right再次开始走,如此进行下去,直到Left和Right最终相遇,此时将相遇点的内容与key交换即可
需要注意的是:若选择最左边的数据作为基准值Key,则需要Right先走;若选择最右边的基准值数据作为基准值Key,则需要Left先走

注意:Left 和Right相遇位置一定比Key小

3 然后我们再将Key的左序列和右序列再次进行这种单趟排序,如此反复操作下去 ,其实就是一个递归

40a86189240a9f74809.png)

如果选择最左边的数据作为基准值Key ,为什么不能Left先走呢?

在这里插入图片描述

在这里插入图片描述

快排hoare版本 单趟动图演示:
在这里插入图片描述
代码实现:

void QuickSort(int* a, int left , int right ) // Hoare 版本 
{int begin = left;int end = right;//升序 // 从左向右走 , 从右向左走 int Keyi = left; // 最左边为基准值 if (left >= right)return;   while (left <right ){//单趟排序 //右边找小 while (right > left && a[right] >= a[Keyi]){right--;assert(right > 0);}//左边找大while (left < right && a[left] <= a[Keyi]){left++;assert(left <= right);}Swap(&a[left], &a[right]);}Swap(&a[Keyi], &a[left]); //Left和Right最终相遇,此时将相遇点的内容与key交换即可//递归 类似二叉树的前序遍历 // [begin , Keyi-1 ]  Keyi  [Keyi+1 , end] Keyi = left;assert(Keyi - 1 >= 0);QuickSort( a,  begin, Keyi -1);QuickSort(a, Keyi+1, end );}

快排时间复杂度 :
如果每趟排序所选的Key都正好是该序列的中间值,即单趟排序结束后Key位于序列正中间,那么快速排序的时间复杂度就是O(NlogN)

挖坑法

以升序为例

单趟排序:一趟下来的结果是让Key左边的数据全部都小于Key,Key右边的数据全部都大于Key

步骤(在最左边挖坑为例) :

1 选出最左边或者最右边的一个数 ,存放在Key变量中,并且在该数据位置形成一个坑

2 定义一个Left和一个Right,Left从左向右走,找比Key大的数 ,Right从右向左走,找比Key小的数。(注意: 如果是在最左边挖坑,则需要Right先走;如果是在最右边挖坑,则需要Left先走

3 因为是以最左边挖坑为例 ,所以是Right 先走 , 在Left和Right 遍历的过程中,如果Right遇到小于Key的数,则将该数放到最左边的坑位,并在Right当前位置形成新的坑位,这时Left再向右边走,若遇到大于Key的数,则将其抛入到刚才在Right位置形成的一个坑位,然后在Left 当前位置形成一个坑位,如此循环下去,直到最终Left和Right相遇,而且Left和Right相遇一定相遇在坑位处 ,这时将Key填在坑位即可。

Key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作

单趟动图演示:
在这里插入图片描述
代码实现:

//快排挖坑法
void QuickSort(int* a, int left, int right)
{int begin = left;int end = right;if (left >= right)return;   // 升序	,最左边挖坑 ,右边先走//选最左边为 Key,并形成hole int hole=left;int Key = a[left];while (left < right){//单趟排序 //right 找小 while (left < right && a[right] > Key){right--;}//找到比Key小的数 ,放进最左边的坑位 ,并在right当前位置形成新的坑位a[hole] = a[right];hole = right;assert(hole >= 0);//left 找 大  while (right > left && a[left] < Key){left++;}// 找到比Key大的数 ,放进right当时形成的坑位 ,并在Left当前位置形成新的坑位 a[hole] = a[left];hole = left;assert(hole >= 0);}//直到最终Left和Right相遇,这时将Key填在坑位即可。assert(hole >= 0);a[hole] = Key;//递归 //递归 类似二叉树的前序遍历 // [begin , Key-1 ]  Key  [Key+1 , end] Key = left;assert(Key - 1 >= 0);QuickSort( a,  begin, Key -1);QuickSort(a, Key+1, end );}

前后指针版本

以升序为例

单趟排序:一趟下来的结果是让Key左边的数据全部都小于Key,Key右边的数据全部都大于Key

步骤(以最左边为Key 为例) :

1 选最左边的或者最右边的当作基准值Key
2 定义一个prev 和 cur ,prev指针指向序列开头 ,prev 从左往右遍历 ,cur指针指向prev+1, cur从左往右遍历 。
3、若cur指向的内容小于Key,则prev先++,然后交换prev和cur的值,然后cur++;若cur的值大于key,则cur指针直接++。如此进行下去,直到cur指针越界,此时将Key和prev指针指向的内容交换即可。

然后我们再将Key的左序列和右序列再次进行这种单趟排序,如此反复操作下去 ,其实就是一个递归

在这里插入图片描述
单趟动图演示:
在这里插入图片描述

代码实现:

int  PartSort2(int* a, int left, int right)  //前后指针法
{int begin = left;int end = right;//升序 // 单趟排序int prev = left;int cur = prev + 1;int Keyi = left; //选最左边为Key //cur 找比Key小的 while (cur <= right)  //cur未越界就继续 {//如果cur找到比Key小 ,往后++if (a[cur] < a[Keyi] && ++prev != cur) //++prev !=cur , 有可能数组前几个元素比Key小,但是没必要交换{Swap(&a[cur], &a[prev]); //交换 }cur++;  }assert(cur <= right+1); Swap(&a[prev], &a[Keyi]);/*递归 类似二叉树的前序遍历 [begin , Keyi-1 ]  Keyi  [Keyi+1 , end] */Keyi = prev;assert(Keyi - 1 >= 0);return prev;
}
void QuickSort(int* a, int left, int right)
{//递归 if (left >= right)return;int keyi = PartSort2(a, left, right);QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);
}

非递归

将一个用递归实现的算法改为非递归时,一般需要借用一个数据结构,那就是栈。将Hoare版本、挖坑法以及前后指针法的快速排序改为非递归版本
Hoare版本、挖坑法和前后指针法 ,这三种排序都只写单趟 ,后面再写一个非递归的快速排序,在函数内部调用单趟排序的函数

Hoare

单趟排序代码实现:

//Hoare版本(单趟排序)
int PartSort1(int* a, int left, int right)
{int keyi = left;//key的下标while (left < right){//right走,找小while (left < right&&a[right] >= a[keyi]){right--;}//left先走,找大while (left < right&&a[left] <= a[keyi]){left++;}if (left < right){Swap(&a[left], &a[right]);//交换left和right的值}}int meeti = left;//L和R的相遇点Swap(&a[keyi], &a[meeti]);//交换key和相遇点的值return meeti;//返回相遇点,即key的当前位置
}

挖坑法

单趟排序代码实现:

//挖坑法(单趟排序)
int PartSort2(int* a, int left, int right)
{int key = a[left];//在最左边形成一个坑位while (left < right){//right向左,找小while (left < right&&a[right] >= key){right--;}//填坑a[left] = a[right];//left向右,找大while (left < right&&a[left] <= key){left++;}//填坑a[right] = a[left];}int meeti = left;//L和R的相遇点a[meeti] = key;//将key抛入坑位return meeti;//返回key的当前位置
}

前后指针

单趟排序代码实现:

//前后指针法(单趟排序)
int PartSort3(int* a, int left, int right)
{int prev = left;int cur = left + 1;int keyi = left;while (cur <= right)//当cur未越界时继续{if (a[cur] < a[keyi] && ++prev != cur)//cur指向的内容小于key{Swap(&a[prev], &a[cur]);}cur++;}int meeti = prev;//cur越界时,prev的位置Swap(&a[keyi], &a[meeti]);//交换key和prev指针指向的内容return meeti;//返回key的当前位置
}

快排的优化

三数取中法选key

如果这个序列是非常无序,快速排序的效率是非常高的 ,一旦序列有序 ,每次选取最左边或是最右边的数作为基准值Key,时间复杂度就会从O(N*logN)变为O(N^2),这样快排的效率极低

在这里插入图片描述

也就是说影响快排时间复杂度就是基准值Key的选取,如果选取的Key离中间位置越近,则效率越高

为了解决这个问题 ,也就出现了三数取中 :
三数取中,当中的三个数指的是:最左边的数、最右边的数以及中间位置的数
三数取中就是取这三个数当中,值的大小居中的那个数作为该趟排序的Key ,因为它离中间位置最近。这就确保了我们所选取的数不会是序列中的最左边的数、最右边的数

在这里插入图片描述
代码实现:

//三数取中  ,得到中间元素的下标
int GetMiddleIndexi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[right] > a[mid]){return mid;// a[right] > a[mid] > a[left]}// a[mid]>=a[right] ,此时a[mid]是最大的 ,只需要比较a[left] ,a[right] else if (a[left] > a[right])// a[mid] > a[left] > a[right] {return left;}else //a[left] < a[right]{return right; // a[mid] > a[right] > a[left]}}else //a[left] >= a[mid]{if (a[mid] > a[right]){return mid;}//a[mid] <= a[right]  ,此时a[mid]已经是最小的 只需要比较a[left] 和a[right]else if (a[left] > a[right]) //  a[left] > a[right] > a[mid] {return right; }else // a[left] <= a[right]{return left;   //a[right] > a[left] > a[mid ]}}}

递归到小的子区间时,可以考虑使用插入排序

归并排序

归并排序是采用分治法的一个非常典型的应用。其基本思想是:将已有序的子序合并,从而得到完全有序的序列,即先使每个子序有序,再使子序列段间有序
在这里插入图片描述

动图演示:
在这里插入图片描述

递归实现

核心步骤:

分解得到子序列
如何得到有序的子序列 ? 序列分解到只有一个元素或是没有元素时,就可以认为是有序了
然后合并两个有序的子序列 ,思路与Leetcode. 88合并两个有序数组 相似 ,依次比较 ,较小值尾插到tmp数组中
创建一个与待排序列大小相同的tmp数组 ,合并完毕后再将tmp数组里的数据拷贝回原数组 ,最后将tmp 数组释放 。
代码实现 :

void _MergeSort(int* a, int left, int right,  int * tmp)
{int mid = (left + right) / 2;//分割// [begin , mid]  [mid+1 , end ]int begin = left;int end = right; if (begin >= end)//序列分解到只有一个元素或是没有元素时,就可以认为是有序了return;_MergeSort(a, begin, mid, tmp); //end-begin+1 是元素个数 _MergeSort(a, mid + 1, end, tmp);//归并 ——类似合并两个有序数组 ,[begin , mid]  [mid+1 , end ]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;  //不能取0 , 两段有序区间不一定是从头开始的区间 while (begin1 <= end1 && begin2 <=end2 ) {if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//begin1 先走完 ,begin2还没有走完,begin2尾插到tmp数组后面while (begin2 <= end2 ){tmp[i++] = a[begin2++];}//begin2 先走完 ,begin1还没有走完, begin1尾插到tmp数组后面 while (begin1 <= end1){tmp[i++] = a[begin1++];}//将tmp数组拷贝到原数组中 memcpy(a+begin, tmp+begin, sizeof(int*) * ( right- left +1));
}
void MergeSort(int* a , int left , int right , int *tmp  )
{//申请一个与原数组大小的tmp数组 tmp = (int*)malloc(sizeof(int) *  (right -left +1 ) );//归并_MergeSort( a,  left,  right , tmp);//释放tmp 数组 free(tmp);}

tmp 和 a的后面为什么都要加begin ?
如图所示:
在这里插入图片描述

非递归实现

递归实现的缺点就是会一直调用栈帧,而栈帧内存往往是很小的。所以,我们尝试着用循环的办法去实现,也就是用非递归的方式去实现 ,但是非递归需要处理几个边界条件

和递归的区别就是递归要将区间一直细分,要将左区间一直递归划分完了,再递归划分右区间,而借助数组的非递归是一次性就将数据处理完毕,并且每次都将下标拷贝回原数组

归并排序的非递归基本思路是将待排序序列a[0…n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并

非递归的核心就是需要控制每次参与合并的元素个数,使序列变为有序

在这里插入图片描述

如果排序的数是奇数,就会出现一些越界的情况需要特殊处理

end1越界,begin2 和end2绝对也越界了 ,那就不对end1后面的进行归并
在这里插入图片描述
举例
在这里插入图片描述


begin2 和end2越界,那不需要对begin2和end2之间的数进行归并
在这里插入图片描述
举例
在这里插入图片描述

end2 越界,还是需要归并,修正end2就可以了
在这里插入图片描述
举例:
在这里插入图片描述

void MergeSort(int* a,int  n )
{int* tmp = (int*)malloc(sizeof(int) * (n ) );if (tmp == NULL){printf("malloc fail");exit(-1);}int gap = 1;while (gap<n){//归并for (int i = 0; i < n; i += 2 * gap){//归并 ——类似合并两个有序数组 ,[begin , end1]  [begin2 , end2 ]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap - 1 + 1, end2 =i+ 2 * gap - 1;  //从图中分析可得int index= i;printf("[%d %d] [%d %d] ", begin1, end1, begin2, end2);/*end1 和begin2越界*/if (end1 >=n || begin2 >=n){break;}//end2越界,修正if (end2 >=n){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}//begin1 先走完 ,begin2还没有走完,begin2尾插到tmp数组后面while (begin2 <= end2){tmp[index++] = a[begin2++];}//begin2 先走完 ,begin1还没有走完, begin1尾插到tmp数组后面 while (begin1 <= end1){tmp[index++] = a[begin1++];}//将tmp数组拷贝到原数组中 memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}free(tmp);
}

归并排序时间复杂度:

每一层归并排序的消耗是O(N) ,有 log层 , 所以归并排序的时间复杂度 O(N * logN)

排序算法复杂度以及稳定性

在这里插入图片描述
在这里插入图片描述

稳定的排序有:直接插入排序、冒泡排序、归并排序
不稳定的排序有:希尔排序、选择排序、堆排序、快速排序、计数排序

在这里插入图片描述

如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注
你们的每一次支持都将转化为我前进的动力!!!

相关文章:

手把手教你 ,带你彻底掌握八大排序算法【数据结构】

文章目录 插入排序直接插入排序希尔排序 选择排序选择排序堆排序升序 交换排序冒泡排序快速排序递归hoare版本挖坑法前后指针版本 非递归Hoare挖坑法前后指针 快排的优化三数取中法选key递归到小的子区间时&#xff0c;可以考虑使用插入排序 归并排序递归实现非递归实现 排序算…...

第十一章 Transform组件(上)

本章节我们介绍Transform类&#xff0c;它是一个组件&#xff0c;每一个游戏对象有拥有该组件。因此&#xff0c;它值得我们重点介绍一下。Transform代表了游戏对象的世界变换&#xff0c;也就是移动&#xff0c;选择和缩放。 首先&#xff0c;我们先介绍它的属性&#xff08;…...

aac音频怎么转mp3,这几个方法很简便

对于aac来说&#xff0c;其是一种高级音频编码&#xff0c;也是专门为声音数据设计的文件压缩格式。通常来说&#xff0c;aac与mp3有一些不同。aac使用了全新的算法进行编码的&#xff0c;其整体的效率较mp3更高一些。同时&#xff0c;aac格式的音质较好一些。但是&#xff0c;…...

分屏视图上线,详情数据秒切换

分屏视图 路径 表单 >> 表单设计 功能简介 新增「分屏视图」。分屏视图是一种对数据阅读提供沉浸式体验的视图组织形式&#xff0c;用户可通过分屏视图更快速的查看数据详情。 使用场景&#xff1a; 对于数据类型是「订单」数据的表单&#xff0c;管理人员往往会对…...

怎么释放C盘空间?清理C盘空间的4大方法分享!

案例&#xff1a;怎么释放c盘空间 【朋友们&#xff0c;最近我的c盘空间内存严重不足了&#xff0c;想释放一下c盘的空间&#xff0c;大家有什么好的方法吗&#xff1f;】 在使用电脑的过程中&#xff0c;经常会遇到C盘空间不足的问题&#xff0c;这时候就需要释放C盘的空间。…...

【文件描述符|重定向|缓冲区】

1 C语言文件操作的回顾 这块博主在讲解C语言时就已经做了很详细的讲解&#xff0c;这里就不详细讲了&#xff0c;直接给出代码。 写操作&#xff1a; #include<stdio.h> #include<stdlib.h> #include<errno.h> #define LOG "log.txt" …...

软件测试—进阶篇

软件测试—进阶篇 &#x1f50e;根据测试对象划分界面测试可靠性测试容错性测试文档测试兼容性测试易用性测试安装卸载测试安全性测试性能测试内存泄漏测试 &#x1f50e;根据是否查看代码划分黑盒测试白盒测试灰盒测试 &#x1f50e;根据开发阶段划分单元测试集成测试系统测试…...

设计模式:创建型设计模式、结构型设计模式

目录 前言如何学习设计模式&#xff1f;设计模式基础设计原则 一. 创建型设计模式1. 模板方法2. 观察者模式3. 策略模式 二. 结构型设计模式1. 单例模式2. 工厂模式3. 抽象工厂4. 责任链5. 装饰器6. 组合模式 前言 如何学习设计模式&#xff1f; 明确目的 在现有的设计模式上…...

如何选择多参数水质分析仪?

如何选择适合的多参数水质分析仪&#xff1f; 首先水质检测仪分为实验室&#xff08;台式&#xff09;和户外使用的便携式多参数水质检测仪。我们呢就要了解自己的需 求使用在什么领域&#xff0c;根据使用领域选择仪器&#xff1b;其次就是选择需要测定的指标&#xff0c;最好…...

明确自动化测试目的

明确自动化测试目的 1.提高测试人员的工作成就感和幸福感&#xff0c;减少手工测试中重复性的工作 目前&#xff0c;在大部分中小企业中&#xff0c;手工测试在日常测试工作占据的比例很大。测试人员必须跟随开发团队不断地进行选代式开发和测试。一个功能模块可能在整个测试周…...

DevExpress.XtraGrid.GridControl导出excel需要添加表头

string head ""; head "单号 \t" txtcCode.Text &#xff1b; string foot ""; foot "制单人 \t" "制单日期 \t" "审核人&#xff1a; \t" "审核日期 \t" "修改人 \t&q…...

守护进程Daemon

进程组、对话期和控制终端关系 每个会话有且只有一个前台进程组&#xff0c;但会有0个或者多个后台进程组。产生在控制终端上的输入&#xff08;Input&#xff09;和信号&#xff08;Signal&#xff09;将发送给会话的前台进程组中的所有进程。对于输出&#xff08;Output&…...

学生成绩管理系统 002

学生成绩管理系统 *****************学生成绩管理系统***************** 1、成绩添加 2、成绩输出 3、成绩查询 4、成绩统计 5、成绩排名 6、成绩删除 7、成绩修改 8、成绩按学号排序 0、退出系统 ************************************************** 请选择功能:1 **********…...

换个花样玩C++(4)细聊C++的引用精妙之处

引用是C++引入的新语言特性。而且在日常工作开发过程中,经常会使用到引用,对于一些做系统架构的架构师而言,这也是不可或缺的一门基本功,我在工作中发现,很多人并没有搞清楚引用。因此我在本篇中将对引用进行详细讨论,希望对大家更好地理解和使用引用起到抛砖引玉的作用。…...

Linux安装helm

前言 运行环境&#xff1a;CentOS7.9 官方参考文档&#xff1a;官方文档 文章末尾附有一键安装脚本 下载安装包 github下载对应版本的安装包&#xff0c;下载地址 进入对应版本的下载页面&#xff0c;这里以v3.11.3为例 选择对应系统的安装包&#xff0c;这里以linux为例 …...

ATTCK v12版本战术介绍——防御规避(四)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权战术理论知识及实战研究、部分防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中防御规避战术第19-24种子技术&#xff0c;后续会介绍防御规避其他子技术&#xf…...

Orangepi Zero2 全志H616(DHT11温湿度检测)

最近在学习Linux应用和安卓开发过程中&#xff0c;打算把Linux实现的温湿度显示安卓app上&#xff0c;于是在此之前先基于Orangepi Zero2 全志H616下的wiringPi库对DHT11进行开发&#xff0c;本文主要记录开发过程的一些问题和细节&#xff0c;主要简单通过开启线程来接收温湿度…...

abbyy是什么软件

ABBYY&#xff0c;一款强大的OCR文字识别软件&#xff01; 在日常的工作中&#xff0c;我们常常需要提取PDF或图片上的大段文字&#xff0c;如果字数少的话&#xff0c;我们可以直接手打&#xff0c;但如果出现大篇幅的文字&#xff0c;那就有点头疼了。今天&#xff0c;我就向…...

软件测试技术(四)白盒测试

白盒测试 白盒测试&#xff08;White Box Testing&#xff09;又称结构测试、透明盒测试、逻辑驱动测试或基于代码的测试。白盒测试只测试软件产品的内部结构和处理过程&#xff0c;而不测试软件产品的功能&#xff0c;用于纠正软件系统在描述、表示和规格上的错误&#xff0c…...

Java基础语法(十二):try-catch块

目录 前言 一、try-catch是什么&#xff1f; 二、其他异常处理机制 总结 前言 Java 异常处理机制是 Java 程序设计中至关重要的一部分。它允许程序员像处理普通数据一样处理异常&#xff0c;并根据异常类型采取合适的措施。其中一个非常基本的异常处理机制是 try-catch 块…...

尚融宝25-投资列表展示以及实现充值功能

目录 一、展示投资列表 &#xff08;一&#xff09;需求 &#xff08;二&#xff09;后端 &#xff08;三&#xff09;前端 二、充值功能 &#xff08;一&#xff09;需求 1、需求描述 2、流程 &#xff08;二&#xff09;充值 1、后端 2、前端 &#xff08;三&…...

QML基础模型(Basic Model)

最基本的分离数据与显示的方法是使用Repeater元素。它被用于实例化一组元素项&#xff0c;并且很容易与一个用于填充用户界面的定位器相结合。 最基本的实现举例&#xff0c;repeater元素用于实现子元素的标号。每个子元素都拥有一个可以访问的属性index&#xff0c;用于区分不…...

如果ChatGPT写作论文,保姆及教程以及问题答疑

上次发表“如何用ChatGPT完成论文”后&#xff0c;许多捧场看官评论讨论&#xff0c;也有不少同学实操成功&#xff0c;但更多人寻求帮助。所以今天再整理一篇&#xff0c;把大家的疑问进行说明。 1. ChatGPT写的论文能否被检查出&#xff1f; 有同学反映将一段ChatGPT…...

机器人中的数值优化(三)—— 无约束最优化方法基础、线搜索准则

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…...

vulnhub靶场之bluemoon

1.信息收集 存活主机进行探测&#xff0c;发现主机192.168.239.176存活。 对主机192.168.239.176进行端口扫描&#xff0c;发现21、22、80端口 访问http://192.168.239.176&#xff0c;并查看源码未发现可利用的行为。 进行目录扫描发现可疑路径/hidden_text 浏览器访问h…...

VTK 几何体连通区域分析 vtkPolyDataConnectivityFilter

前言&#xff1a; vtkPolyDataConnectivityFilter 使用过&#xff0c;但网上没有看到完事的教程&#xff1b;这里整理一下&#xff1b; 提取数据集中连通的多边形数据。 该类是一个滤波器&#xff0c;提取cell&#xff08;区域&#xff09; - 拥有公共点或者满足某个阈值 该类…...

scss、css样式中使用变量的方法;Vue动态改变css等样式文件中的变量

目录 一、问题 二、原因及解决方法 三、总结 一、问题 1.遇到一些样式 设置的值都是重复的不想重复写&#xff0c;想和js一样定义一个常量&#xff0c;然后直接引用这个常量。 2.想要在js中动态设置样式中的值&#xff0c;在 css、scss等样式表中直接使用。 二、原因及解…...

数据治理在学术上的发展史以及未来展望

数据治理是大数据领域中非常重要的一环&#xff0c;从早期的学术研究到如今的各大企业落地实践&#xff0c;经历了漫长的过程&#xff0c;数据治理的实践落地本身也是一场马拉松。 从百度学术通过精确关键词匹配&#xff0c;搜索中文期刊的“数据治理” 和外文期刊的“data gov…...

【搭建博客】宝塔面板部署Typecho博客,并发布上线访问

目录 前言 1.安装环境 2.下载Typecho 3.创建站点 4.访问Typecho 5.安装cpolar 6.远程访问Typecho 7.固定远程访问地址 8.配置typecho 前言 Typecho是由type和echo两个词合成的&#xff0c;来自于开发团队的头脑风暴。Typecho基于PHP5开发&#xff0c;支持多种数据库&…...

【Spring篇】IOC相关内容

&#x1f353;系列专栏:Spring系列专栏 &#x1f349;个人主页:个人主页 目录 一、bean基础配置 1.bean基础配置(id与class) 2.bean的name属性 3.bean作用范围scope配置 二、bean实例化 1.构造方法实例化 2.分析Spring的错误信息 3.静态工厂实例化 4.实例工厂 5.FactoryBean 三…...