常见的几种数组排序方法

---恢复内容开始---

一、研究数组排序的意义:

数据结构中,排序算法各有用处,不同的排序方法有不同的时间复杂度与空间复杂度。为了能够依据不同情况,选用不同的排序方法解决不同的问题。

二、常见的数组排序方法:

以下研究,默认是对操作数组进行从小到大的排序。使用语言是Java。

1.选择排序法

选择排序法是将需要操作的数组分为已排序部分和未排序部分两部分。未排序的数组元素中,最小(或最大)的元素依次按照获得顺序放入已排序的元素中。

public static boolean sortByChoice(int[] arr) {    if(arr == null || arr.length == 0) {        return false;    }    for (int i = 0; i < a.length; i++) {        for (int j = i + 1; j < a.length; j++) {            int swap = arr[j];            if (swap < arr[i]) {                arr[j] = arr[i];                arr[i] = swap;            }        }    }    return true;}

在上述的排序方法中,我们是直接交换了数组中元素的值。那么请看下面一段代码:

public static boolean sortIndexByChoice(int[] a) {    if(a == null || a.length == 0) {        return false;    }    for (int i = 0; i < a.length; i++) {        int mark = i;        //记录需要交换的元素的下标        for (int j = i + 1; j < a.length; j++) {            if (a[mark] > a[j]) {                mark = j;            }            //交换目标            int swap = a[mark];            a[mark] = a[j];            a[j] = swap;        }    }    return true;}

上述方法中,通过定义指针变量mark指向(记录)数组中最小元素(的下标),直接交换指针指向的元素未排序部分首位的值进行交换。

选择排序法的时间复杂度为O(n^2);最多交换次数为N-1次。

2.冒泡排序法(起泡排序法)

冒泡排序法是在每次循环排序过程中,每次交换需要交换的相邻两个元素。冒泡排序法虽然理论上,时间复杂度和选择排序法相等,都是O(n^2),但是实际情况下,由于冒泡排序法较难避免重复比较,最坏情况下,程序执行比较的次数为n^n,消耗时间过长。

public static boolean bubbleSort(int[] a) {    if(a == null || a.length == 0) {        return false;    }    for (int i = 0; i < a.length-1;i++) {        for (int j = 1; j < a.length - i; j++) {            if (a[j-1] > a[j]) {                int temp = a[j];                a[j] = a[j-1];                a[j-1] = temp;            }        }    }    return true;} 

将数组中相邻元素多次进行各个比较,它存在一个问题,即使数组已经有序,函数仍然在执行。因此,冒泡排序法可以尝试进行优化。请看下一段代码:

public static boolean bubbleSort1(int[] a) {    if(a == null || a.length == 0) {        return false;    }    for (int i = 0; i < a.length-1;i++) {        boolean flag = false;        for (int j = 1; j < a.length - i; j++) {            if (a[j-1] > a[j]) {                int temp = a[j];                a[j] = a[j-1];                a[j-1] = temp;                if(!flag) {                    flag = true;                }            }        }        if(!flag) {            break;        }    }    return true;} 

相比起最开始的算法,加入了一个标识变量flag,用于标识是当前循环是否进行过交换。如果当前循环未进行交换,则终止外围循环。我们不难发现,每次循环后最大的数值会向后移动,这些元素被移动到数组末位之后是不需要比较的,而其实多轮循环中,无论第一种算法还是第二种算法,这些元素仍然产生比较的过程。是否可以再次进行优化?请看下面一段代码:

public static boolean bubbleSort2(int[] a) {    if(a == null || a.length == 0) {       return false;    }        //定义下标变量endPos代表下一次循环中,最后一个需要比较的元素的下标。    int endPos = a.length - 1;     while(endPos > 0) {        int flag = 1;        for(int i = 1; j <= endPos; j++) {            if (a[j-1] > a[j]) {                int temp = a[j];                a[j]= a[j-1];                a[j-1] =temp;                                //由于该指针不断被重新赋值,将该指针指向最后一位参与交换的元素下标。                flag = j;            }        }        //调整endPos,使下一次循环只对flag指向的元素前面的元素进行比较与排序。flag后面的元素已拍好。        endPos = flag - 1;     }}

与bubbleSioer1相似的是,额外定义了一个指针变量flag,指向每次循环最后一次参与交换的数组元素。因此,每次循环只对未被排序的元素进行比较。

接下来,请看下面一段代码:

public static boolean bubbleBothway(int[] arr) {    if(arr == null || arr.length == 0) {        return false;    }    int high = arr.length - 1;    int low = 0;    while (low < high) {        for(int i = low ;  i < high;i++)  {            if(arr[i] > arr[i+1]) {                int temp = arr[i];                arr[i] = arr[i+1];                arr[i+1] = temp;            }        }            high--;

for(int j= high; j > low;  j--) {                if(arr[j - 1] > arr [j]) {                    int swap = arr[j - 1];                    arr[j - 1] = arr[j];                    arr[j] = swap;                }            }            low++;    }    return true;}

这种算法是冒泡程序法的一种新的改进方法。相比于前面三种方法,它通过双向比较,同时,规避了重复比较的情况,效率相对前面更高。

3.插入排序法

插入排序法是将未排序部分的首位元素抽取出来,插入至已排序元素的合适位置。

public static boolean insertSort(int[] arr) {    if(arr == null || arr.length == 0) {        return false;    }    for(int i = 1; i < arr.length; i ++) {        int temp = arr[i];        int j = i - 1;        while (temp < arr[j]) {            arr[j + 1] = arr[j];    //后移前面的元素            j--;            if(j < 0) {     //需要插入在首位时                break;            }        }        arr[j + 1] = temp;      //插入元素    }    return true;}

插入排序法理解起来相对简单,时间复杂度同为O(n^2)。

4.希尔排序法

希尔排序法是插入排序法的一种改进。希尔排序法的基本思想是:将数组分成若干个子序列,对于若干个子序列,进行插入排序,然后将这些序列进行插入排序。希尔排序法的核心问题就是在选择序列上。这些序列是采取一定的增量数据组成一个序列,随着排序过程,这个增量会减小。一般情况下,初始增量取数组长度的一半,然后每次再进行折半,直到增量为1一般情况下,初始增量取数组长度的一半,然后每次再进行折半,直到增量为1。请看下面一个例子:

假设说,下面有一个数组

    int arr[10]= {10, 9, 7, 2, 5, 6, 8, 4, 19, 1};

假设此次增量取5,那么就有以下子序列

    {a[0] = 10, a[5] = 6}    {a[1] = 9,  a[6] = 8}    {a[2] = 7,  a[7] = 4}    {a[3] = 2,  a[8] = 19}    {a[4] = 5,  a[9] = 1}

然后,将每个增量通过排序后,得到下面的数组:

    {6, 8, 4, 2, 1, 10, 9, 7, 19, 5}

然后,将增量折半,就有新的子序列,再将子序列插入排序:

    {6, 4, 1, 9, 19} ->{1, 4, 6, 9, 19}    {8, 2, 10, 7, 5} ->{2, 5, 7, 8, 10}

因此,得到新数组

    {1, 2, 4, 5, 6, 7, 9, 8, 19, 10}

最后,对数组直接进行一次插入排序

    {1, 2, 4, 5, 6, 7, 8, 9, 10, 19}

使用java实现希尔排序法的源代码如下:

    public static boolean shellSort(int[] arr) {        if(arr == null || arr.length == 0) {            return false;        }        int h = arr.length / 2;         //定义增量变量h        while(h >= 1) {            for (int i =h; i< arr.length; i++)  {                int j = i - h;          //依据增量,开始分组。                int temp = arr[i];                                          /*子序列插入位置后的元素向后移动*/                while(j>=0 && arr[j]>temp) {                    arr[j + h] = arr[j];                            j -= h;                                         }

       arr[ j+h ] = temp;      //移动完成,插入元素值            }            h  /= 2;                    //缩小增量        }        return true;    }

希尔排序法相对于插入排序法,减少了数组中大量的元素移动的过程。希尔排序法思路和代码相对复杂。

5.快速排序法

快速排序法是最受到程序员欢迎的一种算法。它的思想方法是将一个容量较大的数组分成多个小数组,然后将各个小数组再次分成多个更小的数组,直到元素达到一定值时,开始比较各个小数组中的元素。

5.1递归法简介

在讨论快速排序法的问题之前,我们先说一下什么是函数的递归法:

先看一下下面的数学问题:

已知等差数列的递推公式a(n) = a(n-1) +2,其初始项a(1)=2,求其第18项a(18)。

的确,我们可以用求通用公式的方法求得结果。这里,我们不求结果,先讨论一下这个展开过程

这个数列求a(18)可以看做:

a(18)= a(18-1) + 2     =(a((18-1)-1)+2)+2     =((a(((18-1)-1)-1)+2)+2)+2     ...

递推套用递推,直到找到基准值位置。其实这就是递归过程。

用java描述可以是:

public static int a(int x) {    if (x = 1) {        return 0;    } else {        return a(x - 1) + 2    }}

递归就是假定函数f(x),通过f(x)和f(ax + b)(a,b均为常数的关系,与当x=x0(x0为任意常数)时f(x)的值,求得给定任一x时f(x)的方法。f(x)=f(ax+b)+c为递归函数,f(x0)=f为基准。

5.2 快速排序法

现在,我们回到算法分析上。快速排序法的源代码如下:

    public static boolean fastSort(int[] arr, int left ,int right) {        if(arr == null || arr.length == 0||left<0||right>arr.length) {            System.out.println("传参不合法!");            return false;        }        if (left < right) {            int s = arr[left];            int i = left;            int j = right +1;            while(true) {                //向右找大于s的元素下标                //基本语法问题:由于++i已对i操作,这个while后面是一句空语句,用“;”隔开。                while(i+1 < arr.length && arr[++i] < s) ;                //  向左查找小于s的元素值的下标                while(j-1> - 1 &&arr[--j]>s);                if(i >= j) {                    //  如果左标i大于或等于右标j,退出死循环                    break;                } else {                    //  交换i与j的位置                    int temp = arr[i];                    arr[i] = arr[j];                    arr[j]= temp;                }            }             arr[left] = arr[j];            arr[j] = s;            System.out.println(Arrays.toString(arr));            //对左侧数组递归            fastSort(arr, left, j-1);           // 对右侧数组递归;            fastSort(arr, j+1, right);        }        return true;    }

快速排序法的时间复杂度为O(n*log n),但是正因为递归调用,面对容量较大的数组时,稳定性较差。

6.写在最后

其实,数组排序的方法还有很多,这里,只讨论一些比较基础的,也是比较受欢迎的排序算法。有兴趣的话,可以去学习《数据结构》,了解更多关于排序的算法。最后,本文会有很多不足之处,提前感谢各位对本文不足之处提出建议。谢谢大家。

---恢复内容结束---

原文地址:https://www.cnblogs.com/zhFinal/p/10192489.html

时间: 2024-08-12 07:49:12

常见的几种数组排序方法的相关文章

常见的2种断点方法

[常见的2种断点方法] 1.中断断点. 2.内存断点. 常见的2种断点方法,码迷,mamicode.com

IOS常见的三种回调方法介绍

认识下三种IOS常见的回调模式. 代理模式作为IOS中最常见的通讯模式,代理几乎无处不在. 这里有一个数组,我们首先通过代理的方式将数组传递到其他方法中去. 设置协议及方法 @protocol CallBackDelegate; @interface ViewController : UIViewController @property (weak, nonatomic) id<CallBackDelegate> delegate; @end @protocol CallBackDelegat

iOS里常见的几种信息加密方法简单总结

一.MD5加密 MD5加密是最常用的加密方法之一,是从一段字符串中通过相应特征生成一段32位的数字字母混合码. MD5主要特点是 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样(也不是绝对的,但基本是不能一样的). MD5算法还具有以下性质: 1.压缩性:任意长度的数据,算出的MD5值长度都是固定的. 2.容易计算:从原数据计算出MD5值很容易. 3.抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别. 4.弱抗碰撞:已知原数据和其MD5值,想找到一个

c语言常见的几种排序方法总结

一:选择排序和冒泡排序 这两种排序比较简单,直接贴出代码: 1 #include <stdio.h> 2 3 void choose_sort(int *arr, int n); 4 void bubble_sort(int *arr, int n); 5 void show(int *arr, int n); 6 7 int main() 8 { 9 int arr[10] = {10, 8, 3, 15, 18, 16, 11, 9, 7, 6}; 10 11 /*选择排序*/ 12 ch

Java中数组常见的几种排序方法!

数组的定义: int[] arr = new int[5]; int[] arr1 = {1,2,3,4,5}; long[] arr2 = new long[6]; String[] strs = new String[5]; Person[] ps = new Person[5]; 数组的操作: int[] arr = {45, 34, 53, 43}; Arrays.sort(arr); System.out.println(Arrays.toString(arr)); // 二分搜索法(

大数据开发之常见九种数据分析方法

今天给大家分享一篇关于大数据开发常见的9种数据分析方法,首先数据分析是从数据中提取有价值信息的过程,过程中需要对数据进行各种处理和归类,只有掌握了正确的数据分类方法和数据处理模式,才能起到事半功倍的效果,以下是数据分析员必备的9种数据分析思维模式: 1.分类 分类是一种基本的数据分析方式,数据根据其特点,可将数据对象划分为不同的部分和类型,再进一步分析,能够进一步挖掘事物的本质. .在入门学习大数据的过程当中有遇见学习,行业,缺乏系统学习路线,系统学习规划,欢迎你加入我的大数据学习交流裙:529

iOS 几种加密方法

iOS常见的几种加密方法 普通加密方法是讲密码进行加密后保存到用户偏好设置中 钥匙串是以明文形式保存,但是不知道存放的具体位置 1.base64加密 base64 编码是现代密码学的基础 基本原理: 原本是 8个bit 一组表示数据,改为 6个bit一组表示数据,不足的部分补零,每 两个0 用 一个 = 表示 用base64 编码之后,数据长度会变大,增加了大约 1/3 左右.(8-6)/6可进行反向解密 Xcode7.0 之后出现的 编码有个非常显著的特点,末尾有个 = 号 将文件进行加密 /

Python中执行系统命令常见的几种方法--转载

Python中执行系统命令常见的几种方法 Python中执行系统命令常见的几种方法有: (1)os.system # 仅仅在一个子终端运行系统命令,而不能获取命令执行后的返回信息 # 如果再命令行下执行,结果直接打印出来 例如: >>> import os >>> os.system('ls') chk_err_log.py CmdTool.log  install_log.txt  install_zabbix.sh  manage_deploy.sh  MegaSA

SQL Server查询优化方法(查询速度慢的原因很多,常见如下几种) .

SQL Server查询优化方法(查询速度慢的原因很多,常见如下几种) 标签: sql server优化数据库服务器 2014-12-31 10:13 11988人阅读 评论(0) 收藏 举报 本文章已收录于: 今天看到一位博友的文章,觉得不错,转载一下,希望对大家有帮助,更多文章,请访问:http://blog.haoitsoft.com 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存