八大排序算法JAVA实现(时间复杂度O(n*logn)篇)

本文讲述时间复杂度为n*logn的排序算法:归并排序、快速排序、堆排序以及希尔排序的原理、Java实现以及变形应用。

一、归并排序

  • 原理:把两个有序数列合并为一个有序数列。需递归实现。
  • Java实现:
 1     public int[] mergeSort(int[] a, int n)
 2     {
 3         return doMergeSort(a, n, 0, n - 1);
 4     }
 5         public int[] doMergeSort(int[] a, int n, int start, int end)
 6     {
 7         int[] res = new int[n];
 8         if (n <= 1)
 9         {
10             res[0] = a[start];
11             return res;
12         }
13         // n>=2时,对其左右数组进行处理
14         int half = n / 2;
15         int leftEnd = start + half - 1;
16         int rightStart = leftEnd + 1;
17         //递归调用本函数,获取有序的左数组以及右数组
18         int[] left = doMergeSort(a, half, start, leftEnd);
19         int[] right = doMergeSort(a, n - half, rightStart, end);
20         // 将左右序列合并
21         int k = 0, i = 0, j = 0;
22         while (i < half && j < n - half)
23         {//由前往后比较两个序列,取较小值填充到res中,取值后该序列往后移动取下一个值比较
24             if (left[i] <= right[j])
25             {
26                 res[k++] = left[i++];
27             }
28             else
29             {
30                 res[k++] = right[j++];
31             }
32         }
33         // 剩余的直接放入
34         while (i < half)
35         {
36             res[k++] = left[i++];
37         }
38         while (j < n - half)
39         {
40             res[k++] = right[j++];
41         }
42         return res;
43     }            

二、快速排序

  • 原理:每一次将一个数放在一个左边的数全部比它小,且右边的数全部比它大的位置,然后递归调用函数,将其左右序列排好。这边有一个比较好理解的做法:在数组的左边维护一个小于区间,在遍历的时候,发现比当前数小的数字的时候,将,扩增小于区间,并其放到小于区间内,结束后将当前数填充在小于区间后即可。
  • Java实现:
 1     public int[] quickSort(int[] a, int n)
 2     {
 3         doQuickSort(a, n, 0, n - 1);
 4         return a;
 5     }
 6     public void doQuickSort(int[] a, int n, int start, int end)
 7     {
 8         if (n > 1)
 9         {
10             int current = a[end];
11             int minLen = 0;// 小于区间的长度
12             int i = start;
13             for (; i < end; i++)
14             {
15                 if (a[i] < current)
16                 {//发现比当前数小的数,扩充小于区间
17                     int temp = a[start + minLen];
18                     a[start + minLen] = a[i];
19                     a[i] = temp;
20                     minLen++;
21                 }
22             }
23             a[end] = a[start + minLen];
24             a[start + minLen] = current;
25             //当前位置已经确定,排左右序列
26             doQuickSort(a, minLen, start, start + minLen - 1);
27             doQuickSort(a, n - minLen - 1, start + minLen + 1, end);
28         }
29     }        
  • 变形应用:三色排序练习题
有一个只由0,1,2三种元素构成的整数数组,请使用交换、原地排序而不是使用计数进行排序。给定一个只含0,1,2的整数数组A及它的大小,请返回排序后的数组。保证数组大小小于等于500。

测试样例:

[0,1,1,0,2,2],6
返回:[0,0,1,1,2,2]

解析:运用快排的原理。用数字1来处理,在数组左右各维护一个小于区间和大于区间。

三、堆排序

  • 原理:维护一个大根堆(小根堆同理),即维护一棵二叉树,该数子节点永远比父节点小。每次在大根堆中取出根,根为此时待排序列最大值,放在待排序列最后,然后调整大根堆,重复上诉过程即可。
  • Java实现:博主不太会插图,关于大小根堆的调整细节可自行百度。原理总结来说是从最后一个非叶子节点开始,往前调整。设当前调整的非叶子节点为n,选举n,n的左,n的右三个节点的最大值作为父节点。且每次调整了靠前的非叶子节点的值后,可能会破坏下面的数的大根堆规则,需要再次调整。嗯我觉得我并没有讲清楚,百度看看图就好。
    public int[] heapSort(int[] a, int n)
    {
        for (int len = n; len > 0; len--)
        {// len为构建的堆的大小
            for (int i = len / 2 - 1; i >= 0; i--)
            {// 从后往前遍历非叶子节点
                while (2 * i + 1 < len)
                {
                    int j = 2 * i + 1;// 左节点序号
                    if (j + 1 < len && a[j] < a[j + 1])
                    {
                        j++;
                    }
                    if (a[i] < a[j])
                    {
                        int temp = a[j];
                        a[j] = a[i];
                        a[i] = temp;
                        i = j;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            int temp = a[len - 1];
            a[len - 1] = a[0];
            a[0] = temp;
        }
        return a;
    }
  • 变形应用:常规应用如在1000+个数中找出最大的10个数之类的。

已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。 给定一个int数组A,同时给定A的大小n和题意中的k,请返回排序后的数组。

测试样例: [2,1,4,3,6,5,8,7,10,9],10,2

返回:[1,2,3,4,5,6,7,8,9,10]

解析:维护一个大小为k的小根堆,每次调整完这个小根堆后,最小值就会出现在第一位,此时移除第一位,添加后一位数进来,继续调整这个小根堆即可。可以想象为一个从前往后移动的滑动窗口,滑动窗口中是一个小根堆。

四、希尔排序

  • 原理:变形后的插入排序,每个数只与它前面固定步长的倍数的位置进行比对。如:步长step,当前数与它前面step,step*2,step*3....位置进行比较,插入到合适的位置。
  • Java实现:
    public int[] shellSort(int[] a, int n)
    {
        // 步长选择
        for (int k = 1, step = 10; step > 1; k++)
        {
            step = n / (2 * k);
            for (int i = step; i < n; i++)
            {
                if (a[i] < a[i - step])
                {
                    int temp = a[i];
                    a[i] = a[i - step];
                    a[i - step] = temp;
                    int pre = i - step;
                    while (pre - step > 0)
                    {
                        if (a[pre] < a[pre - step])
                        {
                            temp = a[pre];
                            a[pre] = a[pre - step];
                            a[pre - step] = temp;
                            pre -= step;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
        return a;
    }
时间: 2024-10-24 13:39:43

八大排序算法JAVA实现(时间复杂度O(n*logn)篇)的相关文章

八大排序算法Java(转)

目录(?)[-] 概述 插入排序直接插入排序Straight Insertion Sort 插入排序希尔排序Shells Sort 选择排序简单选择排序Simple Selection Sort 选择排序堆排序Heap Sort 交换排序冒泡排序Bubble Sort 交换排序快速排序Quick Sort 归并排序Merge Sort 桶排序基数排序Radix Sort 总结 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序

算法基础——经典八大排序算法的Java及Python实现

概述 八大排序算法不用多说了,程序员算法基础必须要掌握的,现在总结一下加深记忆.下图是这八大排序算法的分类.名称.时间空间复杂度,以及稳定性. 代码 以下是经典八大排序算法的Java及Python代码,都是基于经典算法书籍<算法导论>里的伪代码实现的,我在关键语句部分附上了注释. 按照上图中的顺序分别介绍八大排序算法的实现(升序),前面是Java,后面是Python.Java的排序函数写在了一个类里,Python的排序函数则直接写出来了. 直接插入排序 public class InsertS

Java实现八大排序算法

本文对常见的排序算法进行了总结. 常见排序算法如下: 直接插入排序 希尔排序 简单选择排序 堆排序 冒泡排序 快速排序 归并排序 基数排序 它们都属于内部排序,也就是只考虑数据量较小仅需要使用内存的排序算法,他们之间关系如下: 稳定与非稳定: 如果一个排序算法能够保留数组中重复元素的相对位置则可以被称为是 稳定 的.反之,则是 非稳定 的. 直接插入排序 基本思想 通常人们整理桥牌的方法是一张一张的来,将每一张牌插入到其他已经有序的牌中的适当位置.在计算机的实现中,为了要给插入的元素腾出空间,我

八大排序算法

转载:http://blog.csdn.net/hguisu/article/details/7776068 目录(?)[+] 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速

谈谈八大排序算法问题

排序算法可以说是算法的入门以及算法学习阶段的基石,排序算法显得那么的基础又是非常重要的一种算法.排序算法常常作为一些高阶算法的数据处理中间过程在实际的问题处理中被应用的最为广泛,因此算法选将阶段就从八大排序算法开始.在本节内容中既可以看到一般性的比如插入排序,冒泡排序等基础算法又可以看到比如基数排序,位图排序,快速排序等等比较难理解的算法,算法之门从排序算法说起. 1.插入排序 插入排序算法的原理很简单,默认A中有一部分数据已经排好序了,后续只要从没有排好序的序列里面每拿出一个数字就在排好序的序

排序算法Java实现

排序算法Java实现 排序算法的分类: 内部排序,在排序过程中,全部记录放在内存中,称为内部排序: 外部排序,在排序过程中需要使用外部存储(磁盘),则称为外部排序. 主要介绍内部排序: 插入排序:直接插入排序.二分法插入排序.希尔排序 选择排序:简单选择排序.堆排序 交换排序:冒泡排序.快速排序 归并排序 基数排序 插入排序 直接插入排序 基本思想:对于给定的一组记录,初始时假设第一个记录自成一个有序序列,其余记录为无序序列.接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的

数据结构与算法之——八大排序算法

附:关于这个主题,网上好的文章已经数不胜数,本篇是整合后的文章. 正文: 一.概述 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 本文所指八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 二.排序算法详述 1.

八大排序算法总结及C/C++实现

概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 1. 插入排序-直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到

[Data Structure] 八大排序算法

排序有内部排序和外部排序之分,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.我们这里说的八大排序算法均为内部排序. 下图为排序算法体系结构图: 1. 直接插入排序(Straight Insertion Sort ) 基本思想:将待排序的无序数列看成是一个仅含有一个元素的有序数列和一个无序数列,将无序数列中的元素逐次插入到有序数列中,从而获得最终的有序数列. 算法流程: 1)初始时, a[0]自成一个有序区, 无序区为a[1