排序总结

  1.概念 

排序 就是一串记录,按照其中的某个或者关键字的大小,递增或者递减的排列起来的操作。

  1.1.稳定性

     两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序
算法。
生活中,价格,生产日期等许多都会用到排序。

  2.七大排序总览

常见的排序算法  1.插入排序(直接插入排序和希尔排序)

                           2.选择排序(选择排序和堆排序)

                           3.交换排序(冒泡排序和快速排序)

                           4.归并排序

部分详解  

一.插入排序

    1.1 直接插入排序-原理

 排序数字被看成两个部分   有序区间和无序区间

   每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入。

    1.2代码实现

    

public static void insertSort(int[] array)
 {
   for (int i = 1; i < array.length; i++)
  {  
// 有序区间: [0, i) // 无序区间: [i, array.length) 
    int v = array[i]; 
// 无序区间的第一个数 int j = i - 1; // 不写 array[j] == v 是保证排序的稳定性
 for (; j >= 0 && array[j] > v; j--) 
     { 
    array[j + 1] = array[j]; 
           }
      array[j + 1] = v; 
         }
       }

1.3性能分析

   时间复杂度

  最好           最坏         平均                   空间复杂度

 O(n)         O(n^2)        O(n^2)                   O(1)

数据有序   数据逆序

稳定性:稳定    

插入排序  数据有接近有序,效率越高。

2.希尔排序

   希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所
有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达 =1
时,所有记录在统一组内排好序。
1. 希尔排序是对直接插入排序的优化。
2. gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很
快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

2.2代码实现

public static void shellSort(int[] array) {
     int gap = array.length; while (gap > 1) { 
   insertSortGap(array, gap); 
        gap = (gap / 3) + 1; // OR gap = gap / 2;
                }
          insertSortGap(array, 1); 
                     }
private static void insertSortGap(int[] array, int gap) {
 for (int i = 1; i < array.length; i++) { 
       int v = array[i]; int j = i - gap; 
      for (; j >= 0 && array[j] > v; j -= gap) { 
          array[j + gap] = array[j];
              }array[j + gap] = v; 
                  }  
                    }

2.3性能分析

时间复杂度                                                       空间复杂度

最好          最坏           平均

O(n)      O(n^2)        O(n^1.3)                        O(1)

数据有序

稳定性:不稳定

二.选择排序 

   1.1直接选择排序-原理

每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元
素排完 。

1.2代码实现

public static void selectSort(int[] array) { 
    for (int i = 0; i < array.length - 1; i++) { 
    // 无序区间: [0, array.length - i) // 有序区间: [array.length - i, array.length) 
       int max = 0; 
        for (int j = 1; j < array.length - i; j++) 
          { 
           if (array[j] > array[max])
               { 
             max = j; 
        } 
  }
          int t = array[max]; 
       array[max] = array[array.length - i - 1];
    array[array.length - i - 1] = t; 
   } 
}

1.3性能分析

    时间复杂度                                               空间复杂度

   O(n^2)                                                        O(1)

数据不敏感                                                    数据不敏感

稳定性:不稳定

2.1堆排序

   基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的
数。
注意: 排升序要建大堆;排降序要建小堆。

2.2代码实现

   

     public static void heapSort(int[] array) { 
          createHeap(array); 
     for (int i = 0; i < array.length - 1; i++) { 
 // 交换前 // 无序区间: [0, array.length - i) // 有序区间: [array.length - i, array.length) 
          swap(array, 0, array.length - 1); 
// 交换后 // 无序区间: [0, array.length - i - 1) // 有序区间: [array.length - i - 1, array.length) // 无序区间长度: array.length - i - 1
       shiftDown(array, array.length - i - 1, 0); 
     } 
}
      private void swap(int[] array, int i, int j) 
     {  
       int t = array[i]; array[i] = array[j]; array[j] = t;
       }
           private void createHeap(int[] array) { 
                for (int i = (array.length - 1) / 2; i >= 0; i--) 
     { 
              shiftDown(array, array.length, i); 
    }
 }

public static void shiftDown(int[] array, int size, int index) { 
int left = 2 * index + 1; while (left < size) {
 int max = left; int right = 2 * index + 2; if (right < size) { 
if (array[right] > array[left])
 {
 max = right; 
}
 }
if (array[index] >= array[max])
 { 
break; 
}
nt t = array[index]; array[index] = array[max]; array[max] = t;
 index = max; left = 2 * index + 1; 
} 
}

2.3性能分析

时间复杂度                         空间复杂度

O(n^log(n))                        O(1)

数据不敏感                           数据不敏感

稳定性:不稳定

三.交换排序

 3.1冒泡排序 

   在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序。

3.2代码实现

 

public static void bubbleSort(int[] array)
 {
 for (int i = 0; i < array.length - 1; i++) 
     {
 boolean isSorted = true; for (int j = 0; j < array.length - i - 1; j++)
       { // 相等不交换,保证稳定性 
if (array[j] > array[j + 1]) 
         { 
     swap(array, j, j + 1); 
        isSorted = false; 
     }
   }   if (isSorted)
 { 
     break; 
   } 
  } 
}

3.3性能分析

  时间复杂度                                                           空间复杂度

最好               最坏                      平均

O(n)              O(n^2)                  O(n^2)                     O(1)

数据有序         数据逆序

稳定性:稳定

2.快速排序

原理 - 总览
1. 从待排序区间选择一个数,作为基准值 (pivot)
2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的
(可以包含相等的)放到基准值的右边;
3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1 ,代表已经有序,或者小区
间的长度 == 0 ,代表没有数据。
代码实现
public static void quickSort(int[] array)
{ 
quickSortInternal(array, 0, array.length - 1); 
}// [left, right] 为待排序区间
 private static void quickSortInternal(int[] array, int left, int right) 
{ 
if (left == right)
{ 
return;
 }if (left > right) { return; }// 最简单的选择基准值的方式,选择 array[left] 作为基准值 // pivotIndex 代表基准值最终停留的下标 
int pivotIndex = partition(array, left, right); // [left, pivotIndex - 1] 都是小于等于基准值的 //
 [pivotIndex + 1, right] 都是大于等于基准值的 quickSortInternal(array, left, pivotIndex - 1);
 quickSortInternal(array, pivotIndex + 1, right); }
原理 -partition
Hoare :

 

代码实现:

  

private static int partition(int[] array, int left, int right)
 { 
int i = left; int j = right; int pivot = array[left];
 while (i < j) {
 while (i < j && array[j] >= pivot) 
   { 
      j--; 
   }while (i < j && array[i] <= pivot) 
  {
 i++; 
}
swap(array, i, j);
 }swap(array, i, left);
 return i; 
}

挖坑法

    

基本思路和 Hoare 法一致,只是不再进行交换,而是进行赋值(填坑 + 挖坑)
实现:
private static int partition(int[] array, int left, int right) 
{
 int i = left; int j = right; int pivot = array[left]; 
while (i < j)
 {
 while (i < j && array[j] >= pivot) 
{
 j--; 
}
array[i] = array[j];
 while (i < j && array[i] <= pivot) { 
i++;
 }
array[j] = array[i];
 }
array[i] = pivot; return i;
 }

前后遍历法实现:

private static int partition(int[] array, int left, int right)
 { 
int d = left + 1; int pivot = array[left]; 
for (int i = left + 1; i <= right; i++)
 {
 if (array[i] < pivot)
 { 
swap(array, i, d); d++;
 } 
}
swap(array, d, left); return d; 
}

性能分析 

  时间复杂度                                                                                   空间复杂度

最好                最坏                  平均                                          最好                最坏                  平均

O(n*log(n)) O(n^2)            O(n*log(n)                             O(log(n))          O(n)               O(log(n))

稳定性:不稳定

优化总结
1. 选择基准值很重要,通常使用几数取中法
2. partition 过程中把和基准值相等的数也选择出来
3. 待排序区间小于一个阈值时(例如 48 ),使用直接插入排序
 
1. 在待排序区间选择一个基准值
       1. 选择左边或者右边
       2. 随机选取
       3. 几数取中法
2. partition ,使得小的数在左,大的数在右
       2. 挖坑
       3. 前后遍历
       4. 将基准值相等的也选择出来(了解)
3. 分治处理左右两个小区间,直到小区间数目小于一个阈值,使用插入排序
 
四.归并排序
  
原理 - 总览
归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法( Divide and
Conquer )的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使
子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
 
 
 
源代码 合并两个有序数组
private static void merge(int[] array, int low, int mid, int high)
   { 
    int i = low; int j = mid; int length = high - low; 
    int[] extra = new int[length]; int k = 0; // 选择小的放入 extra
         while (i < mid && j < high) { 
// 加入等于,保证稳定性 
    if (array[i] <= array[j]) 
{
 extra[k++] = array[i++];
 }
 else { extra[k++] = array[j++];
 } 
}
// 将属于元素放入 extra 
while (i < mid)
 { 
extra[k++] = array[i++]; 
}while 
(j < right) { 
extra[k++] = array[j++];
 }
// 从 extra 搬移回 
array for (int t = 0; t < length; t++)
 { 
// 需要搬移回原位置,从 low 开始 array[low + t] = extra[t];
   } 
}

实现:

  public static void mergeSort(int[] array) { 
     mergeSortInternal(array, 0, array.length); 
     }// 待排序区间为 [low, high)
    private static void mergeSortInternal(int[] array, int low, int high)
 { 
  if (low - 1 >= high)
   { 
   return;
    }
  int mid = (low + high) / 2;
  mergeSortInternal(array, low, mid); 
  mergeSortInternal(array, mid, high);
   merge(array, low, mid, high);
 }

性能分析

    时间复杂度                                                    空间复杂度

O(n*log(n))                                                    O(n)

数据不敏感                                                        数据不敏感

稳定性:稳定

优化总结
在排序过程中重复利用两个数组,减少元素的复制过程
 
海量数据的排序问题
外部排序:排序过程需要在磁盘等外部存储进行的排序
前提:内存只有 1G ,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序
1. 先把文件切分成 200 份,每个 512 M
2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
3. 进行 200 路归并,同时对 200 份有序文件做归并过程,最终结果就有序了
  排序总结图
    
      

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值