平均时间复杂度为O(nlogn)的排序算法

本文包括

1.快速排序

2.归并排序

3.堆排序

1.快速排序

快速排序的基本思想是:采取分而治之的思想,把大的拆分为小的,每一趟排序,把比选定值小的数字放在它的左边,比它大的值放在右边;重复以上步骤,直到每个区间只有一个数。此时数组已经排序完成。

快速排序最重要的是partition函数功能的实现,也就是将比选定基数小的值放在他的左边,比选定基数大的值放在它的右边的功能函数。

熟悉快速排序的人也许都会有自己的partition函数的写法。此处,提供两种不同的partition函数写法。

例1:

public void partition(int[] a,int start,int end){
        if(start>=end)
            return ;
        int low=start;
        int high=end;
        int index = a[low];//选定基数
        while(low<high){
            while(a[high]>index && high>low)//找到右边比基数小的数字
                high--;
            if(low<high)
                a[low++] = a[high];
            while(a[low]<index && high>low)//找到左边比基数大的数字
                low++;
            if(low<high)
                a[high--] = a[low];
        }
        a[low] = index;
        partition(a,start,low-1);
        partition(a,low+1,end);
    }

例2:

public void partition1(int[] a,int start,int end){
        if(start>=end)
            return ;
        int index = start;
        swap(a[index],a[end]);
        int small=start-1;
        for(index = start;index<end;index++){
            if(a[index]<a[end]){//若比选定基数小,则与前面比大于基数的数字进行交换
                small ++ ;
                if(small!=index){
                    swap(a[small],a[index]);
                }
            }
        }
        small++;
        swap(a[small],a[end]);
        partition1(a,start,small-1);
        partition1(a,small+1,end);
    }

快速排序

public void quickSort(int[] a){
        partition1(a,0,a.length-1);
}

快速排序的时间复杂度为O(nlogn),最坏时间复杂度为O(n^2),最坏的情况适之每次区间划分的结果都是基准关键字的最左边或者右边,即选择的数字是待排序列中最小或者最大的。当n较大时使用较好。

2.归并排序

归并排序是利用递归与分治技术将数据序列划分为越来越小的半子表,再对半子表进行排序,最后在利用递归方法将排好序的半子表合并成为越来越大的有序序列。

归并排序中的归,就是递归的意思,递归将数组分成小的字数组。

例如数组[13,6,8,11]

先将数组分成[13,6],[8,11]两个数组,再将数组分为[13],[6],[8],[11]。然后进行合并,[6,13],[8,11],最后合并为[6,8,11,13]。

因为归并排序需要将数组元素分割存储,所以空间复杂度为O(n),是以空间换时间的做法。

public void Merge(int[] a,int start,int mid,int end){
        int length1,length2; //新数组的大小
        int i,j,k;
        length1 = mid - start + 1;
        length2 = end - mid;
        int[] L = new int[length1];
        int[] R = new int[length2];
        for(i=0,k=start;i<length1;i++,k++)//将前半部分数组存入L中
            L[i] = a[k];
        for(i=0,k=mid+1;i<length2;i++,k++)//将后半部分数组存入R中
            R[i] = a[k];
        for(k=start,i=0,j=0;i<length1&&j<length2;k++){//分别从两个数组中读取数据,取较小的放入员数组中
            if(L[i]<R[j]){
                a[k] = L[i];
                i++;
            }
            else{
                a[k] = R[j];
                j++;
            }
        }
        if(i<length1)//将L中还有的剩余数字存入原数组
            for(j=i;j<length1;j++,k++)
                a[k] = L[j];
        if(j<length2)//将R中还有的剩余数字存入元素族
            for(i=j;i<length2;i++,k++)
                a[k] = R[i];
    }

调用归并排序。

public void MergeSort(int[] a,int start,int end){
        if(start<end && a.length>1){
            int mid = (start+end)/2;
            MergeSort(a,start,mid);
            MergeSort(a,mid+1,end);
            Merge(a,start,mid,end);
        }
    }

归并排序的时间复复杂度为O(nlogn),且归并排序是稳定的排序算法,适合n较大时使用。虽然归并排序时间复杂度较低且具有稳定性,但因为其利用了O(n)的空间存储数据,所以使用的时候需要综合考虑。

3.堆排序

堆是一种特殊的树形数据结构,每个节点都有一个值,通常提到的堆是一棵完全二叉树,根节点的值小于或大于两个节点的值,同时根节点的两个子树也分别是两个堆。

堆排序的思想是对于给定的n个记录,初始时把这些记录看作成一棵顺序的二叉树,然后将其调整为一个大顶堆,将堆的最后一个元素与堆顶的元素进行交换后,堆的最后一个元素即为最大元素;接着将前(n-1)个元素重新调整为一个大顶堆,再将堆顶元素与当前堆的最后一个元素进行交换后得到次大的记录,重复该过程知道调整的堆中只剩下一个元素为止。此时已经得到一个有序的数组。

简要来说,堆排序就是两个过程:构建堆;交换堆顶元素与最后一个元素的值。

public void adjustHeap(int[] a,int pos,int len){
        int temp;
        int child;
        for(temp = a[pos];2 * pos+1<=len;pos=child){
            child = 2 * pos+1;//得到子节点的下标
            if(child<len&&a[child]<a[child+1])//得到子节点中较大的节点
                child++;
            if(a[child] > temp)//将子节点和父节点进行交换
                a[pos] = a[child];
            else
                break;
        }
        a[pos] = temp;
    }
    //堆排序
    public void HeapSort(int[] a){
        for(int i=a.length/2-1;i>=0;i--)//构建堆
            adjustHeap(a,i,a.length-1);
        for(int i=a.length-1;i>=0;i--){
            //把第一个数字和最后一个数字交换
            int temp = a[0];
            a[0] = a[i];
            a[i] = temp;
            //调整堆保证第一个数字是最大的
            adjustHeap(a,0,i-1);//调整剩下i-1个元素
        }
    }

堆排序的时间复杂度也为O(nlogn),同样适合n较大时使用。

时间: 2024-07-29 15:13:39

平均时间复杂度为O(nlogn)的排序算法的相关文章

时间复杂度为O(nlogn)的LIS算法

时间复杂度为 n*logn的LIS算法是用一个stack维护一个最长递增子序列 如果存在 x < y 且  a[x] > a[y],那么我们可以用a[y]去替换a[x] 因为a[y]比较小,具有更大的潜力,使得后面的元素和它成为更长的递增序列 如例子: a[] = {1,4,8,3,6}; 我们用一个stack st保存当前的最长递增子序列,top = 0; 很明显,初始化时st[top] = 1; 之后随着i循环变量的递增,如果 a[i] > st[top] , 那么 st[++top

8种主要排序算法的C#实现

新的一年到了,很多园友都辞职要去追求更好的工作环境,我也是其中一个,呵呵! 最近闲暇的时候我开始重温一些常用的算法.老早就买了<算法导论>,一直都没啃下去. 这本书确实很好,只是太难读了,总是读了几章就又读不下去了!工作上也几乎用不到. 我这段时间发现看这些排序算法比以前容易了很多,就借此机会将它们整理总结起来. 一是方便以后重温,二是可以应对笔试面试.同时也希望这篇博文可以帮助各位刚辞职和正在学习排序算法的园友. PS:有可能实现的代码并不是最优的,如果有什么错误或者值得改进的地方,还请大家

各种排序算法之比较

1.时间复杂度 时间复杂度为O(n^2)的排序算法:插入排序.冒泡排序.选择排序 时间复杂度为O(nlogn)的排序算法:堆排序.归并排序.快速排序 希尔排序介于这两者之间 2.算法稳定性 稳定的排序算法:插入排序.冒泡排序.归并排序和基数排序 不稳定的排序算法:选择排序.快速排序.希尔排序.堆排序 版权声明:本文为博主原创文章,转载请注明出处.

常见排序算法及对应的时间复杂度和空间复杂度

转载请注明出处: http://blog.csdn.net/gane_cheng/article/details/52652705 http://www.ganecheng.tech/blog/52652705.html (浏览效果更好) 排序算法经过了很长时间的演变,产生了很多种不同的方法.对于初学者来说,对它们进行整理便于理解记忆显得很重要.每种算法都有它特定的使用场合,很难通用.因此,我们很有必要对所有常见的排序算法进行归纳. 排序大的分类可以分为两种:内排序和外排序.在排序过程中,全部记

各种排序算法时间复杂度、稳定性、初始序列是否对元素比较次数有关

怎么记忆稳定性: 总过四大类排序:插入.选择.交换.归并(基数排序暂且不算) 比较高级一点的(时间复杂度低一点得)shell排序,堆排序,快速排序(除了归并排序)都是不稳定的,在加上低一级的选择排序是不稳定的. 比较低级一点的(时间复杂度高一点的)插入排序,               冒泡排序,归并排序,基数排序都是稳定的. (4种不稳定,4种稳定). 怎么记忆初始序列是否对元素的比较次数有关: [cpp] view plain copy /** * @brief 严版数据结构书代码 *   

快速排序[平均时间复杂度O(NlogN)]

基本思想: 假设我们现在对"6 1 2 7 9 3 4 5 10 8"这10个数进行排序.首先在这个序列中随便找一个数作为基准数.为了方便,就让第一个数6作为基准数.分别从初始序列"6 1 2 7 9 3 4 5 10 8"两端开始"探测".先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换它们,用两个变量i和j分别指向最左边和最右边,直到i=j,将基准数与a[i]交换,再继续递归对两测进行一样的方式. 快速排序的每一轮处理其实就是

C#中常用的排序算法的时间复杂度和空间复杂度

常用的排序算法的时间复杂度和空间复杂度 常用的排序算法的时间复杂度和空间复杂度 排序法 最差时间分析 平均时间复杂度 稳定度 空间复杂度 冒泡排序 O(n2) O(n2) 稳定 O(1) 快速排序 O(n2) O(n*log2n) 不稳定 O(log2n)~O(n) 选择排序 O(n2) O(n2) 稳定 O(1) 二叉树排序 O(n2) O(n*log2n) 不一顶 O(n) 插入排序 O(n2) O(n2) 稳定 O(1) 堆排序 O(n*log2n) O(n*log2n) 不稳定 O(1)

八大排序算法JAVA实现(时间复杂度O(n*logn)篇)

本文讲述时间复杂度为n*logn的排序算法:归并排序.快速排序.堆排序以及希尔排序的原理.Java实现以及变形应用. 一.归并排序 原理:把两个有序数列合并为一个有序数列.需递归实现. Java实现: 1 public int[] mergeSort(int[] a, int n) 2 { 3 return doMergeSort(a, n, 0, n - 1); 4 } 5 public int[] doMergeSort(int[] a, int n, int start, int end)

Java排序算法之快速排序

基本过程: 设置两个变量i.j,排序开始的时候:i=0,j=N-1: 以第一个数组元素作为关键数据,赋值给key,即key=a[0]: 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换: 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换: 重复第3.4步,直到i=j: (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j.i的值,使得j=