返回顶部

算法-排序法归类

  ◎ 排序定义:排序算法几乎是最为常见的算法,其目的是将一串不规则的数据按照递增递减的方式重新排列,使得数据具有某种顺序关系。用以排序的依据称为键或键值,键值的数据类型有数值类型、中文字符串类型以及非中文字符串类型。

  ◎ 对象分类:数值类型排序就直接以数值的大小作为键值的大小比较的依据。中文字符串类型就按照该中文字符串从左到右逐字比较,并以该中文内码的编码顺序作为键值大小比较的依据。如果为非中文字符串依然按照该字符串从左到右逐字比较,但时以ASCⅡ码的编码顺序作为键值大小的比较依据。

  ◎ 移动方式:数据的移动方式分为“直接移动”和“逻辑移动”两种。“直接移动”是直接交换储存数据的位置,而“逻辑移动”则是仅改变指向这些数据的辅助指针的值。

  ◎ 排序种类(传送门 ↓)

        ▣ 冒泡排序法:一次扫描确保一个元素的位置

        ▣ 选择排序法:找到最小放到前面

        ▣ 插入排序法:扑克牌式排序

        ▣ 希尔排序法:加强版插入排序法

        ▣ 合并排序法(归并排序):

        ▣ 快速排序法:已知的地表最强

 

1.1冒泡排序法

  1.1.1 冒泡排序法原理

  从第一个元素开始,比较相邻元素的大小,若大小顺序有误,则对调在进行下一个元素的比较,就如同气泡从水底逐渐升到水面上的一样。这样扫描一遍后可确保最后的一个元素位于正确的顺序,接着逐步进行第二次扫描,直到所有元素的排序完成为止。

 

  1.1.2 冒泡排序法例子

  设待排序列中有 n 个对象, 首先比较对象v[n-1]和v[n-2], 如果v[n-1] < v[n-2],则交换v[n-1]和v[n-2],然后对v[n-i]和v[n-i-1]进行同样操作,知道对v[1]和v[0]进行完操作,每一次冒泡,使得值小的对象前移动,如此重复,直至有序,图解如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int main(){
 5     int i,j,tmp;
 6     int data[6]={20,37,50,35,10,1};
 7     printf("冒泡排序法的原始数据:\n");
 8     
 9     for(i=0;i<6;i++)
10         printf("%3d",data[i]);
11     printf("\n");
12     
13     for(i=0;i<5;i++){     // 冒泡次数(n-1)
14         for(j=5;j>i;j--){ // 冒泡方向,可以从左向右,也可以相反
15             if(data[j]<data[j-1]){  // 冒泡顺序,可以有小到大,也可以相反
16                 tmp=data[j];
17                 data[j]=data[j-1];
18                 data[j-1]=tmp;
19             }
20         }
21         printf("第%d次排序的结果:",i);
22         for(j=0;j<6;j++)
23             printf("%3d",data[j]);
24         printf("\n");
25     }
26     printf("最后排序的结果:");
27     for(i=0;i<6;i++)
28         printf("%3d",data[i]);
29     printf("\n");    
30 }

  输出:

冒泡排序法的原始数据:
 20 37 50 35 10  1
第0次排序的结果:  1 20 37 50 35 10
第1次排序的结果:  1 10 20 37 50 35
第2次排序的结果:  1 10 20 35 37 50
第3次排序的结果:  1 10 20 35 37 50
第4次排序的结果:  1 10 20 35 37 50
最后排序的结果:   1 10 20 35 37 50

 

1.2 选择排序法

  1.2.1 选择排序法原理  

  选择排序法也算是枚举法的应用,就是反复从未排序的数列中取出最小的元素,加入到另一个数列中,最后的结果即为已排序的数列。

  具体步骤如下:

    1、在一个数据列表中找到最小的那个元素,将它和列表的第一个元素交换位置。

    2、在第二个元素位置开始再次寻找最小的那个元素,然后和列表的第二个位置的元素交换。

    3、在第三个元素位置开始再次寻找最小的那个元素,然后和列表的第三个位置的元素交换

    4、如此反复,直到开始查找起始位置到达列表末尾。

  1.2.2 选择排序法例子

  

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void showdata(int data[]){
 5     int i;
 6     for(i=0;i<6;i++)
 7         printf("%3d",data[i]);
 8     printf("\n");
 9 }
10 
11 void Select(int data[]){
12     int i,j,tmp;
13     for(i=0;i<5;i++){  // 排序次数:n-1
14         for(j=i+1;j<6;j++){  // 扫描范围与方向设定
15             if(data[i]>data[j]){
16                 tmp=data[i];
17                 data[i]=data[j];
18                 data[j]=tmp;
19             }
20         }
21         showdata(data);
22     }
23 }
24 
25 int main(){
26     int data[6]={4,1,3,6,2,5};
27     printf("选择排序法的原始数据:\n");
28     
29     showdata(data);
30     printf("--------------------\n");
31     Select(data);
32     printf("最终排序结果为:");
33     showdata(data);
34     return 0;
35 }

   输出:

选择排序法的原始数据:
  4  1  3  6  2  5
--------------------
  1  4  3  6  2  5
  1  2  4  6  3  5
  1  2  3  6  4  5
  1  2  3  4  6  5
  1  2  3  4  5  6
最终排序结果为:  1  2  3  4  5  6

 

 1.3插入排序法

  1.2.1 插入排序法原理 

  插入排序法是将数组中的元素逐一与已拍好的数据进行比较,先将前两数据先排好,再将第三个元素插入到适当位置,也就是说逐步的形成了一排序好的数列,而其余的元素逐一插入,直到排序完成。

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

  1.2.1 插入排序法例子 

 1 void inser(int data[]){
 2     int i,j,tmp;
 3     for(i=1;i<6;i++){   // 扫描次数(n-1)
 4         tmp=data[i];    // 选中当前元素
 5         j=i-1;            // 定位已排好数列大小
 6         while(j>=0 && tmp<data[j]){  //循环中比当前元素大的后推
 7             data[j+1]=data[j];
 8             j--;
 9         }
10         data[j+1]=tmp;  // 将前元素插入数列中
11         printf("第%d次扫描",i);
12         showdata(data);
13     }
14 }

  输出:

插入排序法的原始数据:
 53 27 36 15 69 42
--------------------
第1次扫描 27 53 36 15 69 42
第2次扫描 27 36 53 15 69 42
第3次扫描 15 27 36 53 69 42
第4次扫描 15 27 36 53 69 42
第5次扫描 15 27 36 42 53 69
最终排序结果为: 15 27 36 42 53 69

 

1.4 希尔排序法

  1.4.1 希尔排序法原理

  又称为增量排序,它是一种插入排序,它是直接插入排序算法的一种威力加强版。它的基本思想是:把记录按步长 gap(差距,间隙) 分组,对每组记录采用直接插入排序方法进行排序。 随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,则完成排序。

  套路代码:

void Shellsort(ElementType A[],int N){
    int i,j,Increment;
    ElementType Tmp;
    for( Increment = N/2; Increment >0; Increment /=2){
        for( i=Increment; i<N; i++ ){
            Tmp=A[i];
            for( j=i; j>=Increment; j-=Increment ){
                if( Tmp<A[j-Increment])
                    A[j]=A[j-Increment];
                else
                    break;
            }
            A[j]=Tmp;
             }
    }
} 

  1.4.2 希尔排序法例子

 

void Shellsort(int A[],int N){
    int i,j,Increment; //步长
    int Tmp; // 数据暂存
    for( Increment = N/2; Increment >0; Increment /=2){ //控制步长选择N/2增量;增量h1为1最小;每次排序后步长变小
        for( i=Increment; i<N; i++ ){  // 分组排序
            Tmp=A[i]; 
            for( j=i; j>=Increment; j-=Increment ){ //插入排序法
                if( Tmp<A[j-Increment])
                    A[j]=A[j-Increment];
                else
                    break;
            }
            A[j]=Tmp;
        }
        showdata(A,10);
    }
}

  输出:

插入排序法的原始数据:
  9  1  2  5  7  4  8  6  3  5
--------------------
第1次排序  4  1  2  3  5  9  8  6  5  7
------------------------------
第2次排序  2  1  4  3  5  6  5  7  8  9
------------------------------
第3次排序  1  2  3  4  5  5  6  7  8  9
------------------------------
最终排序结果为:  1  2  3  4  5  5  6  7  8  9

 

1.5 合并排序法

  1.5.1 合并排序法原理

  合并排序法以O(NlogN)最坏情形运行时间运行,而所使用的比较次数几乎是最优的。算法是采用分治法的一个非常典型的应用,且各层分治递归可以同时进行。合并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。步骤如下:(搭配动图理解)

  1、将N的键值组分成个含N/2个元素的子键值组。

  2、将N个长度的为1的键值成对地合并成N/2个长度为2的键值组。

  3、将N/2个长度的键值组成对地合并为N/4个长度为4的键值组。

  4、将键值组不断地合并,直到合并成一组长度为N的键值组为止。

  1.5.2 合并排序法例子

  以上面动图的数据为例:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void showdata(int data[],int size){  // 展示数组
 5     int i;
 6     for(i=0;i<size;i++)
 7         printf("%3d",data[i]);
 8     printf("\n");
 9 }
10 
11 // 主程序:用于判断两个输入键值组的排序
12 void Merge(int A[],int TmpArray[],int Lpos,int Rpos,int RightEnd){
13     int i,LeftEnd,NumElements,TmpPos;
14     
15     LeftEnd = Rpos-1; // 键值组1的左边界
16     TmpPos = Lpos; 
17     NumElements = RightEnd -Lpos + 1; // 元素总数
18     
19     while(Lpos <= LeftEnd && Rpos <= RightEnd) // 排序
20         if(A[Lpos] <=A[Rpos])
21             TmpArray[TmpPos++]=A[Lpos++];
22         else
23             TmpArray[TmpPos++]=A[Rpos++];
24         
25     while(Lpos<=LeftEnd)  // 将可能剩余的元素直接排序
26         TmpArray[TmpPos++]=A[Lpos++];
27     while(Rpos<=RightEnd)
28         TmpArray[TmpPos++]=A[Rpos++];
29         
30     for(i=0;i<NumElements;i++,RightEnd--) // 把已经完成排序的数组放回
31         A[RightEnd]=TmpArray[RightEnd];
32 }
33 
34 
35 void Msort(int A[],int TmpArray[],int Left,int Right){
36     int Center;  // 本次用的N/2,所以需要找中间值
37     
38     if(Left<Right){  // 递归结束条件,将数组分为N个大小为1的键值组
39         Center=(Left+Right)/2;
40         Msort(A,TmpArray,Left,Center);  // 递归,将数组分解
41         Msort(A,TmpArray,Center+1,Right);
42         Merge(A,TmpArray,Left,Center+1,Right); // 当递归过程开始回归时开始排序
43         showdata(A,8);
44     }
45 }
46 
47 void Mergesort(int A[],int N){  // 算法驱动程序
48     int *TmpArray;  // 定义一个指针,用于暂存数据
49         
50     TmpArray=malloc(N*sizeof(int)); // 申请内容空间与输入数组一样大小
51     if(TmpArray!=NULL){
52         Msort(A,TmpArray,0,N-1);  // 开始~
53         free(TmpArray);  // 释放内存空间
54     }
55     else
56         printf("Error!");
57 }
58 
59 
60 int main(){
61     int data[8]={6,5,3,1,8,7,2,4};
62     printf("插入排序法的原始数据:\n");
63     
64     showdata(data,8);
65     printf("--------------------\n");
66     Mergesort(data,8);
67     printf("最终排序结果为:");
68     showdata(data,8);
69     return 0;

  输出:比较动图与实际输出关系,更好理解递归的过程。

插入排序法的原始数据:
  6  5  3  1  8  7  2  4
--------------------
  5  6  3  1  8  7  2  4
  5  6  1  3  8  7  2  4
  1  3  5  6  8  7  2  4
  1  3  5  6  7  8  2  4
  1  3  5  6  7  8  2  4
  1  3  5  6  2  4  7  8
  1  2  3  4  5  6  7  8
最终排序结果为:  1  2  3  4  5  6  7  8

 

 1.6 快速排序法

  1.6.1 快速排序法原理

   顾名思义,快速排序法是在实践中最快的已知排序算法,它的平均运行时间是O(NlogN)。也是使用了分治法的方式,会先在数据中找到一个虚拟的中间值(枢纽元pivot),并按照这个中间值将所有的数据分成两部分,其中小于中间值的放在左边,而大于中间值的则放在右边,再以同样的方法分别处理左右两边的数据,直到排序完为止。

  a、确认列表第一个数据为中间值,第一个值看成空缺(低指针空缺)。

  b、然后在剩下的队列中,看成有左右两个指针(高低)。

  c、开始高指针向左移动,如果遇到小于中间值的数据,则将这个数据赋值到低指针空缺,并且将高指针的数据看成空缺值(高指针空缺)。然后先向右移动一下低指针,并且切换低指针移动。

  d、当低指针移动到大于中间值的时候,赋值到高指针空缺的地方。然后先高指针向左移动,并且切换高指针移动。重复c、d操作。

  e、直到高指针和低指针相等时退出,并且将中间值赋值给对应指针位置。

  f、然后将中间值的左右两边看成行的列表,进行快速排序操作。

   1.6.2 快速排序法例子

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void showdata(int data[],int size){  // 展示数组
 5     int i;
 6     for(i=0;i<size;i++)
 7         printf("%3d",data[i]);
 8     printf("\n");
 9 }
10 
11 void Quick_Sort(int *arr, int begin, int end){
12     if(begin > end)  // 递归退出条件
13         return;
14     int tmp = arr[begin];  // 选择枢纽元
15     int i = begin;
16     int j = end;
17     while(i != j){
18         while(arr[j] >= tmp && j > i) // 左右指针移动,寻找是否有需要移动的元素
19             j--;
20         while(arr[i] <= tmp && j > i)
21             i++;
22         if(j > i){
23             int t = arr[i];  //交换位置
24             arr[i] = arr[j];
25             arr[j] = t;
26         }
27     }
28     printf("未交换枢纽元位置");
29     showdata(arr,9);
30     arr[begin] = arr[i];   // 交换枢纽元位置
31     arr[i] = tmp;
32     printf("已交换枢纽元位置");
33     showdata(arr,9);
34     printf("枢纽元:%d\n",tmp);
35     Quick_Sort(arr, begin, i-1);
36     Quick_Sort(arr, i+1, end);
37 }
38 
39 int main(){
40     int data[9]={3,5,8,1,2,9,4,7,6};
41     printf("插入排序法的原始数据:\n");
42     
43     showdata(data,9);
44     printf("--------------------\n");
45     Quick_Sort(data,0,8);
46     printf("最终排序结果为:");
47     showdata(data,9);
48     return 0;
49 }

   输出:

插入排序法的原始数据:
  3  5  8  1  2  9  4  7  6
--------------------
未交换枢纽元位置  3  2  1  8  5  9  4  7  6
已交换枢纽元位置  1  2  3  8  5  9  4  7  6
枢纽元:3
未交换枢纽元位置  1  2  3  8  5  9  4  7  6
已交换枢纽元位置  1  2  3  8  5  9  4  7  6
枢纽元:1
未交换枢纽元位置  1  2  3  8  5  9  4  7  6
已交换枢纽元位置  1  2  3  8  5  9  4  7  6
枢纽元:2
未交换枢纽元位置  1  2  3  8  5  6  4  7  9
已交换枢纽元位置  1  2  3  7  5  6  4  8  9
枢纽元:8
未交换枢纽元位置  1  2  3  7  5  6  4  8  9
已交换枢纽元位置  1  2  3  4  5  6  7  8  9
枢纽元:7
未交换枢纽元位置  1  2  3  4  5  6  7  8  9
已交换枢纽元位置  1  2  3  4  5  6  7  8  9
枢纽元:4
未交换枢纽元位置  1  2  3  4  5  6  7  8  9
已交换枢纽元位置  1  2  3  4  5  6  7  8  9
枢纽元:5
未交换枢纽元位置  1  2  3  4  5  6  7  8  9
已交换枢纽元位置  1  2  3  4  5  6  7  8  9
枢纽元:6
未交换枢纽元位置  1  2  3  4  5  6  7  8  9
已交换枢纽元位置  1  2  3  4  5  6  7  8  9
枢纽元:9
最终排序结果为:  1  2  3  4  5  6  7  8  9

 

 

 

posted @ 2020-07-03 18:40  郑育  阅读(298)  评论(0编辑  收藏  举报