排序算法 ✈

来源:互联网 发布:淘宝客的链接怎么看 编辑:程序博客网 时间:2024/05/02 02:55

本部分排序算法知识已托管于Github,详见 排序算法总结 | 欢迎各位star

排序算法的分类

冒泡排序

冒泡排序算法通过多次比较和交换来实现排序,其排序流程如下:
1. 对数组中的各数据,依次比较相邻的两个元素的大小。
2. 如果前面的数据大于后面的数据,就交换这两个数据。经过第一轮的多次比较排序后,便可以将最小的数据排好。
3. 再用同样的方法把剩余的数据逐个进行比较,最后便可以按照从小到大的顺序排好数组各数据。

核心代码:

public void bubbleSort(int[] a,int n){    int i,j;    for(i=0; i<n-1; i++){        boolean exchange = false;        for(j=n-1; j>i; j--){            if(a[j]<a[j-1]){                int temp = a[j];                a[j]=a[j-1];                a[j-1]=temp;                exchange = true;            }        }        if(!exchange)            return;    }}

完整示例代码:bubble.java

快速排序

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
1. 首先设定一个分界值,通过该分界值来将数组分成两部分。
2. 将大于等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于等于分界值,而右边部分中各元素都大于等于分界值。
3. 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分为左右两部分,同样将左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序,再递归排好右侧部分的顺序。当左、右两部分各数据排序完成后,整个数组的排序也就完成了。

核心代码:

public void quickSort(int[] a, int left, int right){        // 快速排序算法    int mid,t;    int rtemp,ltemp;    ltemp = left;    rtemp = right;    mid = a[(left+right)/2];            // 分界值    while(ltemp<rtemp){        while(a[ltemp]<mid){            ltemp++;        }        while(a[rtemp]>mid){            rtemp--;        }        if(ltemp<=rtemp){            t=a[ltemp];            a[ltemp]=a[rtemp];            a[rtemp]=t;            ltemp++;            rtemp--;        }    }    if(ltemp==rtemp){        ltemp++;    }    if(left<rtemp){        quickSort(a, left, ltemp-1);        // 左递归调用    }    if(ltemp<right){        quickSort(a, rtemp+1, right);       // 右递归调用    }}

程序中首先确定分解值为数组中间位置的值,当然也可以选在其他位置,比如数组的第一个数据。然后按照快速排序法的思路进行处理。接着,通过递归调用,处理分界值左侧的元素和右侧的元素。
完整示例代码:quick.java

选择排序

选择排序算法通过选择和交换来实现排序,其排序流程如下:
1. 首先从原始数组中选择最小的1个数据,将其和位于第1个位置的数据交换。
2. 接着从剩下的n-1个数据中选择次小的1个数据,将其和第2个位置的数据交换。
3. 然后不断重复上述过程,直到最后两个数据完成交换。至此,便完成了对原始数组的从小到大的排序。

核心代码:

public void bubbleSort(int[] a,int n){    int i,j;    int index;    for(i=0; i<n-1; i++){        index = i;        for(j=i+1; j<n; j++){            if(a[index]>a[j]){                index = j;            }        }        if(index != i){            int temp = a[index];            a[index]=a[i];            a[i]=temp;        }    }}

完整示例代码:select.java

堆排序

堆排序(Heap Sort)算法是基于选择排序思想的算法,其利用堆结构和二叉树的一些性质来完成数据的排序。堆结构是一种树结构,准确的说是一个完全二叉树。在这个树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件:
○ 如果按照从小到大的顺序排序,要求非叶结点的数据要大于或等于其左、右子结点的数据。
○ 如果按照从大到小的顺序排列,要求非叶结点的数据要小于或等于其左、右子结点的数据。

堆排序过程
一个完整的堆排序需要经过反复的两个步骤:构造堆结构和堆排序输出。下面首先分析如何构造堆结构。

构造堆结构就是把原始的无序数据按前面堆结构的定义进行调整。首先,需要将原始的无序数据放置到一个完全二叉树的各个结点中。
然后由完全二叉树的下层向上层逐层对父结点的数据进行比较,使父结点的数据大于子结点的数据,这里需要使用“筛”运算进行节点数据的调整,直到所有结点最后满足堆结构的条件为止。在执行筛运算时,值较小的数据将逐层下移。

插入排序

插入排序算法通过比较和插入来实现排序,其排序流程如下:
1. 首先对数组的前两个数据进行从小到大的排序。
2. 接着将第3个数据与排好序的两个数据比较,将第3个数据插入合适的位置。
3. 然后,将第4个数据插入已排好序的前3个数据中。
4. 不断重复上述过程,直到把最后一个数据插入合适的位置。最后,便完成了对原始数组从小到大的排序。

核心代码:

public void bubbleSort(int[] a,int n){    int i,j;    int t;    for(i=1; i<n; i++){        t=a[i];        j=i-1;        while(j>=0 && t<a[j]){  // 后移每个比t大的元素            a[j+1]=a[j];            j--;        }        a[j+1]=t;       // 将腾出来的那个位置插入新元素    }}

插入排序时,如果元数据已经基本有序,则排序的效率就可大大提高。另外,对于数量较小的序列使用直接插入排序,因需要移动的数据量较小,其效率也较高。

完整示例代码:insert.java

折半插入/二分排序

折半插入算法是在插入排序算法的基础上,在已排序序列中选择插入位置时,优化使用二分查找方法,使得排序效率提升。
核心代码:

public static void BinaryInsertSort(int[] a) {    int low, high;    int mid, temp;    for (int i = 0; i < a.length; i++) {        temp = a[i];        low = 0;        high = i - 1;        while (low <= high) {           // 折半查找的过程            mid = (low + high) / 2;            if (temp < a[mid])                high = mid - 1;            else                low = mid + 1;        }        for (int j = i - 1; j > high; j--)            a[j + 1] = a[j];        a[high + 1] = temp;    }}

完整示例代码:binaryInsert.java

Shell排序

Shell排序算法严格来说基于插入排序的思想,其又称为希尔排序或者缩小增量排序。Shell排序算法的排序流程如下:
1. 将有n个元素的数组分为n/2个数字序列,第1个数据和第n/2+1个数据为一对,第2个数据和第n/2+2个数据为一对,……
2. 一次循环使每一个序列对排好序
3. 然后,再变为n/4个序列,再次排序。
4. 不断重复上述过程,随着序列减少最后变为一个,也就完成了整个序列。

核心代码:

public void bubbleSort(int[] a,int n){    int i,j,r;    int t;    for(r=n/2;r>=1;r/=2){   // 设置间距r 分组比较        for(i=r; i<n; i++){            t=a[i];            j=i-r;            while(j>=0 && t<a[j]){  // 后移每个比t大的元素                a[j+r]=a[j];                j-=r;            }            a[j+r]=t;   // 将腾出来的那个位置插入新元素        }    }}

在程序中使用了三重循环嵌套。最外层的循环用来分解数组元素为多个序列,每次比较两数的间距,直到其值为0就结束循环。下面一层循环按设置的间距r,分别比较对应的数组元素。在该循环中使用插入排序法对指定间距的元素进行排序。

完整示例代码:shell.java

归并排序

归并排序(Merge Sort)算法就是将多个有序数据表归并成一个有序数据表。如果参与归并的只有两个有序表,则称为二路归并。对于一个原始的待排序序列,往往可以通过分割的方法来归结为多路归并排序。

一个待排序的原始数据序列进行归并排序的基本思路是,首先将含有n个结点的待排序数据序列看作由n个长度为1的有序子表组成,将其依次两两归并,得到长度为4的若干有序子表······,重复上述过程,一直到最后的子表长度为n,从而完成排序过程。

算法时间复杂度分析

● 冒泡排序算法:冒泡排序中,存在二层循环遍历,所以平均时间复杂度为O(n²),最坏情况下的时间复杂度为O(n²);
● 快速排序算法:快序中,while循环部分时间复杂度为O(logn),左递归和右递归总和的时间复杂度为O(n),所以总的平均时间复杂度为O(n²);
● 选择排序算法:选择排序中,依次从数组中选取最小的,次小的,… 从而得到整个序列。两层循环遍历,所以时间复杂度为O(n²),最坏情况下的时间复杂度为O(n²);
● 堆排序算法:从堆中,一次堆排序挑选最小(或最大)元素的时间复杂度为O(logn),一共要进行n次堆排序得到有序队列,所以平均时间复杂度为O(nlogn),最坏情况下的时间复杂度为O(nlogn);
● 插入排序算法:插入排序中,两层循环,第一层遍历n个元素,第二层遍历在已排序队列中逐一向前比较,找到合适的位置插入,所以平均时间复杂度为O(n²),最坏情况下的时间复杂度为O(n²);
● 折半插入排序算法:在插入排序的基础上进行优化,在第二层遍历在已排序队列中通过二分查找的方式,找到合适的位置插入,所以平均时间复杂度为O(n²),最坏情况下的时间复杂度为O(n²);
● Shell排序算法:在插入排序的基础上进行优化,通过先将队列进行n/2,n/4,n/8,…分组,时间复杂度为O(n½),再对每个小组分别进行插入排序,时间复杂度为O(n),所以平均时间复杂度将会缩减为O(n³/₂),最坏最坏情况下的时间复杂度为O(n²);:
● 归并排序算法:平均时间复杂度为O(nlogn),最坏情况下的时间复杂度为O(nlogn);

原创粉丝点击