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

各种排序算法

插入排序
  • 核心思想:一次选取一个元素作为tem,然后让其与它前面的数组从后向前依次进行比较,直到找到tem需要插入的位置,然后插入进去

  • void InsertSort(int* a, int n) {for (int i = 1; i < n; i++) {int end = i - 1;int tem = a[i];while (end >= 0) {if (tem < a[end]) {a[end + 1] = a[end];end--;}else {break;}}//当代码进行到这里,有两种情况://一种是此时tem>=a[end],即tem应该插入到end这个下标后面//第二种是此时end==-1,也就是说此时tem比数组里所有元素都小,应该插入到开头//无论是哪一种情况,均是把tem插入到下标为end+1的位置a[end + 1] = tem;}
    }
    


希尔排序
  • 核心思想:因为插入排序对于接近有序的数组进行排序是非常快的,所以希尔排序是在直接插入排序的前面加上一个预排序的过程,也就是说先经过一个预排序让数组接近有序,然后在使用直接插入排序

  • 对于预排序:其实是一种分组插排,也就是将间隔为gap的分为一个小组,gap的数值是多少,整个数组就会被分为gap个小组,然后对于每个小组内部进行直接插入排序。gap越大,大的数字越容易到后边,gap越小,越精确。所以gap应该是一个由大变小的数,也就意味这预排序不只进行一轮,而是对于每一个gap都进行一次因为任何数字在除二的最后都会1,而当gap1时,就相当于进行了一个直接插入排序,即核心思想里面的第二步

  • 代码一:(先排好一个小组,在开始排下一个小组)

  • void ShellSort(int* a, int n) {int gap = n;while (gap > 1) {gap /= 2;for (int j = 0; j < gap; j++) {for (int i = j; i < n - gap; i += gap) {int end = i;int tem = a[i + gap];while (end >= 0) {if (tem < a[end]) {a[end + gap] = a[end];end -= gap;}else {break;}}a[end + gap] = tem;}}}
    }
    
  • 代码二:(轮流排所有小组,即第一组先排一个,然后排第二组的第一个… 然后排第一组的第二个,第二组的第二个…)(这样做可以省去一层循环)

  • void ShellSort(int* a, int n) {int gap = n;while(gap > 1){gap /= 2;for (int i = 0; i < n - gap; i++) {int end = i;int tem = a[i + gap];while (end >= 0) {if (tem < a[end]) {a[end + gap] = a[end];end -= gap;}else {break;}}a[end + gap] = tem;}}
    }
    


选择排序
  • 第一种:直接选择排序(每一次遍历一遍右边未处理过的元素,找出其中最小的一个,放在排好序的元素的后面,然后重复进行)。且有一个优化,每一次遍历的时候同时选出未排序元素里面的最小值和最大值(对于选择排序,无论最坏和最好的情况都是O(n^2))

  • void SelectSort(int* a, int n) {int left = 0;int right = n - 1;while (left < right) {int min = left, max = left;for (int i = left+1; i <= right; i++) {if (a[i] < a[min]) {min = i;}if (a[i] > a[max]) {max = i;}}swap(&a[left], &a[min]);if (left == max) {max = min;}//如果left==max,那么在进行left和min的交换的时候,就会将最大值换到原本最小值的位置上,//如果是这种情况,则必须进行修正,即将max=min,以确保max始终是最大值的下标swap(&a[right], &a[max]);left++;right--;}
    }
    
  • 第二种:堆排序(本质也是一种选择排序)

  • void HeapSort(int* a,int n) {//for (int i = 0; i < n; i++) {//AdjustUp(a, i);//模拟插入建堆,只不过是在自身上进行整个过程(向上调整)//}//建堆的第二种方式:(向下调整)for (int i = (n - 2) / 2; i>=0; i--) {AdjustDown(a, n, i);}for (int i = n - 1; i > 0; i--) {Swap(&a[0], &a[i]);AdjustDown(a, i, 0);//每一次将最大的数字与数组的最后一个数字进行交换,然后对第一个数字进行向下调整,在重复上述操作}
    }


交换排序
  • 第一种:冒泡排序 (每一次遍历从前向后依次两两比较,将大的数放在两个位置的后面那个位置,相当于每一次遍历都能将为排序数据里最大的那个数移动到最后边)

  • void BubbleSort(int* a, int n) {int flag = 0;for (int j = 0; j < n-1; j++) {for (int i = 1; i < n-j; i++) {//相当于控制的是两个数里后边那个数的范围if (a[i - 1] > a[i]) {swap(&a[i - 1], &a[i]);flag = 1;}}if (flag = 0) {return;}}
    }
    
  • 第二种:快速排序 选出一个基准值,然后将它放在它排好序后应该在的位置,

    • 第一种思路:(以左边为key在有序时效率会大大降低,所以应该随机选key)

    • void QuickSort(int* a, int left, int right) {if (left >= right) {return;}int  begin =  left;int end = right;//这是随机数选key法int randi = left + (rand() % (right - left));swap(&a[left], &a[randi]);int k = left;
      ////这是三数取中选key法int GetMidNUmi(int* a, int left, int right) {int mid = (left + right) / 2;if (a[left] < a[mid]) {if (a[mid] < a[right]) {return mid;}else if (a[left] > a[right]) {return left;}else {return right;}}else {if (a[mid] > a[right]) {return mid;}else if (a[left] > a[right]) {return right;}else {return left;}}
      }int tem = GetMidNUmi(a, left, right);swap(&a[left], &a[tem]);int k = left;
      //while (left < right) {while (left < right && a[right] >= a[k]) {right--;}while (left < right && a[left] <= a[k]) {left++;}swap(&a[left], &a[right]);}swap(&a[left], &a[k]);k = right;QuickSort(a, begin, k - 1);QuickSort(a, k+1, end);
      }
      
    • 第二种思路:(挖坑法)

    • void QuickSort(int* a, int left, int right) {if (left >= right) {return;}int  begin =  left;int end = right;int tem = GetMidNUmi(a, left, right);swap(&a[left], &a[tem]);int k = a[left];int index = left;while (left < right) {while (left < right && a[right] >= k) {right--;}a[index] = a[right];index = right;while (left < right && a[left] <= k) {left++;}a[index] = a[left];index = left;}a[index] = k;QuickSort(a, begin, index - 1);QuickSort(a, index +1, end);
      }
      
    • 第三种: 前后双指针法

    • void QuickSort(int* a, int left, int right) {if (left >= right) {return;}int tem = GetMidNUmi(a, left, right);swap(&a[left], &a[tem]);int k = a[left];int prev = left;int cur = left + 1;while (cur <= right) {if (a[cur] < k) {swap(&a[++prev], &a[cur]);}cur++;}swap(&a[prev], &a[left]);QuickSort(a, left, prev - 1);QuickSort(a, prev + 1, right);
      }
      
    • ***对于快排的优化:***在递归到达一定层数之后,如果数据量小于一个限度的时候,使用插入排序而不是继续递归下去会更有优势(所以优化就是快排+插入排序)

    • void InsertSort(int* a, int n) {for (int i = 1; i < n; i++) {int end = i - 1;int tem = a[end+1];while (end >= 0) {if (tem < a[end]) {a[end + 1] = a[end];end--;}else {break;}}a[end + 1] = tem;}
      }void QuickSort(int* a, int left, int right) {if ((right - left + 1) > 10) {int tem = GetMidNUmi(a, left, right);swap(&a[left], &a[tem]);int k = a[left];int prev = left;int cur = left + 1;while (cur <= right) {if (a[cur] < k) {swap(&a[++prev], &a[cur]);}cur++;}swap(&a[prev], &a[left]);QuickSort(a, left, prev - 1);QuickSort(a, prev + 1, right);}else {InsertSort(a+left, right - left + 1);return;}
      }//相当于大规模的时候使用快排,小规模的时候使用插入
      
    • 快排的非递归形式:(在递归形式的快排里,其实本质上每一次递归都是存下了当前这一层所处理的区间下标)

    • int st[1000];
      int tail = 0;
      void QuickSort(int* a, int left, int right) {st[tail++] = right;st[tail++] = left;while (tail != 0) {int begin = st[--tail];int end = st[--tail];//排序一趟int tem = GetMidNUmi(a, begin, end);swap(&a[begin], &a[tem]);int keyi = begin;int prev = begin;int cur = begin + 1;while (cur <= end) {if (a[cur] < a[keyi]) {swap(&a[++prev], &a[cur]);}cur++;}swap(&a[prev], &a[keyi]);keyi = prev;if(keyi + 1 < end) {st[tail++] = end;st[tail++] = keyi+1;}if (begin < keyi - 1) {st[tail++] = keyi-1;st[tail++] = begin;}}
      }
      


归并排序

  • 对于归并排序,应该使用黑盒思想去理解,相信merge函数可以让左,右区间分别有序,在当前层只需要合并左右两个有序区间即可

  • void _MergeSort(int* a, int left, int right, int* tem) {if (left >= right) {return;}int mid = (right + left) / 2;_MergeSort(a, left, mid, tem);_MergeSort(a, mid+1, right, tem);int begin1 = left; int end1 = mid;int begin2 = mid+1; int end2 = right;int flag = left;while (begin1 <= end1 && begin2 <= end2) {if (a[begin1] < a[begin2]) {tem[flag++] = a[begin1++];}else {tem[flag++] = a[begin2++];}}while (begin1 <= end1) {tem[flag++] = a[begin1++];}while (begin2 <= end2) {tem[flag++] = a[begin2++];}for (int i = left; i <= right; i++) {a[i] = tem[i];}
    }
    void MergeSort(int* a, int n) {int* tem = (int*)malloc(sizeof(int) * n);_MergeSort(a, 0, n - 1, tem);free(tem);
    }
    
  • 非递归的归并排序(不能使用栈,因为它相当与是一个后序遍历,而用栈模拟相当于是一个前序,所以应该使用循环直接改非递归)

  • void MergeSort(int* a, int n) {int* tem = (int*)malloc(sizeof(int) * n);int gap = 1;while(gap<n){for (int j = 0; j < n; j += 2*gap) {int begin1 = j; int end1 = j + gap - 1;int begin2 = j + gap; int end2 = j + 2 * gap - 1;if (end1 >= n || begin2 >= n) {break;}else if (end2>=n) {end2 = n - 1;}int flag = j;while (begin1 <= end1 && begin2 <= end2) {if (a[begin1] < a[begin2]) {tem[flag++] = a[begin1++];}else {tem[flag++] = a[begin2++];}}while (begin1 <= end1) {tem[flag++] = a[begin1++];}while (begin2 <= end2) {tem[flag++] = a[begin2++];}for (int i = j; i <= end2; i++) {a[i] = tem[i];}}gap *= 2;}free(tem);
    }
    


计数排序

本质上就是一种哈希思想

void CountSort(int* a, int n) {int min = a[0];int max = a[0];for (int i = 1; i < n; i++) {if (a[i] > max) {max = a[i];}if (a[i] < min) {min = a[i];}}int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);memset(count, 0, sizeof(int) * range);for (int i = 0; i < n; i++) {count[a[i] - min]++;}int flag = 0;for (int i = 0; i < range; i++) {while (count[i]--) {a[flag++] = i + min;}}free(count);
}

相关文章:

  • 从 BI 与 SQL2API 的差异,看数据技术的多元发展路径
  • 网络原理 - 初识网络 1
  • vue3.2 + element-plus 实现跟随input输入框的弹框,弹框里可以分组或tab形式显示选项
  • 操作系统之shell实现(上)
  • 力扣DAY52-54 | 热100 | 图论:腐烂的橘子、课程表、前缀树
  • 解决Flutter 2.10.5在升级Xcode 16后的各种报错
  • Linux网络编程第一课:深入浅出TCP/IP协议簇与网络寻址系统
  • 源码分析之Leaflet中Map类扩展方法之ScrollWheelZoom
  • Redis核心数据类型在实际项目中的典型应用场景解析
  • git UserInterfaceState.xcuserstate 文件频繁更新
  • 【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——IMX335摄像头测试 #MIPI CSI
  • lodash-es 详解
  • 矩阵基础+矩阵转置+矩阵乘法+行列式与逆矩阵
  • TensorRT模型部署剪枝
  • Day92 | 灵神 | 二叉树 路径总和
  • Information-Theoretic Limits of Bistatic Integrated Sensing and Communication
  • 危化品经营单位安全生产管理人员备考要点
  • 深入探究Linux编译器gcc/g++:从基础到进阶
  • B2B2C商城系统流程图解析
  • 从零构建机器学习流水线:Dagster+PyTorch实战指南
  • 小型企业网站设计教程/招商外包
  • 做淘宝客网站用什么程序最好/免费男女打扑克的软件
  • 东莞网站建设seo/临沂seo推广
  • 开发网站找什么公司吗/成都网站快速开发
  • 网站建设哪个最好/网站seo推广营销
  • 常州外贸网站建设/深圳精准网络营销推广