排序算法(冒泡,插入,希尔,归并,选择,快速,基数排序)

排序:所谓排序就是将一组无序的数字用什么样的算法变的有序。
排序

  • 排序算法的稳定性:在未排序的序列中,如果a[i]=a[i+1],a[i]在a[i+1]之前,排序之后a[i]仍旧在a[i+1]前面。不稳定性反之。
  • 内排序:所有排序操作均在内存中完成。
  • 外排序:由于数据过大,因此把数据放在磁盘中那,排序通过磁盘和内存的数据传输才能进行。
  • 时间复杂度:一个算法执行完所消耗的时间
  • 空间复杂度:运行完一个程序需要的内存大小。

1. 稳定的排序算法

2.不稳定的排序算法

  • 选择排序
    首先在未排序的序列中找到最小(大)元素,存放到排序序列的起始位置,然后再从未排序的元素中找到最小(大)的元素放在一排序序列的末尾。以此类推,直到所有元素全部排完。
  • 快速排序
  • 希尔排序
  • 堆排序

3.比较排序

元素之间的次序依赖它们之间的比较,每个数需要与其他数进行比较才能确定自己的位置。比较典型的比较排序有快速排序归并排序,堆排序,冒泡排序

4非比较排序
非比较排序只要确定每个元素之前的已有元素个数即可,经过一次遍历便可以解决。

排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
冒泡排序O(n^2)O(n)O(n^2)O(1)In-place稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)In-place不稳定
插入排序O(n^2)O(n)O(n^2)O(1)In-place稳定
希尔排序O(n log n)O(n log2 n)O(n log 2 n)O(1)In-place不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)Out-place稳定
快速排序O(n log n)O(n log n)O(n^2)O(log n)In-place不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)In-place不稳定
计数排序O(n + k)O(n + k)O(n + k)O(K)Out-place稳定
桶排序O(n + k)O(n + k)O(n^2)O(n + k)Out-place稳定
基数排序O(n * k)O(n * k)O(n * k)O(n + k)Out-place稳定

排序算法

1.冒泡排序
   比较相邻的数字。如果第一个比第二个大就交换它两的位置,对每一个相邻元素都要进行比较,然后最大的就会在最后一个位置,再重复进行上面的步骤就可以。
时间复杂度:O(n^2)
稳定性:稳定
在这里插入图片描述

public class maopao1 {
    public static void main(String[] args) {
        int[]a={2,3,1,4,5,32,21,42,12,33};
        int i,j;
        int temp=0;
        int len=a.length;
        for( i=0;i<len-1;i++)
        {      
            for(j=0;j<len-i-1;j++)
            {
                 if(a[j]>a[j+1])
                 {
                     temp=a[j];
                     a[j]=a[j+1];
                     a[j+1]=temp;                  
                 }
            }
          
        }    
        for(i=0;i<len;i++)
        {
            System.out.print(a[i]+",");
        }
    }
}

运行结果为:

1,2,3,4,5,12,21,32,33,42,

其中从小到大的每一步循环的详细步骤如下:
第一轮:2,1,3,4,5,21,32,12,33,42,
第二轮:1,2,3,4,5,21,12,32,33,42,
第三轮:1,2,3,4,5,12,21,32,33,42,
第四轮:1,2,3,4,5,12,21,32,33,42,
第五轮:1,2,3,4,5,12,21,32,33,42,
第六轮:1,2,3,4,5,12,21,32,33,42,
由上面的步骤可以看出在第三轮已经排好序,后面的排序已经没有必要,所以可以改进代码通过添加标志位使时间复杂度更低。

改进版(通过添加标志位来实现优化)

public class maopao1 {
    public static void main(String[] args) {
        int[]a={2,3,1,4,5,32,21,42,12,33};
        int i,j;
        int temp=0;
        int len=a.length;
        for( i=0;i<len-1;i++)
        {
            boolean isSorted=false;//设置标志位
            for(j=0;j<len-i-1;j++)
            {
                 if(a[j]>a[j+1])
                 {
                     temp=a[j];
                     a[j]=a[j+1];
                     a[j+1]=temp;
                     isSorted=true;//如果发生交换则改变标志位
                 }
            }
            for(j=0;j<len;j++)
            {
                System.out.print(a[j]+",");
            }
            System.out.println();
            if(isSorted==false){
                break;
           }
        }
        System.out.println("****************************");
        for(i=0;i<len;i++)
        {
            System.out.print(a[i]+",");
        }
    }
}

运行结果为:

2,1,3,4,5,21,32,12,33,42,
1,2,3,4,5,21,12,32,33,42,
1,2,3,4,5,12,21,32,33,42,
1,2,3,4,5,12,21,32,33,42,
****************************
1,2,3,4,5,12,21,32,33,42,

我在上面程序中输出了每一次排序的结果和最后一次排序的结果(星号线下面),这样可以更加清楚的看出改进版的程序在前三次排好序,在判断第四次的排序时不满足条件,跳出循环。
2.选择排序
  选择排序就是先在为排序的序列中找的最小(大)的元素放在序列的第一个位置,然后从未排序的序列中继续找出最小(大)的元素放在已排序的后面,以此类推直至排完所有的数字。代码中可以这样理解先默认第一个是最小的,然后从未排序的序列中找出最小的元素与第一个交换位置(如果相等就不用交换了)。
稳定性:不稳定
时间复杂度:O(n^2)
在这里插入图片描述


import java.util.Arrays;

public class selectSort {
    public static void main(String[] args) {
        int []a = {1,2,1,-1,6,4,8,9,5,8};
        for (int i = 0; i < a.length-1; i++) {
            for (int j = i; j < a.length; j++) {
                if(a[j]<a[i]){
                    int t =a[j];
                    a[j] = a[i];
                    a[i] = t;
                }
            }
        }
        System.out.println(Arrays.toString(a));
    }
}

运行结果为:

[-1, 1, 1, 2, 4, 5, 6, 8, 8, 9]

3.希尔排序
  希尔排序又称缩小增量排序,其基本思想为:先将原表按照增量h分组,每个子文件进行直接插入排序。同样,用下一个增量h/2将文件再分为子文件,再进行直接插入排序,直到h=1时整个文件排好序。
关键因素:选择合适的增量,普通的选择就是使用数组长度的一半。
时间复杂度:O(n log n)
稳定性:不稳定在这里插入图片描述
3.1插入排序
前面讲到希尔排序是以不同的步长来实现插入排序。所谓插入排序就是在一个无序数组中,用第二个数字和第一个数字进行比较,如果比第一个小的话就插入再其前面,接下来第三个的数字就需要和它前两个数字都要进行比较,插入到最小的数字的前面,这样一直到排序完。
稳定性:稳定
时间复杂度:O(n^2)
插入排序代码:

import java.util.Arrays;

public class insertSort {
    public static void main(String[] args) {
        int []a={-1,2,2,9,4,6,8,3,6};
        for (int i = 0; i < a.length-1; i++) {
            for (int j = i+1; j > 0; j--) {
                if(a[j]<a[j-1]){
                    int t = a[j];
                    a[j] = a[j-1];
                    a[j-1] = t;
                }
            }
        }
        System.out.println(Arrays.toString(a));
    }
}

运行结果为:

[-1, 2, 2, 3, 4, 6, 6, 8, 9]

希尔排序代码


import java.util.Arrays;

public class sheelSort {
    public static void main(String[] args) {
        int[]a={-1,-4,-5,23,45,678,34,678};
        for (int h = a.length/2; h >0 ; h/=2) {
            for (int i = h; i < a.length; i++) {
                for (int j = i; j >=h; j-=h) {
                    if(a[j]<a[j-h]){
                        int t = a[j];
                        a[j] = a[j-h];
                        a[j-h] = t;
                    }
                }
            }
        }
        System.out.println(Arrays.toString(a));
    }
}

运行结果为:

[-5, -4, -1, 23, 34, 45, 678, 678]

4.归并排序
  归并排序就是利用归并的思想实现排序的方法,他的原理时假设出事序列有N个数字,则可以看成是N个有序的子序列,每个子序列的长度为1,然后两两进行归并,得到 N/2 个长度为2或1的有序子序列(因为有可能N为奇数会产生单独的一个数字),再两两归并,如此重复,直到得到一个长度为N的有序序列为止,这种排序方法称为归并排序
时间复杂度:O(n log n)
稳定性:稳定
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
https://www.cnblogs.com/chengxiao/p/6194356.html(图片链接)
代码如下:

import java.util.Arrays;

public class guiBingSort {
    public static void main(String[] args) {
        int []arr={3, 5, 6, 2, 1, 7, 8, 9, 10,-1};
        //fen
        chaiFen(arr,0,arr.length-1);
        mergeSort(arr,0,(arr.length/2)-1,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    private static void chaiFen(int[] arr, int startIndex, int endIndex) {
        //计算中间索引
        int centerIndex = (startIndex + endIndex)/2;
        //递归来拆分
        if(startIndex < endIndex){
            //拆左边
            chaiFen(arr,startIndex,centerIndex);
            //拆右边
            chaiFen(arr,centerIndex+1,endIndex);

        }
        //归并
        mergeSort(arr,startIndex,centerIndex,endIndex);
    }

    /**
     *
     * @param arr 要归并的数组
     * @param startIndex 开始索引
     * @param centerIndex 中间索引
     * @param endIndex 结束索引
     */
    //mergeSort(arr,0,(arr.length/2)-1,arr.length-1);
    private static void mergeSort(int[] arr, int startIndex, int centerIndex, int endIndex) {
        //定义一个临时数组
        int[]tempArray = new int[endIndex - startIndex+1];
        //定义临时数组的起始索引
        int index = 0;
        //定义左边数组的起始索引
        int i = startIndex;
        //定义右边数组的起始索引
        int j = centerIndex+1;
        //循环比较左右两边的数组元素,往临时数组里边方
        while (i <= centerIndex && j <= endIndex){
            //进行比较
            if(arr[i]<arr[j]){
                tempArray[index] = arr[i];
                i++;
            }else{
                tempArray[index] = arr[j];
                j++;
            }
            index++;//临时数组的索引要增加
        }
        //处理左边剩余元素
        while (i <= centerIndex){
            tempArray[index] = arr[i];
            i++;
            index++;
        }
        //处理右边剩余元素
        while (j <= endIndex){
            tempArray[index] = arr[j];
            j++;
            index++;
        }
        //遍历临时数组,将临时数组中的元素放到原数组中
        for (int k = 0; k < tempArray.length; k++) {
            //这里k要加上起始索引
            arr[k+startIndex] = tempArray[k];
        }
    }
}

运行结果为:

[-1, 1, 2, 3, 5, 6, 7, 8, 9, 10]

5.快速排序
  快速排序基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的数据比另外一部分的所有数据都要小,然后再按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此使整个数据变成有序序列。
实现方法:快速排序会先把数组中的一个数当作基准数(参照数),一般会把数组中最左边的数当作基准数。然后从两边进行查找,先从右边开始查找比基准数小的,再从左边查找比基准数大的(如果选择最右边的数为基准数那么就先从左边检索,方法类似。),如果找到了就停下,然后交换这两个元素,然后在进行查找。直到两个数相遇,那个此时将基准值与相遇位置元素进行交换,此时基准值左边的所有数据全部比基准值小,右边所有数据全部比基准值大。然后将基准数左边和基准数右边的数组分别采用上述同样的方法进行排序,可以采用递归进行,直到整个数据变成有序序列。
时间复杂度:O(n log n)
稳定性:不稳定
在这里插入图片描述

后面,然后将基准数左边和基准数右边的数组分别采用上述同样的方法,可以采用递归进行,直到整个数据变成有序序列。
代码如下:

public class quicksort {
    public static void main(String[] args) {
        int []arr={7,2,3,8,10,4,5,6,11,9};
        //调用方法,进行快速排序
        quicksort(arr, 0, arr.length -1);

        //遍历数组输出
        for(int i=0;i<arr .length;i++){
            System.out.print(arr [i]+" ,");
    }
};
    /*
     *定义方法
     */
    public static void quicksort(int []arr,int left,int right) {
        //进行判断,如果左边索引比右边大,那么直接return结束这个方法
        if (left > right) {
            return;
        }
        //定义变量,保存基准数
        int base = arr[left];
        //定义变量i执行最左边
        int i = left;
        //定义变量j只想左右边
        int j = right;
        while (i != j) {
            //先由j从右往左检索比基准数小的,如果比基准数小的,就停下,
            while (arr[j] >= base && i != j) {
                j--;
            }
            //再从i从左向右检索比基准值大的,如果比基准数大,就停下
            while (arr[i] <= base && i!=j) {
                i++;
            }
            //代码运行到这里,i停下,j也停下,然后交换i,j位置的元素
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;

        }
        //如果上面的while循环不成立,会跳出这个循环,往下执行
        //也就是i,j相遇,那么就把相遇位置元素和基准数交换

        arr[left]=arr[i];
        //把基准数赋给相遇位置元素
        arr[i]=base;
        //基准数在这里归为,左边都比他小,右边都比他大
        //排基准数左边
        quicksort(arr, left,i-1);
        //排基准数右边
        quicksort(arr, i+1, right);
    }
}

运行结果为:

2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,

可以看出快速排序是不稳定的,另外平均时间复杂度为O(nlogn),最好情况O(nlogn),最坏情况O(N^2).在代码中j和i向前查找的条件一定得想好到底那边大,另外此排序采用递归就可以实现,不用每一步都用代码写出来。
6.基数排序
  基数排序的基本思想:首先基数排序采用的是非比较排序,对于一组数字,先按照其个位对它们进行排序,然后下一次的排序再上一次的排序结果上再按照其十位进行排序,直到后面排完所有的数字。可以看出基数排序的最多次数为该组数字中,最大的数字的长度。
时间复杂度:O(O(n * k) )
稳定性:稳定

package org.westos.demo1;

import java.util.Arrays;

public class radixSort {
    public static void main(String[] args) {
        int[]arr = {12,123,212,45,78,678,554,124,765,234,3454,7652,3453,2346};
        sortArray(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void sortArray(int[] arr)  {
        //定义一个二维数组
        int [][]tempArr = new int[10][arr.length];//整个临时数组里面的一维数组的长度的最大值和待排序的数组的长度一样
        //定义一个统计的一维数组,可以统计二维数组每个桶中放了多少个元素
        int []counts = new int[10];//数组长度和二维数组的长度保持一致
        //获取数组中的最大数字
        int max = getMax(arr);
        //计算数组中的最大的数字总共有几位,就是需要比较的轮次
        int len = String.valueOf(max).length();
        for (int i = 0,n = 1; i < len; i++,n *= 10) {
            for (int j = 0; j < arr.length; j++) { 
                //获取每个位上的数字
                int ys = arr[j] / n%10;
                //counts[ys]++就是二维数组中的桶放入一个数字,那么该统计数组对于该位置的统计个数就加一
                tempArr[ys][counts[ys]++] = arr[j];
            }
            //遍历统计数组,取出二维数组中的元素,放回原数组
            int index = 0;
            for (int k = 0; k < counts.length; k++) {
                if(counts[k]!=0);//判断统计数组中元素的个数,如果是零就不必执行下一步了,可以想想原理图就可以理解
                for (int h = 0; h < counts[k]; h++) {
                    arr[index] = tempArr[k][h];
                    index++;
                }
                //一轮清零统计的个数
                counts[k]=0;
            }
        }

    }

    private static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        return max;
    }
}

运行结果为:

[12, 45, 78, 123, 124, 212, 234, 554, 678, 765, 2346, 3453, 3454, 7652]

http://www.niftyadmin.cn/n/1462269.html

相关文章

StringBuffer和StringBuilder的用法区别

StringBuffer和StringBuilder的区别   StringBuffer线程安全&#xff0c;执行速度慢&#xff0c;StringBuilder线程不安全&#xff0c;但是速度快。 StringBuffer()   线程安全的可变字符序列。一个类似于String的字符串缓冲区&#xff0c;但不能被修改。虽然在任意时间电上…

Java中正则表达式以及Pattern和Matcher类

正则表达式   正则表达式就是正确规则的表达式&#xff0c;是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串&#xff0c;就是一种规则的应用。 规则字符在java.util.regex Pattern类中 字符 x 字符 x。举例&#xff1a;a’表示字符a\ 反斜线字符。\n …

Java中基本类型包装类(拆装箱)以及其中Integer的特性

为了对基本数据类型进行更多的操作&#xff0c;Java就针对每一种基本数据类型提供了对应的类类型。 常用操作之一&#xff1a;基本数据类型与字符串之间的转换。 基本类型与其对应的包装类类型。 基本类型对应的包装类byteByteshortShortintIntegerlongLongfloatFloatdoubleDo…

Java中的常用类(Arrays类,Math类,Random类,System类)

1.Arrays类 针对数组进行操作的工具类&#xff0c;提供了排序查找等功能 成员方法 public static String toString(int [] a) 返回指定数组内容的字符串表示形式。public static void sort(int [] a) 对指定的 int 型数组按数字升序进行排序。public static void sort(int[]…

Java中的常用类(BigDecimal类,Date类,SimpleDateFormat类,Calendar类)

1.BigDecimal   由于在计算的时候&#xff0c;float类型和double类型很容易丢失精度&#xff0c;所以&#xff0c;为了能精确的表示&#xff0c;计算浮点数&#xff0c;Java提供了BigDecimal类。不可变的&#xff0c;任意竞速的有符号十进制数。 构造方法 public BigDecimal…

Java集合框架中的ArrayList和collection

集合的由来   面向对象语言的体现都是以对象的形式&#xff0c;所以为了方便对多个对象的操作&#xff0c;Java就提供了集合类。 继承体系 数组与集合的区别 长度区别 数组的长度是固定的集合的长度是可变的。 存储数据类型的区别 数组可以存储基本数据类型&#xff0c;也…

Java集合框架中的List和LinkedList以及Vector

list集合基础 实现了collection接口list接口特性&#xff1a;是有序的&#xff0c;元素是可以重复的允许元素为null 1.list LIst的三个子类的特点 ArrayList 底层数据结构是数组&#xff0c;查询快&#xff0c;增删慢线程不安全&#xff0c;效率高 Vector 底层数据结构是数…

去除数组中的重复元素

方法一 import java.util.Arrays;public class Mytest1 {public static void main(String[] args) {int[] a {2, 1, 3, 2, 4, 5, 4, 6, 7, 4, 6, 7, 4};//先排序&#xff0c;使数组有序Arrays.sort(a);System.out.println(Arrays.toString(a));StringBuffer sb new StringBu…