博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
排 序 算 法
阅读量:3930 次
发布时间:2019-05-23

本文共 7141 字,大约阅读时间需要 23 分钟。

排序算法

1 冒泡排序

从第一个开始比较,找到最大的值,让其交换到数组的最后面,然后就第二大,第三大·····,不断比较,当前一个的值都比后一个的值小的时候,数组排序结束。

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

稳定性较强,因为总是相邻的两个元素在交换

public class TenAlgorithm {
public static void main(String[] args) {
int[] a={
4,5,8,9,6,2,3,5,4,58,15,9}; for (int i:bubbleSort(a)) {
System.out.print(i+","); } } public static int[] bubbleSort(int[] arr){
int len=arr.length; if(arr==null||len<2){
return arr; } int temp=0; for (int i = 0; i < len; i++) {
for (int j=0;j
arr[j+1]){
temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } return arr; }}

2 选择排序(冒泡排序的进化版:个人看法)

从无序的区间不断找出最小值,挑选n-1次(最后一个元素不用挑)。与冒泡排序的不同在于,冒泡排序每次比对都要交换元素的位置,而选择排序只有经过一轮的比较后,找到这一轮最小值的下标才会去交换元素,相比之下节省了不少的资源。

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

稳定性较差,因为有可能第一个和最后一个元素交换

public static int[] selectSort(int[] arr){
int len=arr.length; if(arr==null||len<2){
return arr; } for (int i = 0; i
arr[j]){
minIndex=j; } } swap(arr,minIndex,i); } return arr; } public static void swap(int[] arr,int minIndex,int i){
int temp=arr[i]; arr[i]=arr[minIndex]; arr[minIndex]=temp; }

3 插入排序

插入排序就是把未排序的元素插入到已排序的数组中。

  • 把左边的第一位看做已经排好序的数组,右边的为没有拍好序的数组,通过向左边一个个的插入右边的元素从而实现数组的排序。

  • 从数组的第二个元素开始遍历,在进入第一个循环后,需要保存当前位置元素。然后进行比较,如果前一个元素大于保存的元素,则前一个元素向后移动一位,一直如此,直到前一个元素小于保存的元素时,停止。

  • 然后跳出内循环,因为当前j位置的元素是小于保存的元素,并且其后一个的元素已经向后移动了一位,所以保存的元素要插入的位置是j+1

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

稳定性较强,因为在相邻的元素中移动

public static int[] insertionSort(int[] arr){
int len=arr.length; if(arr==null||len<2){
return arr; } for (int i = 1; i < len; i++) {
int insert=arr[i]; int j=i-1; for (; j>=0&&arr[j] >insert ; j--) {
//如果前一个大于后一个,则移动前一个到后面 arr[j+1]=arr[j]; } arr[j+1]=insert; } return arr; }

小规模数据或者基本有序时高效(数据有序程度越高,越高效(移动少))

4 希尔排序(插入排序的改进版)

首先把一个较大的数据集合分割成若干个小组(逻辑上的分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高。

  • 希尔排序从某个角度来看,可以说是改进版版的插入排序,它不再是向插入排序那样一个个的从未排序的序列向已排序的序列进行插入;而是按照数组的长度的一半为间隔,这个间隔长度的为一组,例如{9,8,7,6,5,4,3,2}这种,一开始以4为间隔,就是{9,5},{8,4},{7,3},{6,2},对里面的进行插入排序得到{5,4,3,2,9,8,7,6},然后再长度对半为2分组,就是{5,3,9,7},{4,2,8,6}得到{3,2,5,4,7,6,9,8},再重复上面的操作,最后得到{2,3,4,5,6,7,8,9}
public static int[] shellSort(int[] arr){
int len=arr.length; if(arr==null||len<2){
return arr; } //进行分组,最开始时的增量为数组长度的一半 for (int gap =len/2 ; gap>0; gap/=2) {
//比较完一组后,再移动下一组(按一组组这样进行插入排序) for (int i = gap; i < len; i++) {
int insertValue=arr[i]; //j已经移到第前grap个了 int j=i-gap; //插入的时候按组进行插入(组内元素两两相隔gap) for (; j >=0 &&insertValue

希尔排序的复杂度和增量序列是相关的

{1,2,4,8,…}这种序列并不是很好的增量序列,使用这个增量序列的时间复杂度(最坏情形)是O(n^2)

Hibbard提出了另一个增量序列{1,3,7,…,2^k-1},这种序列的时间复杂度(最坏情形)为O(n ^ 1.5)

Sedgewick提出了几种增量序列,其最坏情形运行时间为O(n^1.3),其中最好的一个序列是{1,5,19,41,109,…}

5 归并排序

归并排序是建立在归并操作的一种高效的排序方法,该方法采用了分治的思想,比较适用于处理较大规模的数据,但比较耗内存。

  • 创建一个和原来数组一样长度的临时数组来临时存储数据,然后对原数组不停的进行二分,一直到分不了即只有一个元素为止。然后自底向上开始合并,当回到第一层的时候,数组即排序完毕。
public static int[] mergeSortArr(int[] arr){
int len=arr.length; if(arr==null||len<2){
return arr; } int[] temp=new int[len]; //right的长度为len-1,因为从0开始 mergeSort(arr,temp,0,len-1); return arr; } private static void mergeSort(int[] arr, int[] temp, int left, int right) {
//当left==right是停止 if(left
center mergeSort(arr,temp,left,center); //右边center+1->right mergeSort(arr,temp,center+1,right); //合并数组 merge(arr,temp,left,center,right); } } private static void merge(int[] arr, int[] temp, int left, int center, int right) {
int i=left,j=center+1; for (int k = left; k <=right; k++) {
//如果i大于center,表示i的数组已经赋值完,不需要再比较,直接copy数组j //如果j大于right,表示j的数组已经赋值完,不需要再比较,直接copy数组i if(i>center){
temp[k]=arr[j++]; }else if(j>right){
temp[k]=arr[i++]; }else if(arr[i]<=arr[j]){
temp[k]=arr[i++]; }else {
temp[k]=arr[j++]; } } //从临时数组传回原来数组 for (int k = left; k <=right; k++) {
arr[k]=temp[k]; } }

时间复杂度:O(N log N)

稳定性较为稳定,因为在合并的时候,如果相等,选择前面的元素到辅助数组。

6 快速排序(归并排序的进化版)

  • 选取数组的第一个元素为基准,然后分别从第二个和最后一个开始向右、向左比较。左边的如果比基准小就继续移动下一位,直到比基准打为止,而右边的如果比基准大,就继续移动上一位,一直到比基准小为止,然后交换左右两个指针的元素,继续比较,直到左边的指针大于右边的指针,即第一轮交换完成,然后把保存的基准元素和右边指针当前位置的元素进行交换。
  • 然后以第一轮交换完的基准元素的位置为界,左边的数组和右边的数组分别重复上面的操作,直到最后其分出来的数组的左指针大于右指针为止,即表明数组已经不可以再继续划分。即数组排序完成

时间复杂度:O(nlogn),最坏的情况为O(n^2)

稳定性较差

它不像归并排序那样,需要额外的辅助空间,而且在分割调整的时候,不像归并排序那样,元素还要在辅助数组与源数组之间来回复制。

/**     * 快速排序     *     * @param arr     * @return int[]     */    public static int[] partitionSort(int[] arr) {
int len = arr.length; if (arr == null || len < 2) {
return arr; } partition(arr, 0, len - 1); return arr; } private static void partition(int[] arr, int left, int right) {
if (left >= right) {
return; } int pivot = arr[left]; int i = left + 1, j = right; while (i < j) {
while (i <= j && arr[i] <= pivot) {
i++; } while (i <= j && arr[j] >= pivot) {
j--; } if (i >= j) {
break; } int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } if (pivot > arr[j]) {
arr[left] = arr[j]; arr[j] = pivot; } partition(arr, left, j - 1); partition(arr, j + 1, right); }

7 计数排序

基本思想:把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,例如temp[i]=m,表示元素i一共出现了m次。最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来的数据是有序的。

  • 并且如果我们是根据 max 的大小来创建对应大小的数组,假如原数组只有 10 个元素,并且最小值为 min = 10000,最大值为 max = 10005,那我们创建 10005 + 1 大小的数组不是很吃亏?最大值与最小值的差值为 5,所以我们创建大小为 6 的临时数组就可以了,这样可以节省空间浪费。
  • 也就是说,我们创建的临时数组大小 (max – min + 1)就可以了,然后我们再把 min作为偏移量。

时间复杂度:O(n)

private static int[] countSort(int[] arr){
int len = arr.length; if (arr == null || len < 2) {
return arr; } int min=arr[0]; int max=arr[0]; //寻找数组的最大值与最小值 for (int i = 0; i < len; i++) {
if(max
arr[i]){
min=arr[i]; } } int d=max-min+1; //创建大小为max的临时数组 int[] temp=new int[d]; //统计元素i出现的次数 for (int i = 0; i < len; i++) {
temp[arr[i]-min]++; } int k=0; //把临时数组统计好的数据汇总到原数组 for (int i = 0; i < d; i++) {
for (int j = temp[i]; j >0 ; j--) {
arr[k++]=i+min; } } return arr; }

参考:https://www.iamshuaidi.com/608.html

转载地址:http://nlvgn.baihongyu.com/

你可能感兴趣的文章
项目采购管理脉络
查看>>
项目管理总结
查看>>
java内存区域的分布
查看>>
JVM的GC是怎么折腾内存的
查看>>
Java跨平台的构思分析
查看>>
linux目录结构名称对照
查看>>
设计的理念
查看>>
多线程专题 - 脉络图
查看>>
javascript 函数,BOM
查看>>
javascript 客户端能力检测
查看>>
javascript DOM详解之DOM1
查看>>
javascript DOM扩展
查看>>
矛盾论读书笔记
查看>>
规则 - 利用CDN缓存
查看>>
什么是统计学中的 Standard Error ( SE )?
查看>>
统计学中的标准差(SD)和 平均值的标准误差(SEM)的区别
查看>>
[数据挖掘与预测分析] 单变量统计分析思考问题
查看>>
[统计学笔记] (十三)指数分析(2)
查看>>
Data Science 到底是什么?
查看>>
机器学习(Machine Learning)和传统的数据统计分析(Data Statistics)有什么区别?
查看>>