数据结构复习-交换排序

1. 冒泡排序

冒泡排序算法的基本思想是:假设待排序表长为n,从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),则交换它们,直到序列比较完。我们称之为一趟冒泡,结果将最小的元素交换到待排序列的第一个位置。下一趟冒泡时,前一趟确定的最小元素不再参与比较,待排序列减少一个元素,每趟冒泡的结果把序列中的最小元素放到了序列的最终位置,...,这样最多做n-1趟冒泡就能把所有元素排好序。

void BubbleSort(ElemType A[],int n) {
    for(i=0; i<n-1; i++) {
        flag = false;
        for(j=n-1; j>i; j--) {
            if(A[j-1].key>A[j].key) {
                swap(A[j-1],A[j]);
                flag = true;
            }
        }
        if(flag==false)
            return;
    }
}

冒泡排序算法的性能分析如下:
空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1)。
时间复杂度:当初始序列有序时,显然第一趟冒泡后flag依然为false(本趟冒泡没有元素交换),从而直接跳出循环,比较次数为n-1,移动次数为0,从而最好情况下的时间复杂度为O(n);当初始化序列为逆序时,需要进行n-1趟排序,第i趟排序要进行n-i次关键字的比较,而且每次比较都必须移动元素3次来交换元素位置。这种情况下,
比较次数=n(n-1)/2,移动次数=3n(n-1)/3
从而,最坏情况下时间复杂度为O(n2)。其平均时间复杂度也为O(n2)。
稳定性:由于当i>j且A[i].key=A[j].key时,不会交换两个元素,从而冒泡排序是一个稳定的排序方法。

2.快速排序

快速排序是对冒泡排序的一种改进。其基本思想是基于分治法的:在待排序表L[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两部分L[1...k-1]和L[k+1...n],使得L[1...k-1]中所有元素小于pivot,L[k+1...n]中所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上,这个过程称作一趟快速排序。而后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终的位置上。
首先假设划分算法已知,记为Partition(),返回的是上述中的k,注意到L(k)已经在最终的位置,所以可以先对表进行划分,而后对两个表调用同样的排序操作。因此可以递归地调用快速排序算法进行排序,具体的程序结构如下:

void QuickSort(ElemType A[], int low, int high) {
    if(low<high) {
        int pivotpos = Partition(A,low,high);
        QuickSort(A,low,pivotpos-1);
        QuickSort(A,pivotpos+1,high);
    }
}

假设每次总是以当前表中第一个元素作为枢轴值(基准)对表进行划分,则必须将表中比枢轴值大的元素向右移动,比枢轴值小的元素向左移动,使得一趟Partition()操作之后,表中的元素被枢轴值一分为二。

int Partition(ElemType A[], int low, int high) {
    ElemType 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[low]=pivot;
    return low;
}

快速排序算法的性能分析如下:
空间效率:由于快速排序是递归的,需要借助一个递归工作栈来保存每一层递归调用的必要信息,其容量应与递归调用的最大深度一直。最好情况下为log2(n+1);最坏情况下,因为要进行n-1次递归调用,所以栈的深度为O(n);平均情况下,栈的深度为O(log2(n))。因而空间复杂度在最坏情况下为O(n),平均情况下为O(log2(n))。
时间效率:快速排序的运行时间与划分是否对称有关,而后者又与具体使用的划分算法那有关。快速排序的最坏情况发生在两个区域分别包含n-1个元素和0个元素时,这种最大程度的不对称性若发生在每一层递归上,即对应于初始排序表基本有序或基本逆序时,就得到最坏情况下的时间复杂度O(n^2)。
有很多方法可以提高算法的效率。一种方法是当递归过程中划分得到的子序列的规模较小时不要再继续递归调用快速排序,可以直接采用直接插入排序算法进行后续的排序工作。另一种方法就是尽量选取一个可以将数据中分的枢轴元素。如从序列的头尾以及中间选取三个元素,再取这三个元素的中间值作为最终的枢轴元素;或者随机从当前表中选取枢轴元素,这样做使得最坏情况在实际排序中几乎不发生。
在最理想状态下,也即Partition()可能做到最平衡的划分中,得到的两个子问题的大小都不可能大于n/2,在这种情况下,快速排序的运行速度将大大提升,此时,时间复杂度为O(nlog2(n))。好在快速排序平均情况下运行时间与其最佳情况下的运行时间很接近,而不是接近其最坏情况下的运行时间。快速排序是所有内部排序算法中平均性能最优的排序算法。
稳定性:在划分算法中,若右端区间存在两个关键字相同,且均小于基准值的记录,则在交换到左端区间后,它们的相对位置会发生变化,即快速排序是一个不稳定的排序方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值