Java 数据结构 (五)常见排序

本节目标
掌握七大基于比较的排序算法基本原理及实现
掌握排序算法的性能分析
掌握 java 中的常用排序方法

目录

1.概念

1.1 概念

1.2稳定性

1.3 应用

2.七大基于比较的排序

3.插入排序

3.1 直接插入排序-原理

3.2 实现

3.3 性能分析

3.4 折半插入排序(了解)

4.希尔排序

4.1 原理

4.2 实现

4.3 性能分析

5.选择排序

5.1 直接选择排序-原理

5.2 实现

5.3 性能分析

5.4 双向选择排序(了解)

6.堆排序

6.1 原理

6.2 实现

6.3 性能分析

7.冒泡排序

7.1 原理

7.2 实现

7.3 性能分析

8.快速排序(重要)

8.1 原理-总结

8.2 原理-partition

8.3 性能分析

8.4 原理 基准值的选择

8.5 原理-非递归分治

8.7 优化总结

8.8 总结

9.归并排序

9.1 原理 总结

9.2 原理-合并两个有序数组

9.3 实现

9.4 性能分析

9.5 优化总结

9.6 非递归版本

9.7海量数据的排序问题

10.排序总结


1.概念

1.1 概念

排序,就是是一串记录,按照其中的某个或某些关键字的大小,递增或递减的排序起来的操作,平时的上下文中,如果提到排序,通常指的是拍升序(非降序)

通常意义上的排序,都是指的原地排序。

1.2稳定性(重要)

两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具有稳定性的排序算法

1.3 应用

购物时选择价格升序降序

2.七大基于比较的排序

3.插入排序

3.1 直接插入排序-原理

整个区间被分为

        1.有序区间

        2.无序区间

3.2 实现

3.3 性能分析

3.4 折半插入排序(了解)

4.希尔排序

希尔排序又称缩小增量法。希尔排序的基本思想是:先选定一个整数,把持排序文件中所有记录分成个组,所有距离为的记录分在同一组,并对每一组的记录进行排序,然后取。重复上述分组和排序工作,当达到=1 时所有记录同意组内排好序

        1.希尔排序是对直接插入排序的优化

        2.当 gap>1时 都是预排序,目的时让数组更接近于有序,当gap == 1 时,数组已经接近有序的了,这样就会很快。

4.1 原理

4.2 实现

  public static void shellSort (int[] args) {
        int gap = array.length;
        while (gap > 1){
            insertSortGap(array,gap);
            gap = (gap / 3) + 1;
        }
        intsertSortGap(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;
        }
    }

4.3 性能分析

时间复杂度

最好O(n)

平均O(n^1.3)

最坏O (n^2)

空间复杂度

O(1)

5.选择排序


5.1 直接选择排序-原理

5.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;
   }
}

5.3 性能分析

时间复杂度:O(n^2)

空间复杂度:O(1)

5.4 双向选择排序(了解)

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

6.堆排序


6.1 原理

基本原理也是选择排序,只是不在使用遍历的方式查找戊戌区间的最大的数,而是通过堆来选择无序区间的最大的数

注意: 排升序要建大堆;排降序要建小堆。

6.2 实现

 

    public void heapSort(int[] array){
        createHeap(array);
        for(int i = 0; i < array.length - 1; i++){
            //
            //
            //
            swap(array,0 , array.length - 1);
            //
            //
            //
            //
            shiftDown(array, array.length - i - 1, 0);
        }
    }

    public  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--){

        }
    }

    public static void shiftDown(int[] array,int size,int index ){}

    private int index;
    int left = 2 * index + 1;
    while (left < size) {
        int max = left;
        int right = 2* index +2;
        if(right < size){
            if (array[index] >= array[max]){
                break;
            }
        }
        int t = array[index];
        array[index] = array[max];
        array[max] = t;

        index = max;
        left = 2 * index +1;
    }

    private class size {
    }

6.3 性能分析

时间复杂度 O(n*log(n))

空间复杂度 O(1)

7.冒泡排序


7.1 原理

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

7.2 实现

public class bubbleSort {
    public static void bubbleSort(int[] array){
        for(int i = 0; j < array.length - 1; j ++){
            if(array[j] > array[j + i]){
                swap(array. j, j + i);
                isSorted = false;
            }
        }
        if (isSorted){
            break
        }
    }
}

7.3 性能分析

时间复杂度:

最好:O(n)

平均: O (n^2)

最坏: O(n^2)

空间复杂度 O(1)

8.快速排序(重要)


8.1 原理-总结

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); 
}

8.2 原理-partition

实现

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; 
}

8.3 性能分析

最好: O(n * log(n))
平均:O(n * log(n))
最坏:O(n^2)
最好:O(log(n))
平均:O(log(n))
最坏:O(n)

8.4 原理 基准值的选择

1. 选择边上(左或者右)
2. 随机选择
3. 几数取中(例如三数取中): array[left], array[mid], array[right] 大小是中间的为基准值

8.5 原理-非递归分治

public static void quickSort(int[] array) { 
 Stack<Integer> stack = new Stack<>(); 
 stack.push(array.length - 1); 
 stack.push(0); 
 
 while (!stack.isEmpty()) { 
 int left = stack.pop(); 
 int right = stack.pop(); 
 if (left >= right) { 
 continue; 
 } 
 
 int pivotIndex = partition(array, left, right); 
 stack.push(right); 
 stack.push(pivotIndex + 1); 
 
 stack.push(pivotIndex - 1); 
 stack.push(left); 
 } 
}

8.7 优化总结

1. 选择基准值很重要,通常使用几数取中法
2. partition 过程中把和基准值相等的数也选择出来
3. 待排序区间小于一个阈值时(例如 48 ),使用直接插入排序

8.8 总结

1. 在待排序区间选择一个基准值
1. 选择左边或者右边
2. 随机选取
3. 几数取中法
2. partition ,使得小的数在左,大的数在右
        1. hoare
        2. 挖坑
        3. 前后遍历
        4. 将基准值相等的也选择出来(了解)
        3. 分治处理左右两个小区间,直到小区间数目小于一个阈值,使用插入排序

9.归并排序


9.1 原理-总览

归并排序是建立在归并排序操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用,将已有序列的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序。若将两个有序表合成一个有序表,称为二路并归。

9.2 原理-合并两个有序数组

9.3 实现

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); 
}

9.4 性能分析

时间复杂度:O(n * log(n))

空间复杂度:O(n)

9.5 优化总结

在排序过程中重复利用两个数组,减少元素的复制过程

9.6 非递归版本

public static void mergeSort(int[] array) { 
 for (int i = 1; i < array.length; i = i * 2) { 
 for (int j = 0; j < array.length; j = j + 2 * i) { 
 int low = j; 
 int mid = j + i; 
 if (mid >= array.length) { 
 continue; 
 }
 int high = mid + i; 
 if (high > array.length) { 
 high = array.length; 
 } 
 
 merge(array, low, mid, high); 
 } 
 } 
}

9.7海量数据的排序问题

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

10.排序总结

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值