• 常见内部排序


      排序使程序开发中一种常见的操作。将一组杂乱无章的数据按一定的规则将其排列好。使数据得以快速检索到,这就是排序的目的。

    对于一个排序算法,一般可以从下面三个方面去衡量算法的优劣:

    1) 时间复杂度:主要缝隙关键字的比较次数和移动字数;

    2) 空间复杂度:缝隙排序算法中需要多少辅助内存;

    3) 稳定性:当两个关键字相同,如果排序完成后两者的顺序保持不变,则是该排序算法是稳定。

     排序算法大致可以分为内部算法和外部算法。如果整个排序过程不需要借助于外部储存器(磁盘),所有的操作都在内存中完成,则称之为内部算法。如果参与排序树数据元素非常多,数据量非常大,则计算机必须借助外部储存器(如磁盘),这种排序称为外部排序。

    1 内部排序的分类

    一般我们所说排序指的就是内部排序,常见的内部排序又可以分为6大类。

     1 package sort;
     2 
     3 // 定义一个数据包装类
     4 class DataWrap implements Comparable<DataWrap>
     5 {
     6     int data;
     7     String flag;
     8     public DataWrap(int data, String flag)
     9     {
    10         this.data = data;
    11         this.flag = flag;
    12     }
    13     public String toString()
    14     {
    15         return data + flag;
    16     }
    17     // 根据data实例变量来决定两个DataWrap的大小
    18     public int compareTo(DataWrap dw)
    19     {
    20         return this.data > dw.data ? 1
    21             : (this.data == dw.data ? 0 : -1);
    22     }
    23 }

    1.1 选择排序

    选择排序方法有两种,直接选择排序和堆排序。直接选择排序简单直观,但是性能较差;堆排序较为高效,但是实现起来较为复杂。

    1.1.1 直接选择排序

    直接选择排序思想:程序遍历n个数,然后比较出最大的一个与最后一个数进行交换,最大值确定好了;接下来遍历n-1,找个这n-1个数中的最大值并且交换位置,直至n=0位置。

    该排序方法的数据交换次数是,但是比较次数较多。总的时间效率是。空间效率是,并且算法不稳定。

     1 package sort;
     2 
     3 import java.util.*;
     4 
     5 public class SelectSort
     6 {
     7     public static void selectSort(DataWrap[] data)
     8     {
     9         System.out.println("开始排序");
    10         int arrayLength = data.length;
    11         // 依次进行n-1趟比较, 第i趟比较将第i大的值选出放在i位置上。
    12         for (int i = 0; i < arrayLength - 1 ; i++ )
    13         {
    14             // minIndex永远保留本趟比较中最小值的索引
    15             int minIndex = i ;
    16             // 第i个数据只需和它后面的数据比较
    17             for (int j = i + 1 ; j < arrayLength ; j++ )
    18             {
    19                 // 如果第minIndex位置的数据 > j位置的数据
    20                 if (data[minIndex].compareTo(data[j]) > 0)
    21                 {
    22                     // 将j的值赋给minIndex
    23                     minIndex = j;
    24                 }
    25             }
    26             // 每趟比较最多交换一次
    27             if (minIndex != i)
    28             {
    29                 DataWrap tmp = data[i];
    30                 data[i] = data[minIndex];
    31                 data[minIndex] = tmp;
    32             }
    33             System.out.println(java.util.Arrays.toString(data));
    34         }
    35     }
    36     public static void main(String[] args)
    37     {
    38         DataWrap[] data = {
    39             new DataWrap(21 , ""),
    40             new DataWrap(30 , ""),
    41             new DataWrap(49 , ""),
    42             new DataWrap(30 , "*"),
    43             new DataWrap(16 , ""),
    44             new DataWrap(9 , "")
    45         };
    46         System.out.println("排序之前:
    "
    47             + java.util.Arrays.toString(data));
    48         selectSort(data);
    49         System.out.println("排序之后:
    "
    50             + java.util.Arrays.toString(data));
    51     }
    52 }

    1.1.2 堆排序

    堆排序的思路:将数据建立成最大(小)堆,将根节点与最后一个节点交换;重复上面的过程。

    1)堆树是一颗完全二叉树;

    2)堆树中某个节点的值总是不大于或不小于其孩子节点的值;

    3)堆树中每个节点的子树都是堆树。

    当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。如下图所示,左边为最大堆,右边为最小堆。

    引用:https://www.cnblogs.com/zf-blog/p/9010977.html

    堆排序是选择算法的改进,可以减少选择排序中的比较次数,进而减少排序时间。对于堆排序来说,有n个数据,需要进行n-1次建堆,每次建堆本身耗时为,所以其时间效率为,堆排序是不稳定的。

     1 package sort;
     2 
     3 
     4 
     5 public class HeapSort {
     6     public static void heapSort(DataWrap[] data) {
     7         System.out.println("开始排序");
     8         int arrayLength=data.length;
     9         //循环建堆
    10         for(int i=0;i<arrayLength-1;i++) {
    11             //建堆
    12             buildMaxdHeap(data,arrayLength-1-i);
    13             //交换堆与最后一个的数据
    14             swap(data,0,arrayLength-1-i);
    15             System.out.println(java.util.Arrays.toString(data));
    16         }
    17     }
    18 
    19     private static void swap(DataWrap[] data, int i, int j) {
    20         // TODO Auto-generated method stub
    21         DataWrap tmp=data[i];
    22         data[i]=data[j];
    23         data[j]=tmp;
    24         
    25         
    26     }
    27 
    28     private static void buildMaxdHeap(DataWrap[] data, int lastIndex) {
    29         //从lastIndex出节点(最后一个)的父节点开始
    30         for(int i=(lastIndex-1)/2;i>+0;i--) {
    31             //k保存当前正在判断的点
    32             int k=i;
    33             //如果当前K节点的子节点存在
    34             while(k*2+1<=lastIndex) {
    35                 //k节点的左子节点的索引
    36                 int biggerIndex=2*k+1;
    37                 //判断是否存在右子节点
    38                 if(biggerIndex<lastIndex) {
    39                     biggerIndex++;
    40                 }
    41                 if((data[k].compareTo(data[biggerIndex])<0)) {
    42                     swap(data,k,biggerIndex);
    43                     k=biggerIndex;
    44                 }else {
    45                     break;
    46                 }
    47             }
    48         }
    49     }
    50     public static void main(String[] args)
    51     {
    52         DataWrap[] data = {
    53             new DataWrap(21 , ""),
    54             new DataWrap(30 , ""),
    55             new DataWrap(49 , ""),
    56             new DataWrap(30 , "*"),
    57             new DataWrap(21 , "*"),
    58             new DataWrap(16 , ""),
    59             new DataWrap(9 , "")
    60         };
    61         System.out.println("排序之前:
    "
    62             + java.util.Arrays.toString(data));
    63         heapSort(data);
    64         System.out.println("排序之后:
    "
    65             + java.util.Arrays.toString(data));
    66     }
    67 }

    1.2 交换排序

    交换排序的主体操作是对数组中的数据不断的进行交换操作。交换排序主要有冒泡排序和快速排序。

    1.2.1  冒泡排序

    冒泡排序思路:类似于鱼吐泡泡,一个接着一个,如果靠近鱼的泡泡比远离鱼的泡泡大,则两个泡泡就会交换。一次执行,就成了一串有序的泡泡。

    时间效率:;空间效率:;算法是有序的;

     1 package sort;
     2 
     3 public class BubbleSort {
     4     public static void bubbleSort(DataWrap[] data) {
     5         System.out.println("开始排序");
     6         int arrayLength=data.length;
     7         for(int i=0;i<arrayLength-1;i++) {
     8             for(int j=0;j<arrayLength-1-i;j++) {
     9                 if(data[j].compareTo(data[j+1])>0) {
    10                     DataWrap tmp=data[j+1];
    11                     data[j+1]=data[j];
    12                     data[j]=tmp;
    13                     }
    14                 }
    15             System.out.println(java.util.Arrays.toString(data));
    16         }
    17     }
    18 
    19     public static void main(String[] args) {
    20         DataWrap[] data = { new DataWrap(9, ""), new DataWrap(16, ""), new DataWrap(21, "*"), new DataWrap(23, ""),
    21                 new DataWrap(30, ""), new DataWrap(49, ""), new DataWrap(21, ""), new DataWrap(30, "*") };
    22         System.out.println("排序之前:
    " + java.util.Arrays.toString(data));
    23         bubbleSort(data);
    24         System.out.println("排序之后:
    " + java.util.Arrays.toString(data));
    25     }
    26 }

    1.2.2 快速排序

    快速排序的思路:如列队一样,先选择一个士兵为基准,比该士兵高则站在其后面,比该士兵矮则站在其前面,这样大致将队伍分为了两个子列。然后在这两个子进行递归。

    空间效率:,算法并不稳定。

     1 package sort;
     2 import java.util.*;
     3 
     4 
     5 public class QuickSort
     6 {
     7     // 将指定数组的i和j索引处的元素交换
     8     private static void swap(DataWrap[] data, int i, int j)
     9     {
    10         DataWrap tmp;
    11         tmp = data[i];
    12         data[i] = data[j];
    13         data[j] = tmp;
    14     }
    15     // 对data数组中从start~end索引范围的子序列进行处理
    16     // 使之满足所有小于分界值的放在左边,所有大于分界值的放在右边
    17     private static void subSort(DataWrap[] data
    18         , int start , int end)
    19     {
    20         // 需要排序
    21         if (start < end)
    22         {
    23             // 以第一个元素作为分界值
    24             DataWrap base = data[start];
    25             // i从左边搜索,搜索大于分界值的元素的索引
    26             int i = start;
    27             // j从右边开始搜索,搜索小于分界值的元素的索引
    28             int j = end + 1;
    29             while(true)
    30             {
    31                 // 找到大于分界值的元素的索引,或i已经到了end处
    32                 while(i < end && data[++i].compareTo(base) <= 0);
    33                 // 找到小于分界值的元素的索引,或j已经到了start处
    34                 while(j > start && data[--j].compareTo(base) >= 0);
    35                 if (i < j)
    36                 {
    37                     swap(data , i , j);
    38                 }
    39                 else
    40                 {
    41                     break;
    42                 }
    43             }
    44             swap(data , start , j);
    45             // 递归左子序列
    46             subSort(data , start , j - 1);
    47             // 递归右边子序列
    48             subSort(data , j + 1, end);
    49         }
    50     }
    51     public static void quickSort(DataWrap[] data)
    52     {
    53         subSort(data , 0 , data.length - 1);
    54     }
    55     public static void main(String[] args)
    56     {
    57         DataWrap[] data = {
    58             new DataWrap(9 , ""),
    59             new DataWrap(-16 , ""),
    60             new DataWrap(21 , "*"),
    61             new DataWrap(23 , ""),
    62             new DataWrap(-30 , ""),
    63             new DataWrap(-49 , ""),
    64             new DataWrap(21 , ""),
    65             new DataWrap(30 , "*"),
    66             new DataWrap(13 , "*")
    67         };
    68         System.out.println("排序之前:
    "
    69             + java.util.Arrays.toString(data));
    70         quickSort(data);
    71         System.out.println("排序之后:
    "
    72             + java.util.Arrays.toString(data));
    73     }
    74 }

    1.3 插入排序

    插入排序主要分为直接插入排序、Shell排序和折半排序。

    1.3.1 直接插入排序

    直接插入排序思路:类似于把一堆杂乱的且大小不一的碗按从大到小(从小到大不符合实际)排序。我可以先拿出一个碗,然后在拿出碗与之比较;这两个碗排好序后,再拿出一个碗进行比较,插在其合适的顺序上,以此方式直至最后一个碗位置。

    最坏情况的时间复杂度:;空间复杂度:;该算法是稳定的。

     1 package sort;
     2 
     3 public class insertSort {
     4     public static void  insertSort(DataWrap[] data) {
     5         System.out.println("开始排序");
     6         int arrayLenght=data.length;
     7         for(int i=1;i<arrayLenght;i++) {
     8             DataWrap tmp=data[i];
     9             
    10             if(data[i].compareTo(data[i-1])<0){
    11                 int j=i-1;
    12                 for(;j>=0&&data[j].compareTo(tmp)>0;j--) {
    13                     data[j+1]=data[j];
    14                 }
    15                 
    16                 data[j+1]=tmp;
    17             }
    18             System.out.println(java.util.Arrays.toString(data));
    19         }
    20         
    21     }
    22     public static void main(String[] args)
    23     {
    24         DataWrap[] data = {
    25             new DataWrap(9 , ""),
    26             new DataWrap(-16 , ""),
    27             new DataWrap(21 , "*"),
    28             new DataWrap(23 , ""),
    29             new DataWrap(-30 , ""),
    30             new DataWrap(-49 , ""),
    31             new DataWrap(21 , ""),
    32             new DataWrap(30 , "*"),
    33             new DataWrap(13 , "*")
    34         };
    35         System.out.println("排序之前:
    "
    36             + java.util.Arrays.toString(data));
    37         insertSort(data);
    38         System.out.println("排序之后:
    "
    39             + java.util.Arrays.toString(data));
    40     }
    41 }

    1.3.2 折半插入排序

    思路:与二分法有点相似。因为之前的数据是排好序的,因此为了减少比较次数,可以选择之前数据的中点与要插入的数据进行比较。我们拿到一个碗,把它插在已经排好的碗中时,可以先看中间的碗,如果中间的碗比插入的碗大,在进行二分即可。

    算法可能不稳定;

     1 package sort;
     2 
     3 public class BinaryInsertSort
     4 {
     5     public static void binaryInsertSort(DataWrap[] data)
     6     {
     7         System.out.println("开始排序:
    ");
     8         int arrayLength = data.length;
     9         for(int i = 1 ; i < arrayLength ; i++)
    10         {
    11             // 当整体后移时,保证data[i]的值不会丢失
    12             DataWrap tmp = data[i];
    13             int low = 0;
    14             int high = i - 1;
    15             while(low <= high)
    16             {
    17                 // 找出low、high中间的索引
    18                 int mid = (low + high) / 2;
    19                 // 如果tmp值大于low、high中间元素的值
    20                 if(tmp.compareTo(data[mid]) > 0)
    21                 {
    22                     // 限制在索引大于mid的那一半中搜索
    23                     low = mid + 1;
    24                 }
    25                 else
    26                 {
    27                     // 限制在索引小于mid的那一半中搜索
    28                     high = mid - 1;
    29                 }
    30             }
    31             // 将low到i处的所有元素向后整体移一位
    32             for(int j = i ; j > low ; j--)
    33             {
    34                 data[j] = data[j - 1];
    35             }
    36             // 最后将tmp的值插入合适位置
    37             data[low] = tmp;
    38             System.out.println(java.util.Arrays.toString(data));
    39         }
    40     }
    41     public static void main(String[] args)
    42     {
    43         DataWrap[] data = {
    44             new DataWrap(9 , ""),
    45             new DataWrap(-16 , ""),
    46             new DataWrap(21 , "*"),
    47             new DataWrap(23 , ""),
    48             new DataWrap(-30 , ""),
    49             new DataWrap(-49 , ""),
    50             new DataWrap(21 , ""),
    51             new DataWrap(30 , "*"),
    52             new DataWrap(30 , "")
    53         };
    54         System.out.println("排序之前:
    "
    55             + java.util.Arrays.toString(data));
    56         binaryInsertSort(data);
    57         System.out.println("排序之后:
    "
    58             + java.util.Arrays.toString(data));
    59     }
    60 }

    1.3.3 希尔排序

    Shell排序方法思路:以一个较为合适的步长h,让数据中相差为h的步数进行排序;让后将步长改为h/2,在进行排序;递归下去,直至h=1。(当h=1是,可以认为此时的希尔排序算法就是直接插入排序算法)。对于直接插入算法来说,如果原始数据大部分是有序的,数据的移动操作就会较少。利用希尔排序算法就是把原始数据做到基本有序,有效的减少数据移动的操作。

     1 public class ShellSort
     2 {
     3     public static void shellSort(DataWrap[] data)
     4     {
     5         System.out.println("开始排序:");
     6         int arrayLength = data.length;
     7         // h变量保存可变增量
     8         int h = 1;
     9         // 按h * 3 + 1得到增量序列的最大值
    10         while(h <= arrayLength / 3)
    11         {
    12             h = h * 3 + 1;
    13         }
    14         while(h > 0)
    15         {
    16             System.out.println("===h的值:" + h + "===");
    17             for (int i = h ; i < arrayLength ; i++ )
    18             {
    19                 // 当整体后移时,保证data[i]的值不会丢失
    20                 DataWrap tmp = data[i];
    21                 // i索引处的值已经比前面所有值都大,表明已经有序,无需插入
    22                 // (i-1索引之前的数据已经有序的,i-1索引处元素的值就是最大值)
    23                 if (data[i].compareTo(data[i - h]) < 0)
    24                 {
    25                     int j = i - h;
    26                     // 整体后移h格
    27                     for ( ; j >= 0 && data[j].compareTo(tmp) > 0 ; j-=h)
    28                     {
    29                         data[j + h] = data[j];
    30                     }
    31                     // 最后将tmp的值插入合适位置
    32                     data[j + h] = tmp;
    33                 }
    34                 System.out.println(java.util.Arrays.toString(data));
    35             }
    36             h = (h - 1) / 3;
    37         }
    38     }
    39     public static void main(String[] args)
    40     {
    41         DataWrap[] data = {
    42             new DataWrap(9 , ""),
    43             new DataWrap(-16 , ""),
    44             new DataWrap(21 , "*"),
    45             new DataWrap(23 , ""),
    46             new DataWrap(-30 , ""),
    47             new DataWrap(-49 , ""),
    48             new DataWrap(21 , ""),
    49             new DataWrap(30 , "*"),
    50             new DataWrap(30 , ""),
    51         };
    52         System.out.println("排序之前:
    "
    53             + java.util.Arrays.toString(data));
    54         shellSort(data);
    55         System.out.println("排序之后:
    "
    56             + java.util.Arrays.toString(data));
    57     }
    58 }

    1.4 归并排序

    归并排序基本思想:就是将两个有序的序列合成一个新的有序序列。其时间效率为,需要一个与原始序列同样大的辅助序列,因此空间效率较差。算法是稳定的。

      1 class DataWrap implements Comparable<DataWrap>
      2 {
      3     int data;
      4     String flag;
      5     public DataWrap(int data, String flag)
      6     {
      7         this.data = data;
      8         this.flag = flag;
      9     }
     10     public String toString()
     11     {
     12         return data + flag;
     13     }
     14     // 根据data实例变量来决定两个DataWrap的大小
     15     public int compareTo(DataWrap dw)
     16     {
     17         return this.data > dw.data ? 1
     18             : (this.data == dw.data ? 0 : -1);
     19     }
     20 }
     21 public class MergeSort
     22 {
     23     // 利用归并排序算法对数组data进行排序
     24     public static void mergeSort(DataWrap[] data)
     25     {
     26         // 归并排序
     27         sort(data , 0 , data.length - 1);
     28     }
     29     /**
     30      * 将索引从left到right范围的数组元素进行归并排序
     31      * @param data 待排序的数组
     32      * @param left 待排序的数组的第一个元素索引
     33      * @param right 待排序的数组的最后一个元素的索引
     34      */
     35     private static void sort(DataWrap[] data
     36          , int left, int right)
     37     {
     38         if (left < right)
     39         {
     40             // 找出中间索引
     41             int center = (left + right) / 2;
     42             // 对左边数组进行递归
     43             sort(data, left, center);
     44             // 对右边数组进行递归
     45             sort(data, center + 1, right);
     46             // 合并
     47             merge(data, left, center, right);
     48         }
     49     }
     50     /**
     51      * 将两个数组进行归并,归并前两个数组已经有序,归并后依然有序。
     52      * @param data 数组对象
     53      * @param left 左数组的第一个元素的索引
     54      * @param center 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
     55      * @param right 右数组的最后一个元素的索引
     56      */
     57     private static void merge(DataWrap[] data
     58         , int left, int center, int right)
     59     {
     60         // 定义一个与待排序序列长度相同的临时数组
     61         DataWrap[] tmpArr = new DataWrap[data.length];
     62         int mid = center + 1;
     63         // third记录中间数组的索引
     64         int third = left;
     65         int tmp = left;
     66         while (left <= center && mid <= right)
     67         {
     68             // 从两个数组中取出小的放入中间数组
     69             if (data[left].compareTo(data[mid]) <= 0)
     70             {
     71                 tmpArr[third++] = data[left++];
     72             }
     73             else
     74             {
     75                 tmpArr[third++] = data[mid++];
     76             }
     77         }
     78         // 剩余部分依次放入中间数组
     79         while (mid <= right)
     80         {
     81             tmpArr[third++] = data[mid++];
     82         }
     83         while (left <= center)
     84         {
     85             tmpArr[third++] = data[left++];
     86         }
     87         // 将中间数组中的内容复制拷回原数组
     88         // (原left~right范围的内容复制回原数组)
     89         while (tmp <= right)
     90         {
     91             data[tmp] = tmpArr[tmp++];
     92         }
     93     }
     94     public static void main(String[] args)
     95     {
     96         DataWrap[] data = {
     97             new DataWrap(9 , ""),
     98             new DataWrap(-16 , ""),
     99             new DataWrap(21 , "*"),
    100             new DataWrap(23 , ""),
    101             new DataWrap(-30 , ""),
    102             new DataWrap(-49 , ""),
    103             new DataWrap(21 , ""),
    104             new DataWrap(30 , "*"),
    105             new DataWrap(30 , "")
    106         };
    107         System.out.println("排序之前:
    "
    108             + java.util.Arrays.toString(data));
    109         mergeSort(data);
    110         System.out.println("排序之后:
    "
    111             + java.util.Arrays.toString(data));
    112     }
    113 }

     

  • 相关阅读:
    LINUX开发使用的3个远程工具
    NDK 链接第三方静态库的方法
    GMap.NET 显示GIF图标的定制
    从MySQL获取数据
    web2py远程开发
    升级后,使用dynamic报错
    实用的MVVM:ImageView
    node.js 连接 mysql
    vector常见用法
    XCODE中配置使用boost
  • 原文地址:https://www.cnblogs.com/tianliang94/p/10458888.html
Copyright © 2020-2023  润新知