排序

各种排序复杂度

 

类别

 

排序算法

时间复杂度

 

空间复杂度

 

稳定性

平均情况

最好情况

最坏情况

插入

直接插入

O(

O(n)

O(

O(1)

稳定

 

希尔排序

不好说

O(1)

不稳定

交换

冒泡排序

O(

O(n)

O(

O(1)

稳定

 

快速排序

O()

O()

O(

O()

不稳定

选择

简单选择

O(

O(

O(

O(1)

不稳定

 

堆排序

O()

O()

O()

O(1)

不稳定

归并排序

O()

O()

O()

O(n)

稳定

基数排序

O(d(r+n))

O(d(r+n))

O(d(r+n))

O(r)

稳定

基本概念

l  并不是所有的内部排序都是基于比较的,基数排序就不是基于比较的

l      对于任意n个关键字排序的比较次数至少为

插入排序

插入排序总共有三种:直接插入、折半插入和希尔排序

直接插入

l  算法:从头开始进行遍历,每一次都将访问元素插入到前面已经有序的子序列中。对于具有n个元素的序列表而言,需要进行n-1次操作。在前面有序的子序列中找到需要插入的地方k,将k~i-1的所有元素向后移动一位,最后将i处的复制到k的位置。

l  空间效率:因为是就地进行操作,所有空间复杂度为O(1)

l        时间效率:时间复杂度为O()时间效率取决于比较和移动的次数。最好的情况下,不用移动,只用比较n-1次;最坏的情况下,比较的次数为 = n(n-1)/2,移动的次数同上。

l  稳定性:稳定。因为查找插入位置的时候是寻找比当前元素小的地方,所以不会改变相等元素的前后位置。

l  适用性:适用于基本有序的排序表且数据量不会很大。适用于顺序存储和链式存储

//直接插入算法

voidInsertSort(int[] A){

    for(int i = 1; i < A.length; i++){

        if(A[i-1] > A[i]){

            int temp = A[i];

            for(int j = i - 1; j >= 0; j--){

                if(A[j] < temp){

                    A[j] = temp;

                    break;

                }else{

                    A[j+1] = A[j];

                }

            }

        }

    }

}

折半插入

l  算法思想:和前面直接插入是类似的,不同的只是缩短了在有序子序列中查找插入位置的时间。用的是折半查找的方法。

l  空间效率:O(1)

l        时间效率:O()

l  稳定性:稳定

l  适用性:仅适用于顺序存储的情况

//直接插入算法

voidInsertSort(int[] A){

    for(int i = 1; i < A.length; i++){

        if(A[i-1] > A[i]){

            int temp = A[i];

            int low = 0;

            int high = i - 1;

            while(low <= high){

                int mid = (low + high)/2;

                if(A[mid] > temp){

                    high = mid - 1;

                }else{

                    low = mid + 1;

                }

            }

            for(int j = i - 1; j >= high +1; j--){

                A[j+1] = A[j];

            }

            A[high + 1] = temp;

        }

    }

}

希尔排序(缩小增量排序)

l  算法思想:将排序表分为距离相等的部分,距离相等部分进行直接插入排序的方法。然后不断缩小距离增量,直至d = 1为止。常用的方法是, = n/2, ,直到为1。

l  空间效率:O(1)

l        时间效率:不好说,但是最坏情况下为O()

l  稳定性:不稳定,因为可能会根据不同d导致相同元素的排序颠倒

l  适用性:仅适用于顺序存储的情况

交换排序

常用的算法为:冒泡排序和快速排序

冒泡排序

l   算法思想:从前向后或者从后向前,相邻两位两两比较,如果为逆序,交换两者。这样一趟下来,最小的元素会被放置在最开始的位置。一共进行n-1次完成排序。

l   空间效率:常数个辅助单元,所以为O(1)

l   时间效率:因为有设置一个标志为来表示这一趟是否进行了交换操作。所以最好情况下,比较n-1次,移动0次,为O(n)。最坏情况下,比较次数为: = n(n-1)/2,每一次都移动3次,那么时间为3n(n-1)/2. 即O()。平均情况下为:O()

l   稳定性:因为移动的前提条件是大或小于,因而相等的时候不会有变化,是稳定的算法。

l   注:冒泡排序中已经产生的有序子序列一定是全局有序的(不同于选择排序),每一趟的排序都会将一个元素放置在最终的位置上。

//冒泡排序

voidBubbleSort(int[] a){

    for(int i = 0; i < a.length - 1; i++){

        int flag = 0;

        for(int j = a.length - 1; j > i;j--){

            if(a[j] < a[j-1]){

                swap(a[j],a[j-1]);

                flag = 1;

            }

        }

        if(flag == 0){

            return;

        }

    }

}

 

快速排序

l   算法思想:基本思想是分治法。在待排序表L[1…n]中任意选取一个元素pivot作为基准。通过一趟排序将待排序表划分为独立的两部分L[1…k]和L[k+1…n],使得L[1…k-1]全部小雨pivot,L[k+1…n]全部大于pivot,将pivot放在了最终的位置L(k)上,这个过程是一趟排序。之后递归的对两个表重复上述过程,直到每部分只有一个元素或空为止。快速排序算法的关键在于划分操作。

l       空间效率:因为用的是递归,所以需要借助一个递归工作栈。最好情况下是+1,最坏的情况是n-1次,所以平均情况下为O()

l       时间效率:最好的情况为O(),最坏情况为O()。平均情况下接近最好的情况。

l   稳定性:不稳定。因为如果右边区间存在两个关键字相同,且均小于pivot,那么交换到左边区间之后会打乱顺序。

l   注:在快速排序算法中,并不产生有序子序列,但每一趟排序之后都会将一个元素放到最终的位置上。

void QuickSort(int[] A,int low, int high){

    if(low < high){

        int pivotpos = Partation(A,low,high);

        QuickSort(A, low, pivotpos - 1);

        QuickSort(A, pivotpos + 1, high);

    }

}

 

int Partation(int[] A,int low, int high){

    int pivot = A[low];

while(low< high){

while(low < high &&A[high] >= pivot){

            high--;

        }

    A[low] = A[high];

        while(low < high && A[low]<= pivot){

            low++;

        }

        A[high] = A[low];

    }

    A[low] = pivot;

    return low;

}

选择排序

主要由简单选择排序和堆排序构成,两者都是不稳定的算法。主要思想是:每一趟在后面待排序的元素中选取关键字最小的元素,作为有序子序列的第i个元素,总共n-1趟即可完成排序。每次排序都会将一个元素放在最终的位置。

简单选择排序

l   算法思想:假设排序表为L[1…n],第i趟排序即从L[i…n]中寻找最小的元素,和L(i)交换,每一趟排序可以确定一个元素的最终位置。经过n-1次就可以使得整个排序表有序。

l   空间效率:常数个辅助空间,所以空间复杂度为O(1).

l  时间效率:移动次数很少,最多也就是3(n-1)次,但是比较的次数是固定的。都为n(n-1)/2次,所以时间复杂度始终为:O(n2)

l   稳定性:可能将后面的相同元素移动到前面,因此是不稳定的算法。

堆排序

是一种树形排序方法,堆满足一定的条件,分为大根堆和小根堆两种。有两大步骤,首先是建堆过程,然后是排序过程。

l   建堆过程:从最后一个非叶结点开始进行调整,逆序调整到根结点为止。中间调整过程中,会对向下的部分造成影响,因此需要用相同的方法构造下级的堆。

l   排序过程:首先将n个元素构造成初始堆(以大根堆为例),堆顶元素就是最大值。输出堆顶元素之后,将堆底元素送入堆顶,从堆顶向下继续调整,再输出堆顶元素。如此重复n-1次,直到堆中仅剩下一个元素为止。整个过程中涉及到堆顶元素的删除,就是前面的先交换堆顶和堆底元素,然后从堆顶元素向下调整使其继续保持堆的性质。

l   堆的插入:先将该元素放在堆的末端,然后从这个新结点执行向上调整操作。

l   空间效率:使用了常数个辅助空间,所以为O(1)

l   时间效率:常数个辅助单位,O(1)

l  空间效率:初始建堆为O(n),后面n-1次向下调整和树的高度h有关,所以在最好、最坏和平均之下,时间复杂度都是:O()

l   稳定性:是不稳定的算法

归并排序

l  归并:指的是将两个或两个以上有序的子序列合并一个新的有序的序列。

l  过程:开始的时候,将带排序表看为有n个有序的子表,每个字表长度为1,然后两两归并。得到向上取整的n/2个有序的表,然后继续两两归并…直到最终表的长度为n为止。这种排序方法称为“2-路归并”

l  归并的过程:merge()算法先把两个有序的子表[low,mid],[mid+1,high]复制到一个新的数组中,然后对这个数组的两部分进行遍历,将较小的元素放入最终合并的数组A中。当某个部分到头,就将另一个数组剩余部分全部复制到A中即可。

voidMerge(int[] A, int low, int mid, int high){

    int[] B = new int[A.length];

    for(int i = low; i <= high; i++){

        B[i] = A[i];

    }

    int i = low;

    int j = mid + 1;

    while(i <= mid && j <= high){

        if(B[i] < B[j]){

            A[k++] = B[i++];

        }else{

            A[k++] = B[j++];

        }

    }

    while(i <= mid){

        A[k++] = B[i++];

    }

    while(j <= high){

        A[k++] = B[j++];

    }

}

l      一趟二路归并排序,会进行趟。对于k路归并,那么需要进行次。递归形式的归并排序算法是基于分治的,先分解再合并。分解:将含有n个元素的待排序表分成各含n/2的子表,然后对两个子表递归。合并,就是上面的merge算法。

voidMergeSort(int[] A, int low, int high){

    if(low < high){

        int mid = (low + high)/2;

        MergeSort(A,low,mid);

        MergeSort(A,mid+1,high);

        Merge(A,low,mid,high);

    } 

}

l  空间效率:因为在Merge的时候,需要一个复制数组,所以为O(n)

l      时间效率,依次Merge为O(n),一共进行趟,其分割子序列和初始序列是无关的。所以时间效率为O(n)

l  稳定性:稳定

基数排序

l  不是基于比较的算法,是根据多关键字排序思想,借助分配和收集两种操作对单逻辑关键字进行排序。基数排序分为最高为优先和最低为优先。以r为基数,则需要r个队列进行辅助。每个结点为d元组,那么需要进行d趟。

l  空间效率:因为需要r个队列进行辅助,所以空间复杂度为O(r)

l  时间效率:一共进行d趟,每一趟有分配和收集两个步骤。分配时间为O(n),收集为O(r),因此时间复杂度为O(d(r+n))

l  稳定性:基数排序一定是稳定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值