交换排序

很多人学习编程的时候,第一个接触到排序算法就是冒泡算法,它是最简单的交换类排序算法。此外,另一个著名的算法——快速排序算法,也属于交换类排序算法。

1 冒泡排序

(1) 基本思想
在非有序的序列中,两两比较相邻的元素,并交换不满足排序要求的元素对,直到整个序列都满足排序要求。由于每一趟排序,都会把非有序序列的最大元素(这里以升序为例)移动到非有序序列的末端,非常类似于水中气泡的上升过程,因此形象地称该算法为冒泡排序。

(2) 图文分析
915116-20170210205012338-378923660.png

(3) 代码实现

// 冒泡排序
template <typename T>
void BubbleSort(T array[], int len)
{
    if(array == nullptr || len <= 0)
        return;
    int i, j;
    bool isSwap = true;    // 一趟比较中,标志是否有元素交换
    for(i = 0; i < len - 1 && isSwap; ++i)
    {
        isSwap = false;
        for(j = 1; j < len - i; ++j)
        {
            if(array[j] < array[j - 1])
            {
                isSwap = true;
                T temp = array[j];
                array[j] = array[j-1];
                array[j-1] = temp;
            }
        }
    }
}

(4) 测试

int main()
{
    int array[] = {4, 2, 1, 5, 3};
    int len = sizeof(array)/sizeof(int);
    BubbleSort(array, len);
    
    for(int i = 0; i<len; ++i)
        cout << array[i] << " ";
    cout<<endl;
    return 0;
}

测试结果

1 2 3 4 5

(5) 复杂度分析

a. 主要操作是比较相邻的两个元素

b. 最好的情况:初始序列为正序,一趟扫描之后便可完成排序,比较n-1次,交换0次,时间复杂度为O(n);

c. 最坏的情况:初始序列为逆序,需要进行n-1趟排序,比较和交换元素的次数达到最大,时间复杂度为O(n^2);

d. 平均时间复杂度为O(n);

e. 采用就地排序,空间复杂度为O(1)。

(6) 稳定性
冒泡排序比较的是相邻的两个元素比较,交换也发生在这两个元素之间。相同元素的相对位置没有发生改变,因此该算法是稳定的。

2 快速排序

(1) 基本思想

a. 找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值都不小于基准值,如此作为基准的元素调整到排序后的正确位置。

b. 左、右子区间递归快速排序,将其他n-1个元素也调整到排序后的正确位置。

c. 最后每个元素都是在排序后的正确位置,排序完成。

(2) 图文分析
915116-20170210205028916-36351412.png

(3) 代码实现

// 快速排序
template <typename T>
void QuickSort(T array[], int left, int right)
{
    if(array == nullptr || left >= right)
        return;
    
    int i, j;
    i = left;
    j = right;
    T sentinel = array[left];       // sentinel作为基准数(哨兵)
    
    while(i < j)
    {
        while(array[j] >= sentinel && i < j)
            --j;
        while(array[i] <= sentinel && i < j)
            ++i;
        if(i < j)
        {
            T temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }
    
    array[left] = array[i];
    array[i] = sentinel;
    
    QuickSort(array, left, i-1);
    QuickSort(array, i+1, right);
}

(4) 测试

int main()
{
    int array[] = {3,2,4,1,6,5,1};
    int len = sizeof(array)/sizeof(int);
    QuickSort(array, 0, len-1);
    
    for(int i = 0; i<len; ++i)
        cout << array[i] << " ";
    cout<<endl;
    return 0;
}

测试结果

1 1 2 3 4 5 6

(5) 复杂度分析

a. 主要操作是比较操作(划分),对长度为n的区间进行划分, 需要进行n-1次比较。

b. 最坏的情况:每次划分所取的基准数都是当前区间的最小(或最大)元素,划分的结果是基准数左边的子区间为空(或右边的子区间为空),导致划分所得的另外一个非空区间中元素的个数仅仅比划分前少1个,因此时间复杂度为O(n^2);

c. 最好的情况:每次划分所取的基准数都是当前区间的“中值”,划分的结果是基准数左右两边的子区间长度大致相等,时间复杂度为O(n log2 n);

d. 平均时间复杂度为O(n log n)。

e. 采用就地排序,空间复杂度为O(1)。

(6) 稳定性
在排序的过程中,由于是跳跃性地比较和交换,因此相同元素的相对位置可能会发生改变,故快排算法不是稳定排序算法。

转载于:https://www.cnblogs.com/west000/p/6387946.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值