常用Java排序算法

时间复杂度

f(n) 算法基本操作执行的方法
n表示算法的规模
O(n) f(n)的量级
稳定性: 同样元素的相对位置在排序前后是否有可能发生变化

冒泡排序

普冒

public class SortUtil {
    public static SortService bubble = new BubbleSort();
    public static SortService bubbleA = new BubbleASort();

    public static void main(String[] args) {
        int[] data = new int[10000];
        for (int i = 0; i < data.length; i++) {
            data[i] = new Random().nextInt(50000);
        }

        SortUtil.sort(SortUtil.bubbleA, data);
        System.out.println(Arrays.toString(data));
    }

    private static void sort(SortService sortService, int[] data) {
        long before = System.currentTimeMillis();
        sortService.sort(data);
        long after = System.currentTimeMillis();
        System.out.println(sortService.getName() + "cost:" + (after - before));
    }
}

interface SortService{
    public void sort(int[] data);

    public String getName();
}

class BubbleSort implements SortService{
    @Override
    public void sort(int[] data) {
        for (int i = 0; i < data.length; i++) {  // n
            for (int j = 0; j < data.length - 1 - i; j++) {   // n(n-1)/2
                if(data[j] > data[j + 1]){    // n(n-1)/2
                    int temp = data[j + 1];   // n(n-1)/2
                    data[j + 1] = data[j];    // n(n-1)/2
                    data[j] = temp;           // n(n-1)/2
                }
            }
        }
    }

    @Override
    public String getName() {
        return "冒泡排序";
    }
}
  • 耗时:170
  • 时间复杂度: 最大:f(n) = n + 5*n(n-1)/2 ;O(n)=n^2 最小 f(n) = n + n(n-1)/2, O(n^2)
  • 稳定

改进冒

上面的冒泡有个问题,是完全正序是复杂度过大。可以优化成如下:

class BubbleASort implements SortService {

    @Override
    public void sort(int[] data) {
        boolean swap = true;
        for (int i = 0; i < data.length; i++) {  // 2
            if(! swap){   // 2
                break;
            }
            swap = false; // 1
            for (int j = 0; j < data.length - 1 - i; j++) {  // n
                if(data[j] > data[j + 1]){   // n
                    int temp = data[j + 1];
                    data[j + 1] = data[j];
                    data[j] = temp;
                    swap = true;
                }
            }
        }
    }

    @Override
    public String getName() {
        return "冒泡排序-改";
    }
}
  • 耗时:会比上面慢一点
  • 时间复杂度:最大差不多, 最小: f(n) = 2n + 2 + 2 + 1; O(n)
  • 稳定

选择排序

/**
 * 选择排序
 * 把数组分割为有序区和无序区
 * 从无序区中[选择出]最小/最大的放到有序区的最后
 */
class SelectSort implements SortService{

    @Override
    public void sort(int[] data) {
        for (int i = 0; i < data.length; i++) {   //n
            // 选择最小数的位置的下标
            int minIndex = i;                     // n
            for (int j = i + 1; j < data.length; j++) {  // n(n-1)/2
                if(data[j] < data[minIndex]){    // n(n-1)/2
                    minIndex = j;              // n(n-1)/2
                }
            }
            int temp = data[i];                //n
            data[i] = data[minIndex];          //n
            data[minIndex] = temp;             //n
        }
    }

    @Override
    public String getName() {
        return "选择排序";
    }
}
  • 耗时: 70 比冒泡快不少,是因为减少了数据交换的执行测试
  • 时间复杂度:最坏和平均: f(n)=5n + 3n(n-1)/2 O(n^2). 最好的情况同样跟冒泡一样需要加入检测交换的情况,加入之后其复杂度为 O(n)】
  • 不稳定

插入排序

普插

/**
 * 插入排序
 * 把数组分为左有序,右无序,初始状态左第一个为有序,后面都为无序
 * 遍历无序集合,从第一个元素开始,跟有序的所有比较,找到合适位置
 * 从这个位置,左部元素右移该位置插入无序集合中遍历的这个元素
 */
class InsertSort implements  SortService{

    @Override
    public void sort(int[] data) {
        for (int i = 1; i < data.length; i++) {   //n
            int j;                                //n
            for (j = i - 1; j >= 0; j--) {  //n(n-1)/2 因为左有序,因此找到了data[i]大于的第一个位置,就说明data[i]应该插在这个位置之后
                if(data[j] < data[i]){      //n(n-1)/2,
                    break;
                }
            }
            // 插入元素并后移
            if(j != (i-1)){                 // n
                int temp = data[i];         // n
                for (int k = i - 1; k > j; k--) {  // n(n-1)/2
                    data[k + 1] = data[k];         // n(n-1)/2
                }
                data[j + 1] = temp;                // n
            }
        }
    }

    @Override
    public String getName() {
        return "插入排序";
    }
}
  • 稳定
  • 耗时 63, 比选择好,是因为平均复杂度低一点,但是最坏情况比选择还要高
  • 复杂度:最坏f(n)=5n + 2n(n-1) O(n^2) 平均 O(n) 最好的情况:f(n)=4n O(n)

插入排序-改

/**
 * 插入排序-该
 * 改前是先找插入位置,然后统一移动顺序,再插入
 * 改后,使用类似冒泡的方式,在比较的同时就移动顺序
 */
class InsertSortAdvance implements  SortService{

    @Override
    public void sort(int[] data) {
        for (int i = 1; i < data.length; i++) {     // n
            // j是有序表尾, 连带着无序表头做冒泡
            for (int j = i-1; j>=0 && data[j] > data[j+1]; j--) {       // 最坏n(n-1)/2 最好 // n
                int temp = data[j];                                     // 最坏n(n-1)/2
                data[j] = data[j+1];                                    // 最坏n(n-1)/2
                data[j+1] = temp;                                       // 最坏n(n-1)/2
            }
        }
    }

    @Override
    public String getName() {
        return "插入排序";
    }
}
  • 稳定
  • 53, 因为平均中交换的操作可能执行的比较少。
  • 时间复杂度 最坏:f(n) = 2n(n-1) + n O(n^2) 最好f(n)=2n O(n)

二叉树排序

/**
 * 二叉树
 * 先构造二叉树,构建好之后就是有序的
 * 然后遍历二叉树
 */
class BinaryTreeSort implements SortService{
    class Data{
        int i = 0;
        int[] data;
        public Data(int[] data){
            this.data = data;
        }
        public void add(int j){
            data[i++] = j;
        }
    }

    class BinaryNode{
        int value;
        BinaryNode left;
        BinaryNode right;
        public BinaryNode(int value){
            this.value = value;
            this.left = null;
            this.right = null;
        }

        public void add(int value){
            if(value > this.value){
                if(this.right != null){
                    this.right.add(value);
                }else{
                    this.right = new BinaryNode(value);
                }
            }else{
                if(this.left != null){
                    this.left.add(value);
                }else{
                    this.left = new BinaryNode(value);
                }
            }
        }

        // 中序遍历
        public void iterator(Data d){
            if(this.left != null){
                this.left.iterator(d);
            }
            if(this.right != null){
                this.right.iterator(d);
            }
            d.add(this.value);
        }
    }
    @Override
    public void sort(int[] data) {
        BinaryNode n = new BinaryNode(data[0]);
        for (int i = 1; i < data.length; i++) {
            n.add(data[i]);
        }
        Data d = new Data(data);
        n.iterator(d);
    }

    @Override
    public String getName() {
        return "二叉树排序";
    }
}
  • 稳定
  • cost 3
  • 时间复杂度f(n) ~= nlogn + logn O(nlogn)

快速排序

/**
 * 快速排序
 * 选取一个元素,比其小的放在左边,比其大的放到右边
 * 对于数组就是用旳第一个元素,然后从后往前比较,找到第一个比他小的,然后交换,再从前往后比较找到第一个比它大的交换,直到前后会和,完成一次分组。然后再对每个小组重新进行快排分组
 */
class QuickSort implements SortService{

    @Override
    public void sort(int[] data) {
        quickSort(data, 0, data.length-1);
    }

    private void quickSort(int[] data, int low, int high) {
        if(low >= high){
            return;
        }
        int p = partition(data, low, high);
        quickSort(data, low, p-1);
        quickSort(data, p+1, high);
    }

    /**
     * 得到分界线
     * @param data
     * @param low
     * @param high
     * @return
     */
    private int partition(int[] data, int low, int high) {
        boolean asc = false;
        while(low != high){
            if(asc){
                if(data[low] > data[high]){
                    int temp = data[low];
                    data[low] = data[high];
                    data[high] = temp;
                    asc = !asc;
                }else{
                    high --;
                }

            }else{
                if(data[low] > data[high]){
                    int temp = data[low];
                    data[low] = data[high];
                    data[high] = temp;
                    asc = !asc;
                }else{
                    low ++;
                }
            }
        }
        return low;
    }

    @Override
    public String getName() {
        return "快速排序";
    }
}
  • 不稳定
  • cost 2
  • 时间复杂度 最大O(n^2) 平均O(nlogn) 最小O(nlogn)

归并排序

/**
 * 归并排序
 * 申请空间,使其大小为两个已排序数组之和
 * 设定两个指针,起始位置为两个数组头
 * 比较两个指针,选择相对小的元素放入合并空间,并移动指针到下一个位置。直到某一个指针到达末尾
 * 把另一个数组的所有元素复制到合并数组的末尾
 */
class MergeSort implements SortService{

    @Override
    public void sort(int[] data) {
        mergeSort(data, 0, data.length - 1);
        int mid = data.length / 2;
        // leftPart is data[]
    }

    private void mergeSort(int[] data, int left, int right) {
        if(left >= right){
            // 说明是单数组了,因此不进行分解了
            return;
        }
        int center = (left + right)/2;
        // 分解数组为左右两部分,分别排序归并
        mergeSort(data, left, center);
        mergeSort(data, center+1, right);
        // 开始归并
        merge(data, left, center, center+1, right);
    }

    private void merge(int[] data, int oneSortLeft, int oneSortRight, int anotherLeft, int anotherRight){
        int begin = oneSortLeft;
        // 用缓存来存储排序后的数据
        int[] temp = new int[anotherRight + 1 - oneSortLeft];
        int index = 0;
        while((oneSortLeft <= oneSortRight) && (anotherLeft<=anotherRight)){
            if(data[oneSortLeft] < data[anotherLeft]){
                temp[index] = data[oneSortLeft];
                oneSortLeft ++;
                index++;
            }else{
                temp[index] = data[anotherLeft];
                anotherLeft ++;
                index++;
            }
        }
        if(oneSortLeft <= oneSortRight){
            for (int i = oneSortLeft; i <= oneSortRight; i++) {
                temp[index] = data[i];
                index ++;
            }
        }else{
            for (int i = anotherLeft; i <= anotherRight; i++) {
                temp[index] = data[i];
                index ++;
            }
        }

        for (int i = 0; i < temp.length; i++) {
            data[i + begin] = temp[i];
        }
    }

    @Override
    public String getName() {
        return "归并排序";
    }
}
  • 稳定
  • cost 3比快排慢一点点
  • 时间复杂度O(nlogn),平均最大最小都是这么多

Arrays.sort

  • Java对Primitive(int,float等原型数据)数组采用快速排序,对Object对象数组采用归并排序。
  • 这样是因为很多情况下对象的稳定性很重要,比如已经按照学号拍好序,想按照成绩再排一下
  • 实现中快排和归并都采用递归方式,而在递归的底层,也就是递归到后面数组长度小于7时,直接使用冒泡排序,而不再递归下去。
  • 这时因为当n较小的时候比较次数已经不是开销的大头,递归涉及的方法调用的开销反而会凸显
  • 当数组中的元素个数较少时,源码中的阀值为7,采用的是插入排序。尽管插入排序的时间复杂度为0(n^2),但是当数组元素较少时,插入排序优于快速排序,因为这时快速排序的递归操作影响性能。
  • 快排时比较好的划分了元,比如比较小时,去前中后三个点的中间值作为元。或者更多的踩点来获得元,避免最坏情况的发生
时间: 2024-11-01 22:32:10

常用Java排序算法的相关文章

总结5种比较高效常用的排序算法

原文:总结5种比较高效常用的排序算法 1 概述     本文对比较常用且比较高效的排序算法进行了总结和解析,并贴出了比较精简的实现代码,包括选择排序.插入排序.归并排序.希尔排序.快速排序等.算法性能比较如下图所示:   2 选择排序     选择排序的第一趟处理是从数据序列所有n个数据中选择一个最小的数据作为有序序列中的第1个元素并将它定位在第一号存储位置,第二趟处理从数据序列的n-1个数据中选择一个第二小的元素作为有序序列中的第2个元素并将它定位在第二号存储位置,依此类推,当第n-1趟处理从

C#几种常用的排序算法

排序|算法 C#几种常用的排序算法:1 冒泡排序法 1冒泡排序法#region 冒泡排序法 2public void Sort(int[] list) 3{ 4    long begintime = System.DateTime.Now.Second*1000+System.DateTime.Now.Millisecond; 5    WriteLine(begintime); 6    int j,temp; 7    j= 1; 8    while((j<list.Length)) 9

java排序算法

Java 1.0和1.1库都缺少的一样东西是算术运算,甚至没有最简单的排序运算方法.因此,我们最好创建一个Vector,利用经典的Quicksort(快速排序)方法对其自身进行排序. 编写通用的排序代码时,面临的一个问题是必须根据对象的实际类型来执行比较运算,从而实现正确的排序.当然,一个办法是为每种不同的类型都写一个不同的排序方法.然而,应认识到假若这样做,以后增加新类型时便不易实现代码的重复利用. 程序设计一个主要的目标就是"将发生变化的东西同保持不变的东西分隔开".在这里,保持不

常用内部排序算法之五:希尔排序

前言 在前面介绍常用内部排序算法的文章中,我们知道在简单排序算法中,直接插入排序算法的时间复杂度是要优于冒泡排序和简单选择排序的.而这里要讲的希尔排序则是优于直接插入排序的.而且希尔排序打破了原来简单排序中时间复杂度不能超过O(n2)的神话.这也是希尔排序伟大的地方,而且希尔排序不同于之前三种简单排序,希尔排序是以一个科学家的名字进行命名的. 希尔排序的原理 由于希尔排序是在直接插入排序的基础上进行改进的,所以为了更好理解希尔排序,需要再次将直接插入排序请出来.我们知道直接插入排序的基本思想是将

Java排序算法总结之插入排序_java

本文实例讲述了Java插入排序方法.分享给大家供大家参考.具体分析如下: 有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到插入排序法.本文主要介绍的是插入排序的java实现.   插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的.个数加一的有序数据.比较和交换的时间复杂度为O(n^2),算法自适应,对于数据已基本有序的情况,时间复杂度为O(n),算法稳定,开销很低.算法适合于数据已基本有序或者数据量

Java排序算法总结之希尔排序_java

本文实例讲述了Java排序算法总结之希尔排序.分享给大家供大家参考.具体分析如下: 前言:希尔排序(Shell Sort)是插入排序的一种.是针对直接插入排序算法的改进.该方法又称缩小增量排序,因DL.Shell于1959年提出而得名.本文主要介绍希尔排序用Java是怎样实现的. 希尔排序(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序.希尔排序并不稳定,O(1)的额外空间,时间复杂度为O(N*(logN)^2).最坏的情况下的执行效率和在平均情况下的执行效率相

5种java排序算法汇总工具类_java

工具类简单明了地总结了java的快速排序,希尔排序,插入排序,堆排序,归并排序五种排序算法,代码中并没有对这几种排序算法的一个说明,关于思想部分希望在自行查阅相关说明,这里只是对这几种算法进行一个概括,以供大家使用. public class Sort { public static <AnyType extends Comparable<? super AnyType>> void insertionSort(AnyType[] a) { insertionSort(a, 0,

C++实现八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序等_C 语言

本文实现了八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 .快速排序.归并排序.堆排序和LST基数排序 首先是算法实现文件Sort.h,代码如下: /* * 实现了八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 * 以及快速排序.归并排序.堆排序和LST基数排序 * @author gkh178 */ #include <iostream> template<class T> void swap_value(T &a, T &b) { T t

Java排序算法总结之冒泡排序_java

本文实例讲述了Java排序算法总结之冒泡排序.分享给大家供大家参考.具体分析如下: 前言:冒泡排序(BubbleSort)就是依次比较相邻的两个数,将小数放在前面,大数放在后面. 下面让我们一起    来看冒泡排序在Java中的算法实现. 冒泡排序是计算机的一种排序方法,它的时间复杂度为O(n^2),虽然不及堆排序.快速排序的O(nlogn,底数为2),但是有两个优点: 1."编程复杂度"很低,很容易写出代码: 2.具有稳定性,这里的稳定性是指原序列中相同元素的相对顺序仍然保持到排序后