zoukankan      html  css  js  c++  java
  • java数据结构之排序

    写在前面的话:本篇文章是我自己阅读程杰老师的大话数据结构之后自己所做的总结,网上有很多关于排序的详细介绍,我推荐伍迷家园所写的文章,介绍的非常详细。

    排序是我们在程序中经常要用到的一种算法,好的排序可以极大的提高我们的工作效率,本篇主要介绍几种常见的排序算法;

     (简单说明:下面的java程序是用来排序的,其中sum为排序数组,sum[0]不进行排序,只作为哨兵或者临时变量)

    1、冒泡排序:冒泡排序是一种交换排序,其的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

     1 //冒泡排序2 最正宗的冒泡排序,i每循环一次,最小的数就像气泡一样慢慢浮到水面上
     2     public static void BubbleSort_2(int[] num){
     3         for(int i = 1; i < num.length; i++){
     4             for(int j = num.length-1; j > i; j--){
     5                 if(num[j-1] > num[j]){
     6                     Swap(num,j-1,j);    //交换函数  交换num[j-1]和num[j]的值
     7                 }
     8             }
     9         }
    10     }

    2、选择排序:简单选择排序法(Simple Selection Sort)就是通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1≤i≤n)个记录交换之。

     1 //选择排序
     2     public static void SelectSort(int[] num){
     3         int min;
     4         for(int i = 1; i < num.length; i++){
     5             min = i;
     6             for(int j = i + 1; j <num.length; j++){
     7                 if(num[min] > num[j]){
     8                     min = j;
     9                 }
    10             }
    11             if(i != min){
    12                 Swap(num,i,min);        //交换函数  交换num[j-1]和num[j]的值
    13             }
    14         }
    15     }

    3、插入排序:直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。

     1  //插入排序
     2     public static void InsertSort(int[] num){
     3         int j;                             
     4         for(int i = 2; i < num.length; i++){
     5             if(num[i] < num[i-1]){
     6                 num[0] = num[i];
     7                 for( j = i-1; num[j] > num[0]; j--){
     8                     num[j+1] = num[j];          //记录右移
     9                 }
    10                 num[j+1] = num[0]; //插入到正确位置
    11                 num[0] = 0;         //把num[0] 置为0,还回到初始值
    12             }
    13         }
    14     }

    4、希尔排序:我们将原本有大量记录数的记录进行分组。分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了,然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序时,注意只是基本有序时,再对全体记录进行一次直接插入排序。

     1 //希尔排序
     2     public static void ShellSort(int[] num){
     3         int n = num.length; 
     4         int j;
     5         do{
     6             n = n/3 + 1;                    //增量序列
     7             for(int i = n; i < num.length; i++){
     8                 if(num[i] < num[i-n]){          //需要将num[i]插入到有序增量子表中
     9                     num[0] = num[i];
    10                     for(j = i - n; j > 0 && num[0] < num[j];j -= n){
    11                         num[j+n] = num[j];
    12                     }
    13                     num[j+n] = num[0];
    14                 }
    15             }
    16         }while(n > 1);
    17     }

    5、堆排序:堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它与堆数组的末尾元素进行交换,此时末尾元素就是最大值,然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列了。(大顶堆:在完全二叉树中,每个结点的值都大于或等于其左右孩子结点的值)

     1  //堆排序算法   堆排序的时候一定要注意数组的下标,很容发生数组下标越界的情况
     2     public static void HeadSort(int[] num){
     3         for(int i = (num.length-1)/2; i>0; i--){        //将num构建成一个大顶堆
     4         HeapAdjust(num,i,num.length-1);
     5         }
     6         for(int i = num.length-1; i > 1; i--){     //排序num[3]~num[n]  其中数组开始两个元素num[1]和num[2]没有进行交换
     7             Swap(num,1,i);                            //交换函数  交换num[j-1]和num[j]的值
     8             HeapAdjust(num,1,i-1);
     9         }
    10     }
    11     /*本函数是为了将num调整为一个大顶堆,num除了num【1】以外,
    12     其他节点都符合大顶堆的定义,这个函数是为堆排序算法服务的*/
    13     public static void HeapAdjust(int[] num,int start,int stop){            
    14         int i, num_x;
    15         num_x = num[start];
    16         for(i = 2*start; i <= stop; i*=2){       //沿关键字较大的纪录项下筛选
    17             if(i+1 >= num.length){                //没有右孩子节点时,单独判断,否则num[i+1]会越界
    18                 if(num[i/2] < num[i]){
    19                     num[i/2] = num[i];
    20                     start = i;
    21                  break;
    22               }else{
    23                   break;
    24               }
    25             }
    26             if(num[i] < num[i+1] && i < stop){
    27                 ++i;                        //i为关键字较大的记录的下标 
    28             }
    29             if(num_x >= num[i]){                 //不在向下找了,num_x已经是最大的了
    30                 break;
    31             }
    32             num[start] = num[i];
    33             start = i;
    34         }
    35         num[start] = num_x;
    36     }

    6、归并排序:归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归 并,得到⌈n/2⌉(⌈x⌉表示不小于x的最小整数)个长度为2的有序子序列;再两两归并,……     如此重复,直至得到一个长度为n的有序序列为止,这 种排序方法称为2路归并排序。

     1 //归并排序
     2     public static void MergeSort(int[] num){
     3         int[] temp =new int[num.length];
     4         MSort(num,temp ,1,num.length-1);
     5     }
     6     //Msort函数是将list数组进行分割,Merge函数是将分割后的list数组进行排序在组合
     7     public static void MSort(int[] list ,int[] temp,int left,int right){
     8         if(left  == right){
     9             return;   
    10         }else{
    11             int m = (left + right) / 2;
    12             MSort(list,temp,left,m);
    13             MSort(list,temp,m+1,right);
    14             Merge(list,temp,left,m+1,right);
    15         }
    16     }
    17     public static void Merge(int[] list,int[] temp,int left,int mid,int right){
    18         int j = 0;
    19         int left_x = left;
    20         int mid_x = mid -1;
    21         int n = right - left +1;                        //当前temp数组中数的个数
    22         while(left <= mid_x &&  mid <= right){              //左右数组第一个数进行比较  把较小的数如到temp数组中
    23             if(list[left] < list[mid]){
    24                 temp[j++] = list[left++];
    25             }else{
    26                 temp[j++] = list[mid++];
    27             }
    28         }
    29         while(left <= mid_x){                         //如果左边的数比右边的数多,就将剩下的入到temp数组中    j
    30             temp[j++] = list[left++];
    31         }
    32 
    33         while(mid <= right){                           //如果右边打数比左边的数多,就将右边剩下的数加入到temp数组当中去
    34             temp[j++] = list[mid++];
    35         }
    36         
    37         for(j = 0; j < n; j++){             //   将得到的temp数组加到list数组中    
    38             list[left_x+j] = temp[j];
    39         }         
    40     }

     7、快速排序:快速排序(Quick Sort)的基本思想是通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

     1 //快速排序
     2     public static void QuickSort(int[] num){
     3         QSort(num,1,num.length-1);
     4     }
     5     public static void QSort(int[] num, int left, int right){
     6         int mid = Partition(num,left,right);
     7 
     8         QSort(num,left,mid-1);
     9         QSort(num,mid+1,right); 
    10     }
    11     //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
    12     public static int   Partition(int[] num, int left, int right){
    13         int result;
    14         result = num[left];
    15         while(left < right){
    16             while( left < right && num[right] >= result){
    17                 right++;
    18             }
    19             Swap(num,left,right);
    20             while( left < right && num[left] <= result){
    21                 left++;
    22             }
    23             Swap(num,left,right);
    24         }
    25         return left;
    26     }

     8、快速排序优化

     1  //快速排序优化
     2     public static void QuickSort_Op(int[] num){
     3         Qsort_Op(num,1,num.length-1);
     4     }
     5     public static void Qsort_Op(int[] num, int left, int right){
     6         final int N = 7;                //数组长度的阀值  当数组长度低于这个长度是就不在使用快速排序,而是使用插入排序
     7         if((right-left) > N){
     8             //尾递归优化
     9             while(left < right){
    10                 int mid = Partition_Op(num,left,right);
    11         
    12                 Qsort_Op(num, left, mid-1);        //对底子表进行递归排序
    13                 left = mid;            //尾递归
    14             }
    15          }else{              //当right-left小于7时,用直接插入排序
    16                InsertSort(num);  
    17          }
    18    }
    19   //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
    20     public static int Partition_Op(int[] num, int left, int right){
    21         int result;
    22 
    23         //优化选取轴承    三数取中(取左端、右端,中间三个),进行排序,取中间数作为轴承
    24         int m = left + (right - left);
    25         if(num[left] > num[right]){
    26             Swap(num,left,right);
    27         }
    28         if(num[m] > num[right]){
    29             Swap(num,m,right);
    30         }
    31         if(num[left] < num[m]){
    32             Swap(num,left,m);
    33         }
    34         result  = num[left];         //y用子表的第一个记录作为轴承记录
    35         num[0] = result;            //将轴承关键字备份到num[0]
    36         while( left < right ){
    37             while(left < right && num[right] > result){
    38                 right--;
    39             }
    40             num[left] = num[right];  //选择替换而不是交换方式进行
    41             while(left < right && num[left] < result){
    42                 left++;
    43             }
    44             num[right] = num[left];         //选择替换而不是交换方式进行操作
    45         }
    46         num[left] = num[0];
    47         return left;
    48     }

     下面代码包含了上述所有排序算法,是一段总结代码:

      1 /**
      2  * 2016/5/3
      3  * 排序算法大总结
      4  * 说明:本java程序是用来排序的,其中sum为排序数组,sum[0]不进行排序,只作为哨兵或者临时变量
      5  */
      6 package cn.Link;
      7 public class Sort{
      8     //测试主函数
      9     public static void main(String[] args){
     10         int n = 20;         //数组长度
     11         int[] num = new int[n];     //要排序的数组
     12         num[0] = 0;             //数组首位不进行排序操作,仅作为临时变量或者是哨兵
     13         for(int i = 1; i < n; i ++){
     14             num[i] = (int)(Math.random()*100);  //随机生成一个数作为数组元素
     15         }
     16 
     17         System.out.println("排序前的数组:");
     18         Print_num(num);
     19 
     20         //冒泡排序1
     21         int[] num_1 = (int[]) num.clone();        //将num数组全部复制给num_1数组
     22         BubbleSort_1(num_1);
     23         System.out.print("
    冒泡排序1排序后的结果");
     24         Print_num(num_1);
     25 
     26         //冒泡排序2
     27         int[] num_2 = (int[]) num.clone();        //将num数组全部复制给num_2数组
     28         BubbleSort_2(num_2);
     29         System.out.print("
    冒泡排序2排序后的结果:");
     30         Print_num(num_2);
     31 
     32         //冒泡排序3 对冒泡排序2的优化
     33         int[] num_3 = (int[]) num.clone();        //将num数组全部复制给num_3数组
     34         BubbleSort_3(num_3);
     35         System.out.print("
    冒泡排序3排序后的结果:");
     36         Print_num(num_3);
     37 
     38         //选择排序
     39         int[] num_4 = (int[]) num.clone();        //将num数组全部复制给num_4数组
     40         SelectSort(num_4);
     41         System.out.print("
    选择排序排序后的结果:");
     42         Print_num(num_4);
     43 
     44         //插入排序
     45         int[] num_5 = (int[])num.clone();        //将num数组全部复制给num_5数组
     46         InsertSort(num_5);
     47         System.out.print("
    插入排序排序后的结果");
     48         Print_num(num_5);
     49         
     50         //希尔排序
     51         int[] num_6 = (int[])num.clone();        //将num数组全部复制给num_6数组
     52         InsertSort(num_6);
     53         System.out.print("
    希尔排序排序后的结果");
     54         Print_num(num_6);
     55 
     56         //堆排序
     57         int[] num_7 = (int[])num.clone();        //将num数组全部复制给num_7数组
     58         HeadSort(num_7);
     59         System.out.print("
    堆排序排序后的结果");
     60         Print_num(num_7);
     61         
     62         //堆排序
     63         int[] num_8 = (int[])num.clone();        //将num数组全部复制给num_8数组
     64         MergeSort(num_8);
     65         System.out.print("
    归并排序排序后的结果");
     66         Print_num(num_8);
     67         
     68         //快速排序
     69         int[] num_9 = (int[])num.clone();        //将num数组全部复制给num_8数组
     70         MergeSort(num_9);
     71         System.out.print("
    快速排序排序后的结果");
     72         Print_num(num_9);
     73         
     74       //快速排序优化
     75         int[] num_10 = (int[])num.clone();        //将num数组全部复制给num_8数组
     76         MergeSort(num_10);
     77         System.out.print("
    快速排序优化后排序后结果");
     78         Print_num(num_10);
     79 
     80     }
     81 
     82     //交换数组中的两个数
     83     public static void Swap(int[]num,int i,int j){
     84         int num_x;
     85         num_x = num[i];
     86         num[i] = num[j];
     87         num[j] = num_x;
     88     }
     89 
     90     //遍历输出一个数组
     91     public static void Print_num(int[] num){
     92         System.out.println();
     93         for(int u:num){            //for-each 
     94             System.out.print(u+" ");
     95         }
     96     }
     97 
     98     //冒泡排序1  最简单的两两比较,反序则交换的排序
     99     public static void BubbleSort_1(int[]num){
    100         for(int i = 1; i < num.length; i++){
    101             for(int j = i+1; j < num.length; j++){
    102                 if(num[i] > num[j]){
    103                     Swap(num,i,j);
    104                 }
    105             }
    106         }
    107     }
    108 
    109     //冒泡排序2 最正宗的冒泡排序,i每循环一次,最小的数就像气泡一样慢慢浮到水面上
    110     public static void BubbleSort_2(int[] num){
    111         for(int i = 1; i < num.length; i++){
    112             for(int j = num.length-1; j > i; j--){
    113                 if(num[j-1] > num[j]){
    114                     Swap(num,j-1,j);    //交换函数  交换num[j-1]和num[j]的值
    115                 }
    116             }
    117         }
    118     }
    119 
    120     //冒泡排序3  对冒泡排序的优化
    121     public static void BubbleSort_3(int[] num){
    122         boolean  flag = true;
    123         for(int i = 1; i < num.length && flag; i++){
    124             flag = false;                   //flag初始化为假
    125             for(int j = num.length-1; j>i ; j--){
    126                 if(num[j-1] > num[j]){
    127                     Swap(num,j-1,j);        //交换函数  交换num[j-1]和num[j]的值
    128                     flag = true;
    129                 }
    130             }
    131         }
    132     }
    133 
    134     //选择排序
    135     public static void SelectSort(int[] num){
    136         int min;
    137         for(int i = 1; i < num.length; i++){
    138             min = i;
    139             for(int j = i + 1; j <num.length; j++){
    140                 if(num[min] > num[j]){
    141                     min = j;
    142                 }
    143             }
    144             if(i != min){
    145                 Swap(num,i,min);        //交换函数  交换num[j-1]和num[j]的值
    146             }
    147         }
    148     }
    149 
    150     //插入排序
    151     public static void InsertSort(int[] num){
    152         int j;                             
    153         for(int i = 2; i < num.length; i++){
    154             if(num[i] < num[i-1]){
    155                 num[0] = num[i];
    156                 for( j = i-1; num[j] > num[0]; j--){
    157                     num[j+1] = num[j];          //记录右移
    158                 }
    159                 num[j+1] = num[0]; //插入到正确位置
    160                 num[0] = 0;         //把num[0] 置为0,还回到初始值
    161             }
    162         }
    163     }
    164     //希尔排序
    165     public static void ShellSort(int[] num){
    166         int n = num.length; 
    167         int j;
    168         do{
    169             n = n/3 + 1;                    //增量序列
    170             for(int i = n; i < num.length; i++){
    171                 if(num[i] < num[i-n]){          //需要将num[i]插入到有序增量子表中
    172                     num[0] = num[i];
    173                     for(j = i - n; j > 0 && num[0] < num[j];j -= n){
    174                         num[j+n] = num[j];
    175                     }
    176                     num[j+n] = num[0];
    177                 }
    178             }
    179         }while(n > 1);
    180     }
    181 
    182     //堆排序算法   堆排序的时候一定要注意数组的下标,很容发生数组下标越界的情况
    183     public static void HeadSort(int[] num){
    184         for(int i = (num.length-1)/2; i>0; i--){        //将num构建成一个大顶堆
    185         HeapAdjust(num,i,num.length-1);
    186         }
    187         for(int i = num.length-1; i > 1; i--){     //排序num[3]~num[n]  其中数组开始两个元素num[1]和num[2]没有进行交换
    188             Swap(num,1,i);                            //交换函数  交换num[j-1]和num[j]的值
    189             HeapAdjust(num,1,i-1);
    190         }
    191     }
    192     /*本函数是为了将num调整为一个大顶堆,num除了num【1】以外,
    193     其他节点都符合大顶堆的定义,这个函数是为堆排序算法服务的*/
    194     public static void HeapAdjust(int[] num,int start,int stop){            
    195         int i, num_x;
    196         num_x = num[start];
    197         for(i = 2*start; i <= stop; i*=2){       //沿关键字较大的纪录项下筛选
    198             if(i+1 >= num.length){                //没有右孩子节点时,单独判断,否则num[i+1]会越界
    199                 if(num[i/2] < num[i]){
    200                     num[i/2] = num[i];
    201                     start = i;
    202                  break;
    203               }else{
    204                   break;
    205               }
    206             }
    207             if(num[i] < num[i+1] && i < stop){
    208                 ++i;                        //i为关键字较大的记录的下标 
    209             }
    210             if(num_x >= num[i]){                 //不在向下找了,num_x已经是最大的了
    211                 break;
    212             }
    213             num[start] = num[i];
    214             start = i;
    215         }
    216         num[start] = num_x;
    217     }
    218 
    219 
    220     //归并排序
    221     public static void MergeSort(int[] num){
    222         int[] temp =new int[num.length];
    223         MSort(num,temp ,1,num.length-1);
    224     }
    225     //Msort函数是将list数组进行分割,Merge函数是将分割后的list数组进行排序在组合
    226     public static void MSort(int[] list ,int[] temp,int left,int right){
    227         if(left  == right){
    228             return;   
    229         }else{
    230             int m = (left + right) / 2;
    231             MSort(list,temp,left,m);
    232             MSort(list,temp,m+1,right);
    233             Merge(list,temp,left,m+1,right);
    234         }
    235     }
    236     public static void Merge(int[] list,int[] temp,int left,int mid,int right){
    237         int j = 0;
    238         int left_x = left;
    239         int mid_x = mid -1;
    240         int n = right - left +1;                        //当前temp数组中数的个数
    241         while(left <= mid_x &&  mid <= right){              //左右数组第一个数进行比较  把较小的数如到temp数组中
    242             if(list[left] < list[mid]){
    243                 temp[j++] = list[left++];
    244             }else{
    245                 temp[j++] = list[mid++];
    246             }
    247         }
    248         while(left <= mid_x){                         //如果左边的数比右边的数多,就将剩下的入到temp数组中    j
    249             temp[j++] = list[left++];
    250         }
    251 
    252         while(mid <= right){                           //如果右边打数比左边的数多,就将右边剩下的数加入到temp数组当中去
    253             temp[j++] = list[mid++];
    254         }
    255         
    256         for(j = 0; j < n; j++){             //   将得到的temp数组加到list数组中    
    257             list[left_x+j] = temp[j];
    258         }         
    259     }
    260 
    261     //快速排序
    262     public static void QuickSort(int[] num){
    263         QSort(num,1,num.length-1);
    264     }
    265     public static void QSort(int[] num, int left, int right){
    266         int mid = Partition(num,left,right);
    267 
    268         QSort(num,left,mid-1);
    269         QSort(num,mid+1,right); 
    270     }
    271     //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
    272     public static int   Partition(int[] num, int left, int right){
    273         int result;
    274         result = num[left];
    275         while(left < right){
    276             while( left < right && num[right] > result){
    277                 right++;
    278             }
    279             Swap(num,left,right);
    280             while( left < right && num[left] < result){
    281                 left++;
    282             }
    283             Swap(num,left,right);
    284         }
    285         return left;
    286     }
    287 
    288     //快速排序优化
    289     public static void QuickSort_Op(int[] num){
    290         Qsort_Op(num,1,num.length-1);
    291     }
    292     public static void Qsort_Op(int[] num, int left, int right){
    293         final int N = 7;                //数组长度的阀值  当数组长度低于这个长度是就不在使用快速排序,而是使用插入排序
    294         if((right-left) > N){
    295             //尾递归优化
    296             while(left < right){
    297                 int mid = Partition_Op(num,left,right);
    298         
    299                 Qsort_Op(num, left, mid-1);        //对底子表进行递归排序
    300                 left = mid;            //尾递归
    301             }
    302          }else{              //当right-left小于7时,用直接插入排序
    303                InsertSort(num);  
    304          }
    305    }
    306   //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
    307     public static int Partition_Op(int[] num, int left, int right){
    308         int result;
    309 
    310         //优化选取轴承    三数取中(取左端、右端,中间三个),进行排序,取中间数作为轴承
    311         int m = left + (right - left);
    312         if(num[left] > num[right]){
    313             Swap(num,left,right);
    314         }
    315         if(num[m] > num[right]){
    316             Swap(num,m,right);
    317         }
    318         if(num[left] < num[m]){
    319             Swap(num,left,m);
    320         }
    321         result  = num[left];         //y用子表的第一个记录作为轴承记录
    322         num[0] = result;            //将轴承关键字备份到num[0]
    323         while( left < right ){
    324             while(left < right && num[right] > result){
    325                 right--;
    326             }
    327             num[left] = num[right];  //选择替换而不是交换方式进行
    328             while(left < right && num[left] < result){
    329                 left++;
    330             }
    331             num[right] = num[left];         //选择替换而不是交换方式进行操作
    332         }
    333         num[left] = num[0];
    334         return left;
    335     }
    336 
    337 }
  • 相关阅读:
    mysql 数据类型学习笔记(持续更新)
    datetime 和 timestamp 的区别
    Jupyter notebook 常用快捷键(持续更新)
    遍历SnoMed的multiHierarchy中给定概念的子概念
    Ramdom Walk with Restart
    矩阵和向量
    power-law
    一些SQL操作(收集)
    MySQL5.7.19-win64安装启动
    OO_UNIT1_SUMMARY
  • 原文地址:https://www.cnblogs.com/snail-lb/p/5453607.html
Copyright © 2011-2022 走看看