十大排序算法的实现和个人简单总结

内部排序和外部排序
根据排序过程中涉及的存储器不同,可以讲排序方法分为两大类:一类是内部排序,指的是待排序的几率存放在计算机随机存储器中进行的排序过程;另一类的外部排序,指的是排序中要对外存储器进行访问的排序过程。
内部排序是排序的基础,在内部排序中,根据排序过程中所依据的原则可以将它们分为5类:插入排序、交换排序、选择排序、归并排序和基数排序;根据排序过程的时间复杂度来分,可以分为三类:简单排序、先进排序、基数排序。
评价排序算法优劣的标准主要是两条:一是算法的运算量,这主要是通过记录的比较次数和移动次数来反应;另一个是执行算法所需要的附加存储单元的的多少。
排序的稳定性:
若在待排序的记录中,存在两个或两个以上的关键码值相等的记录,经排序后这些记录的相对次序仍然保持不变,则称相应的排序方法是稳定的方法,否则是不稳定的方法。



1. 交换类排序
交换排序的基本方法是:两两比较待排序的排序码,交换不满足顺序要求时偶对,直到全部满足的位置。常见的冒泡排序和快速排序。


1 冒泡排序
从数组的第一个数开始,一次遍历数组中的每一个数,通过相邻的交换,每一轮下来找出剩余未排序数的最大数,并“冒泡”至顶端
算法:从第一个数开始,一次与下一个数比较并交换,直到最后一个数时间复杂度o(n);


2。快速排序
快速排序又称分区交换排序,是冒泡的改进;
基本思想:通过一趟排序将排序的数据分割成独立的两部分,其中一部分的所有数据比另外一部分都要小,然后按此方法对这两部分数据递归进行快速排序。
算法:
(1)从待排序中任意选取一个记录(通常选择第一个记录)为分区标准
(2)第一趟排序把所有小于该拍序列的记录移动到左边,把所有大圩该排序码的记录移动到右边。
(3)对两个子序列继续插入排序
模拟算法
例如  4 5 2 1 3 6
low=0;hight=5;
value=str[0]=4;
  
   4 5 2 1 3 6
从高位向低位进行比较,将高位的较小值放入strt[low]
 第一次  3 5 2 1 3 6   start=0,end=4;
然后从低位向高位比较,将较小值与上次遍历得到的较大值进行换算
   3 5 2 1 5 6   start=1,end=4
当low 与high不相等时  继续循环
   3 1 2 1 5 6  start=1,end=3;
   3 1 2 2 5 6 start=3,end=3;
低位与高位相遇
   str[3]=value=4;
所以第一次排序的结果为 3 1 2 4 5 6
整体排序思路:   从后面找到一个较小值将第一位替代(并未交换,只是覆盖),然后从前面找到一个较大值将上一次所找到较小值(后面)覆盖,
按此思路,不断寻找较大值和较小值覆盖前者,当Low和higt相遇时,将
value放置至此。因此就实现了value左边始终小于,右边始终大于,但并未整体排好序。
然后进行递归

代码如下:
void quicksort(int x[], int length)
{
 int start = 0;
 int end = length - 1;
 int value = x[start];
 if (length <= 1)return;
 while(start<end)
 {
 
  while (x[end] > value &&start < end)end--;
  x[start] = x[end];
  while (start < end&& x[start] < value)start++;
  x[end] = x[start];
 }
 x[start] = value;
 quicksort(x, start);
 quicksort(x+start+1, length - start-1);
}


2.0插入类排序
直接插入排序
  有一个已经有序的数据序列,要求在这个排好的数据列中插入一个数,但要求插入后此序列任然有序。适用于少量的数据元素时,时间复杂度为o(n^2)稳定方法;
 基本思想:每步将一个待排序的记录,按其关键码值的大小插入前面已经排好序的文件中,知道插入完为止。
例子:   4 5 2 1 3 6
第一次时 将4设为有序集合  5 2 1 3 6 表示待插入集合
从i=1开始计算        5>4  所以j=i-1  不用变换
从I=2    2<5 <4   j<0时 到达末尾
  将第I位赋值给temp=2;
  从j位后一位到i位前一位都需要后移一位
  x 4 5 1 3 6
  将temp值赋予j+1位。
  则表示为
  2 4 5 1 3 6
     出顺序


代码如下:
void insert_sort(int x[], int length)
{
 int i, j, k;
 for (i = 1; i < length; i++)
 {
  //为a[i]在前面的a[]有序区间找一个合适的位置
  for (j = i - 1; j >= 0; j--)
  {
   if (x[j] < x[i])break;
  }
  if (j != i - 1)
  {
   int temp = x[i];
   for (k = i - 1; k > j; k--)
    x[k + 1] = x[k];//循环结束后,k移动至j位,失配位
   x[k + 1] = temp;//将后一位置为temp

  }
 }
}



2
Shell排序,又称缩小增量排序,直接插入排序的改进,Shell排序的时间依赖于增量的序列。
希尔排序对直接插入排序进行了优化,能间隔来控制前几次排序移位的长度。最后以间隔为1实现排序。
例如
   4   5  2  1  3   6
排序中 首先设置首次增量,且增量的变化
首先默认增量为长度的一办
第一次移位时  1 5 2 4 3 6
i++   1 3 2 4 5 6
第三次跳出
d/2=1;现在进行最后排序
   1 3 2 4 5 6
   1 2 3 4 5 6
因此便排序好
希尔排序实际上就是直接插入排序,当我们选择间隔不同时,每个数组对应的顺序就因此不同,希尔排序也可以直接通过  嵌套冒泡排序,或者嵌套直接插入排序实现。
希尔排序比插入排序快速的原因,d较大时,每一堂移动的数目较少,但数据项移动的距离很长,当d和小时,移动多,但已经趋于稳定。所以效率极高。
代码如下:



void Shell_insert(int x[], int length)
{
 if (x == NULL)return;
 int d = length;
 int i, j, temp=0;
 while (d >= 1)
 {
  d = length / 3+1;
  for (i = d; i <length; i++)
  {
   for (j = i; j >= d && (x[j] < x[j - d]); j -= d)
   {
    temp = x[j];
    x[j] = x[j - d];
    x[j - d] = temp;
   }
  }
 }
}




3  选择类排序
选择类排序有点像贪心法的概念,每次将最小的找出,知道排完。

简单选择排序 又称直接选择排序
时间复杂度 不管是最好还是最差情况下,简单选择排序的时间复杂度都为o(n^2)所以 算法的效率太低
例子  
   4  5  2  1  3   6
将最小数找出并交换第 i位 1  5  2  4  3   6
第二次同样  1 2  5   4  3    6
   1 2  3  4  5   6
后面一次比较 当i>length时退出
代码



void selectSort(int x[], int length)
{
 int i, j, k, temp;
 for (i = 0; i < length; i++)
 {
  k = i;
  for (j = i+1; j < length; j++)
  {
   if (x[j] < x[k])k = j;
  }
  temp = x[k];
  x[k] = x[i];
  x[i] = temp;
  
 }
}


 折半插入排序
是插入排序算法的改进。
插入排序是与始终与前面相比,然而并未利用到已经排好序的特性。
从左到右寻找合适的位置插入
直接代码,没什么意义


void BinsertSort(int x[], int length)//0号单元,表示比较的数目
{
 int low, high, middle, temp;
 for (int i = 1; i <= length; ++i)
 {
  temp = x[i];
  low = 0;
  high = i - 1;
  while (low <= high)
  {
   middle = (low + high) / 2;
   if (x[middle] > temp)high = middle - 1;
   else low = middle + 1;
  }
  for (int j = i - 1; j >= low; j--)
  {
   x[j + 1] = x[j];
  }
  x[low] = temp;
 }
}
重点  
将待插入位 与前区间中点进行比较,当大于时low的位置进行变化
          当小于时high的位置变化
不断缩小区间。low和high的位置颠倒时,low的位置 便是待插入应放入的位置
例子
   2    4    6    8    10  3
3与前5位中点进行比较  low=0; high=4;middle=(0+4)2=2;
   3<6
   high=middle-1=1; low=0;middle=0;
   3>2
   low=1;high=1;midle=1;
   3<4
   low=1;high=0;
   跳出  所以 3应该插入节点1的位置,后面都   应后移一位





 堆排序
  堆排序是利用堆积树设计的一种排序算法,可以利用数组的特点快速点位指定索引的元素,分为大根堆和小根堆,即完全二叉树。
大根堆根是最大值,小根堆,根是最小值

大根堆排序算法的操作
1 建堆
如何构建初始堆,将一个数组想象成一个完全二叉树,在每一位
i应该大于 2*i+1和2*i+2位,满足父节点大于子节点的条件
例如
   4
             5       2
          1   3   6                      从后面开始换  531 已经满足 。26 不满足  
  
       4
   5      6            此时  546 不满足 
  1 3    2
 
       6
   5      4    
 1  3   2      此时满足条件  构造完毕
假如构造这一
               0                             5                                                5
          5      4                      0     4    013不满足,再次交换     3    4
          1  3   2                  1   3  2                                        1   0   2
 
2 调整堆  每一次取得最大值之后,将最大值放于末尾,长度-1,继续调整堆。调整的方法可以是递归,也可以是循环
3 堆排序   不断的取得最大值,不断调整
当然,可以将三步合并在一起。



 void adjustHeap(int x[],int length,int i)//循环法调整堆
{
 int leftchidren;
 int temp = 0;
 for (int max=i;max<length;i=max)//将max设置为头节点   每一次循环后将父节点设为max;
 {
   leftchidren = max * 2 + 1; //左孩子
   if (leftchidren >= length)break;//左孩子大于length时,已经没有继续的价值
  if (leftchidren < length && x[leftchidren]>x[max])//当左孩子大于父节点时
  {
   max = leftchidren;  //max改为左节点
  }
  if (leftchidren+1 < length && x[leftchidren+1]>x[max])
  {
   max = leftchidren+1;//同理
  }
  if (max != i)
  {
   temp = x[max];
   x[max] = x[i];
   x[i] = temp;
         
  }
  else
  {
   break;  //当i与max相等时,说明节点之间并没有改变,不需要去改变子节点大小
  }

 }
}


void adjustHeapby(int x[], int length, int i)//递归
{
 int leftchidren;
 int temp = 0;
 int  max = i;
  leftchidren = max * 2 + 1; //左孩子
  if (leftchidren >= length)return;//左孩子大于length时,已经没有继续的价值
  
  if (leftchidren < length && x[leftchidren]>x[max])//当左孩子大于父节点时
  {
   max = leftchidren;  //max改为左节点
  }
  if (leftchidren + 1 < length && x[leftchidren + 1]>x[max])
  {
   max = leftchidren + 1;//同理
  }
  if (max != i)
  {
   temp = x[max];
   x[max] = x[i];
   x[i] = temp;
  }
  else
  {
   return;  //当i与max相等时,说明节点之间并没有改变,不需要去改变子节点大小
  }
  adjustHeap(x, length, max);
 }


void heap_sort(int x[], int length)
{
 int temp;
 for (int i = 0; i < length; i++)
 {
  for (int j = (length - i) / 2 - 1; j >= 0; j--)
   adjustHeap(x, length - i, j);
  temp = x[0];
  x[0] = x[length - i-1];
  x[length - i-1] = temp;
 }
}



归并排序
归并排序是建立在归并操作上一种有效的排序算法,该算法采用分治法的一个典型的应用,是一种稳定的排序。将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,将两个有序表合并成一个有序表,称为二路归并。
重点:递归分解数列,合并数列完成归并。
第一步:申请空间,使其大小为两个排序序列之和,该空间用来存放合并后的序列。
第二步:指定两个指针,最初位置分别为两个已经排好序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素入合并空间,并移动指针到下一位。重复直到某指针超出序列



代码如下
void merge(int x[], int left, int mid, int right,int temp[])//合并操作,申请一个临时储存的空间保存最后转移
{
 int startLeft = left;  //左边区域的开始
 int startRight = mid + 1; //右边区域的开始
 int m = mid, last= right;
 int startMerge = 0;//合并区域的开始
 while (startLeft<=mid && startRight<=last)//三个储存记号,此处应用于多项式的合并
 {
  if (x[startLeft] <= x[startRight])
  {
   temp[startMerge++] = x[startLeft++];
  }
  else
  {
   temp[startMerge++] = x[startRight++];
  }
 }     
 while (startLeft<=m)  //将后缀添加至末尾
 {
  temp[startMerge++] = x[startLeft++];
 }
 while (startRight <= last)
 {
  temp[startMerge++] = x[startRight++];
 }
 for (int i = left; i<= right; i++) //将排序好的值返回对应数组空间
 {
  x[i] = temp[i - left];
 }
}
void mergeSort(int x[], int left, int right, int temp[])//递归实现,当使用非递归方法时,可像上述希尔排序的d一样
{
 int mid;
 if (left < right)
 {
  mid = (left + right) / 2;
  mergeSort(x, left, mid,temp);
  mergeSort(x, mid + 1, right, temp);
  merge(x, left,mid,right, temp);
 }
}
void MergeSort(int x[], int length)
{
 int *temp = new int[length];
 if (temp == NULL)return;
 mergeSort(x, 0, length - 1, temp);
 delete[]temp;
}

//重点部分,归的方式就不用多说
 递归方式如下
  4 5  2  1  3  6
  (4,5,2)(1,3,6);
  (4,5),2,(1,3),6
  (4)(5)(2)(1)(3)(2)
当递归到末尾只有两位数时,进入最后一次递归 只有1位数 跳出
  然后执行至合并操作。一层层跳出递归,实现排序



计数排序 
 是一个非基于比较的排序算法,是对一定整数范围内的整数进行排序,快于任何比较排序的算法,是一种牺牲空间换取时间的算法。
代码
void countingSort(int x[], int length)
{
 int max=x[0], min = x[0];//取得该数组中的最大值和最小值
 for (int i = 0; i < length; i++)
 {
  if (x[i] > max)max =x[i];
  if (x[i] < min)min = x[i];
 }
 int tempLength = max - min+1; //申请辅助数组的长度
 int *countArray=new int[tempLength];//申请最大值后最小值差值的辅助数组长度
 for (int i = 0; i < tempLength; i++)
 {
  countArray[i] = 0;    //将辅助数组初始化为0
 }
 for (int i = 0; i < length; i++)
 {
  countArray[x[i]-min]++;   //将从0-leng位  放置入数组
 }
 int idex = 0;
 for (int i = 0; i < tempLength; i++)
 {
  while (countArray[i]--)
  {
   x[idex++] = i + min;   //将数组返回至原来数组
  }

 }
 delete []countArray;
}
思想:在较少数的情况下,申请空间,利用当前数组下标的已知资讯情况下,将每一位数,映射到临时数组中所对应的位置,然后依次返回便可。


桶排序,或者称为箱排序,工作原理是将数组分到有限数量的桶子里面。每个桶子再个别排序。
思想:将整数的数组最大值的范围与最小值的范围建立一个具有一定容量的桶内(我们想象成数学里的长方形的统计通),然后在每一个长方块内单独排序,最后将其连接起来,实现排序。
代码不做研究。




基数排序
基数排序属于分配式排序,又称桶子法,是透过键值的部分资讯,将要排序的元素分配至某些“桶”中,达到排序的作用,是稳定性的排序

基数排序,将整型位数为标准,从低位到高位比较各位,比较后排序,直到整个数组有序。
例子
 64,54,42,1,317,28,128,368,456,879,4564,1354,123
个位       0   1     2      3    4     5       6      7     8     9   
    1    42  123  64         456    317  28    879
         54                          128
       4564         368
       1354
 
十位   0      1        2        3        4       5        6         7       8          9
          1      317    123             42     54      64       789 
             28                      1354  4564
             128          456    368
百位  0      1          2        3            4          5        6        7         8       9
         1     123               317        456     4564              789      
         28   128              1354
         42                        368
         54
         64
千位  不太方便  横着放
0     1   28  42   54   64       123   128   317 368 456 789
1      1354  
2
3
4     4564
其余位上都没有  直接排序所得
1   28  42  54   64   123 128 317 368 456  789  1254 4564
核心在每一次排序时,实际上是上一位之前已经将同位次上进行排好序
代码如下
int getMaxIdex(int x[], int length)//取得最大数的位数
{
 int max = x[length - 1];
 while (--length>=0)
 {
  max = x[length] > max ? x[length] : max;
 }
 int count = 1;
 while (max / 10 > 0)
 {
  max /= 10;
  count++; }
 return count;
}
void RadixSort(int x[], int length)
{
 int radix = 10;
 int distance = getMaxIdex(x, length);
 int temp = 1, round = 1;//以round位排序
 int bucket[10][10];//以基数为0-9   容量为10保存;
 int *count = new int[radix];
 while (round <= distance)
 {
  for (int i = 0; i < radix; i++)count[i] = 0;
  for (int i = 0; i < length; i++)
  {
   int indix = (x[i] / temp) % radix;
   bucket[indix][count[indix]] = x[i];
   count[indix]++;
  }
  int indix = 0;//原数组指向
  for (int i = 0; i < radix; i++)
  {
   if (count[i] != 0)
    for (int j = 0; j < count[i]; j++)
     x[indix++] = bucket[i][j];//将每一个桶里的数依次放入原数组
  }
  temp *= radix;
  round++;

 }
 delete []count;
}
算法步骤:创建十个桶,并且每个桶的容量为该数组的长度。取得最大数以此取得最大位数,最大位数运用与循环的次数(这里写出一个函数求得最大位)创建一个计数count保存符合位数出现的次数
首先置零(置零操作可在后文将值归还原数组后置零)
第二步   将原数组内的数,进行遍历,取得所求符号位的值,然后将其存入
bucket篮子中,并且记录当前桶内出现的次数,以便下一次正确放入
按此顺序还原至原数组中。然后除数*10;取得更高一位的值;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值