排序算法解析

    xiaoxiao2025-03-06  7

    摘要 

    在IT界,,有很多的时候要对于一些数据 ,,进行排序  有时候 可能是 小量的数据 ,,,但是  也有可能是 一些的大数据  ,,,量十分的大。。。 在不同的时候我们对于这些的排序使用的算法是不相同的。。。 我写的这篇文章就是 很好的解析了  这些排序 算法。。。

    算法解析 

    我们经常所说的排序算法,,经常大概有 下面的这几种l;; 下面来我自己实现这些个算法

    一、插入排序 

    插入排序主要分为 直接插入排序 还有 希尔排序    两种

    直接插入排序  

    基本思想: 将一个数据直接插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止,,,这就是 所谓的直接插入排序  。 代码的实现  //插入排序 //最坏的情况 (接近逆序)时间复杂度 为 N*N //最好的情况 (接近升序)时间复杂度 为 N void InsertSort(int* array,const size_t n) { assert(array); for(size_t i = 1 ;i < n;++i) { int tmp = array[i]; int end = i-1;//使用一个end来表示插入的有序数列的位置 //单次排序 while(end>= 0) { //插入到一个有序的数列中 if(tmp < array[end]) { array[end+1] =array[end]; --end; } else { break; } } //找到位置 ,,将值插入进去 array[end+1] =tmp; } }

    希尔排序 

    基本思想:希尔排序也是一种插入排序方法,实际上是一种分组插入方法。 换句话说 就是 ,,在直接插入排序之前 增加一个 预排序 ,,来减少移位的次数 方法就是 ,,,, 先将 数组分成一个gap组 走到此处之后 ;;;就是 直接 改变gap的值 ,,,,直到最后的直接插入排序 ;;; 代码的实现 //希尔排序 //这个算法时间复杂度 不好计算 //时间复杂度大致可知在 为 [N ,N*N]; void ShellSort(int *array,const size_t n ) { int gap = n; //排序 直接的插入排序 while(gap > 1) { gap = gap/3+1; //预排序 gap组的数据 for(size_t i = gap;i < n;++i) { int tmp =array[i]; int end =i-gap; //排序 gap间隔的数据 while(end>=0) { if(tmp < array[end]) { array[end+gap] =array[end]; end -= gap; } else { break; } } array[end+gap] =tmp; } } }

    二、选择排序

    可以被称作的选择排序的 有两种 ;;;; 选择排序 还有 就是堆排序

    选择排序

    基本思路: 每一趟从待排序的数据中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。 关键点:记住有序数列与无序数列的间隔  思路图: 代码实现 //选择排序 // 时间复杂度 为 N*N void SelectSort(int * array,const size_t n) { assert(array); int end = n;//end来保存有序和无序的间隔 while(end > 0) { end--; int maxidex = 0 ; //单趟排序 for(size_t i = 0 ;i <= end;++i) { //找到最大的值的下标 if(array[i] > array[maxidex]) { maxidex = i; } } swap(array[maxidex],array[end]); } }

    堆排序

    堆排序,,,说穿了就是 一个选择排序 ,,,,但是 使用的是堆的数据结构 ,,,找到当前的(未有序的的序列)的最大值(或者是最小值),放到有序序列中  关键点:建堆 ,交换 ,堆调整  代码实现 1、使用系统库中的函数来实现 //堆排序 直接使用的是 库函数 //时间复杂度 为 N*logN void HeapSort(int * array,const size_t n) { assert(array); //先建堆 make_heap(array,array+n); for(size_t i = 0 ;i < n-1;++i) { pop_heap(array,array+n-i); } } 2、自己实现 //堆排序 自己实现 //需要自己实现一个向下调整的函数 //要是 升序 ,,我们需要建立 一个大堆 //函数有一个假设就是 左右子树 都已经是 大堆 void AdjustDown(int * array, int K,const size_t n) { int parent = K; //定义一个父节点的下标 int child = parent *2+1; //得到一个孩子节点的下标 while(child < n) { //找到两个孩子中最大的那个节点的下标 给 child if((child + 1) < n && array[child+1] >array[child]) { child++; } //要是孩子节点的值大于 父节点的值 //将 孩子节点的值 与 父亲节点的值 进行交换 //但会导致 子树的结构不是大堆 //所以要继续向下调整 if(array[child] > array[parent]) { swap(array[child],array[parent]); parent =child; child = parent*2+1; } else { break; } } } void HeapSort(int * array,const size_t n) { assert(array); //先对于整个数据 进行建堆 //如果要保证左右子树都是大堆的话,,,就要从第一个非叶子节点进行 调整 for(int i = (n-1-1)/2; i >= 0;--i) { AdjustDown(array,i,n); } int end = n;//使用 end作为 已经得到的有序 的值 起始下标 初始值 为n; while(end > 1) { --end; swap(array[0],array[end]); AdjustDown(array,0,end); } }

    三、交换排序 

    交换排序,,,就是将两个数据 进行交换的算法;;;;

    冒泡排序 

    这个算法,,,就是很简单的算法。。。。。 实现起来也很容易  ,,,这里就不细说了 。。。 //冒泡排序 //时间复杂度 为 //最坏的情况(降序的时候 )N*N //最好的情况(升序 ) N //但是 相对来说还是 插入排序 更优 void BubbleSort(int *array,const size_t n ) { assert(array); for(size_t i = 0 ;i < n-1;++i) { //单趟排序 //优化 使用一个数 来记录交换的次数 int j = 0; int count = 0 ; for(;j < n-1-i;++j) { if(array[j] > array[j+1]) { swap(array[j],array[j+1]); count++; } } if(count == 0)//要是交换的次数 为0 表示 已经 是有序的了 break; } }

    快速排序 

    这是一个很经典的排序算法  ,,,,很多的算法都是使用此类算法 基本思路:它是由冒泡排序改进而来的。在待排序的n个记录中任取一个记录(通常取第一个记录),把该记录放入适当位置后,数据序列被此记录划分成两部分 。 数组所有元素大小 比该记录小的放到前一部分 比它大的放到右边 并把该记录排在这两部分的中间(称为该记录归位),这个过程称作一趟快速排序。           最核心的思想是  左右 分割。 代码实现 //部分 快速排序 //快速排序一般使用的是 递归算法 //[left,right] int PartSort(int * array,int left,int right) { //取一个中间值作为 关键字 int key = right; while(left < right) { //从左边找到一个比关键值大的 while(left<right&& array[left] <=array[key] ) { ++left; } //从右边找到一个 比 关键值小的 while(left < right && array[right] >= array[key]) { --right; } swap(array[left],array[right]); } swap(array[left],array[key]); return left;//需要返回一个中间值 } //[left,right] void QuickSort(int * array,int left,int right) { assert(array); //如果要是 if(left >= right) return ; int ret = PartSort(array,left,right);//得到分割点 //将左边的进行 排序 QuickSort(array,left,ret-1); //将右边的进行 排序 QuickSort(array,ret+1,right); }

    五、归并排序

    基本思路: 把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列                                         

    如设有数列{6,202,100,301,38,8,1}

    初始状态:6,202,100,301,38,8,1

    第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;

    第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;

    第三次归并后:{1,6,8,38,100,202,301},比较次数:4;

    总的比较次数为:3+4+4=11,;

    归并排序具体工作原理如下(假设序列共有n个元素):

    将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素

    将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素

    重复步骤2,直到所有元素排序完毕 

    代码实现

    //归并排序 //假设 两个数组 已有序 ,,, //然后 将两个数组里的数,,,排序 void _MergeSort(int * array,int * tmp,int left,int right) { //当数组只有一个数时 ,,,,为有序 if(left >= right) { return ; } //让左右两个数组里的数 int mid = left + ((right-left)>>1); _MergeSort(array,tmp,left,mid); _MergeSort(array,tmp,mid+1,right); //到此处说明两个集合的数已经有序 // int begin1 =left,end1 = mid; int begin2 = mid+1,end2 = right; int idex = left; while(begin1<= end1 && begin2<=end2) { if(array[begin1] <= array[begin2]) { tmp[idex++] = array[begin1++]; } else { tmp[idex++] = array[begin2++]; } } while(begin1<= end1) { tmp[idex++] = array[begin1++]; } while(begin2<= end2) { tmp[idex++] = array[begin2++]; } while(left<=right) { array[left] = tmp[left]; left++; } } void MergeSort(int * array,const size_t n) { int * tmp = new int[n]; _MergeSort(array,tmp,0,n-1); delete[] tmp; }

    基数排序

     基本思想 它是一种非比较排序。它是根据位的高低进行排序的,也就是先按个位排序,然后依据十位排序……以此类推。

    设有一个初始序列为: R {50, 123, 543, 187, 49, 30, 0, 2, 11, 100}。

    我们知道,任何一个阿拉伯数,它的各个位数上的基数都是以0~9来表示的。

    所以我们不妨把0~9视为10个桶。

    我们先根据序列的个位数的数字来进行分类,将其分到指定的桶中。例如:R[0] = 50,个位数上是0,将这个数存入编号为0的桶中。

    分类后,我们在从各个桶中,将这些数按照从编号0到编号9的顺序依次将所有数取出来。

    这时,得到的序列就是个位数上呈递增趋势的序列。

    按照个位数排序: {50, 30, 0, 100, 11, 2, 123, 543, 187, 49}。

    接下来,可以对十位数、百位数也按照这种方法进行排序,最后就能得到排序完成的序列。

    代码分析

    //基数排序 //得到的是 数组最大数 的位数 size_t GetMaxDegit(int * array,const size_t n) { int base =10; int degit = 1; for(size_t i= 0 ;i < n;++i ) { while(array[i] >= base) { ++degit; base*= 10; } } return degit; } //先低位再高位 void LSDSort(int *array,size_t n) { int degit = GetMaxDegit(array,n); int base = 1; for(size_t j = 0 ;j < degit;++j) { int count[10] = {0}; int start[10] = {0}; //记录每个下标的位置 for(size_t i = 0 ;i < n;++i) { int num = (array[i]/base)%10; count[num]++; } for(size_t i = 1 ;i < 10;++i) { start[i] = count[i-1] +start[i-1]; } int * tmp = new int[n]; for(size_t i = 0;i < n;++i) { int num = (array[i]/base)%10; tmp[start[num]++] =array[i]; } memcpy(array,tmp,sizeof(int)*n); base *= 10; } } //先高位再低位 //单趟排序 //[left,ri]ht] 表示找到高位相等,,,,的数 低位排序 void MSDPartSort(int *array,int left,int right,int base,int n) { int count[10] = {0}; int start[10] ={left}; //记录每个位置的数据 的个数 for(size_t i = left ;i <= right;++i) { int num = (array[i]/base)%10; count[num]++; } //找到每个位置的起始位置 for(size_t i = 1 ;i < 10;++i) { start[i] = count[i-1] +start[i-1]; } int * tmp = new int[n]; for(size_t i = left;i <= right;++i) { int num = (array[i]/base)%10; tmp[start[num]++] =array[i]; } memcpy(array+left,tmp+left,sizeof(int)*(right-left+1)); delete[] tmp; } void MSDSort(int *array,size_t n) { int degit = GetMaxDegit(array,n); int base = 1; for(size_t i = 0;i <(degit-1);++i) { base *= 10; } for(size_t j = 0 ;j < degit;++j) { int left = 0;//使用 left表示的是 相同高位的数左下标的 int right =0 ;// 相同高位的右下标 while(right<n) { int num1 = array[left]/(base*10);//找到每个数的高位 int num2 =array[right]/(base*10); if(num1!= num2)//如果要是相等的话 { MSDPartSort(array,left,right-1,base,n);//将之前的数排序 left =right;//继续向后 寻找 ,高位相等的数 } else { right++; } } if(left != right)//数组走到尽头之后 把但前组的数据排序 { MSDPartSort(array,left,right-1,base,n); } base /=10;//j继续向低位排序 } }

    转载请注明原文地址: https://ju.6miu.com/read-1296937.html
    最新回复(0)