基础排序算法练习

先复习下常用的基础的排序算法,主要有冒泡排序、插入排序、选择排序、希尔排序、归并排序、快速排序、堆排序。本文记录基本的算法思想和C++实现代码,并记录各算法的时间和空间复杂度。
本文内容如有错误,请各位大侠帮忙指出啊,多谢~

1,冒泡排序
1)基本思想
一个数组,分前面的无序段和后面的有序段,每次循环把无序段的最大/小的元素移到有序段的开头。
注:本文都实现从小到大排序
2)代码实现

void swap(int &a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

int bubble_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    int i = 0;
    int j = 0;

    for (i = 0; i < len; ++i)
    {
        for (j = 0; j < len - 1 - i; ++j)
        {
            if (array[j] > array[j + 1])
            {
                swap(array[j], array[j + 1]);
            }
        }
    }

    return TRUE;
}

3)时间复杂度,平均O(n2)(注,是n的平方,下同);空间复杂度,O(1)

2,插入排序
1)基本思想
一个数组,分前面的有序段和后面的无序段,每次循环把无序段的首元素插入到有序段的相应位置,此时要保存有序段的有序性。
2)代码实现

int insert_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    int i = 0;
    int j = 0;
    int tmp = 0;

    for (i = 1; i < len; ++i)
    {
        tmp = array[i];
        for (j = i; (j > 0) && (array[j - 1] > tmp); --j)
        {
            array[j] = array[j - 1];
        }
        array[j] = tmp;
    }

    return TRUE;
}

3)时间复杂度,平均O(n2),空间复杂度O(1)

3,选择排序
1)基本思想
一个数组,分前面的有序段和后面的无序段,每次循环把无序段中的最小/大值放到有序段的最后面。
2)代码实现

void select_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    int i = 0;
    int j = 0;
    int min;
    int pos;

    for (i = 0; i < len - 1; ++i)
    {
        min = array[i];
        pos = i;
        for (j = i + 1; j < len; ++j)
        {
            if (array[j] < min)
            {
                min = array[j];
                pos = j;
            }
        }

        if (pos != i)
        {
            swap(array[i], array[pos]);
        }
    }

    return TRUE;
}

3)时间复杂度,平均O(n2),空间复杂度,O(1)

4,希尔排序
1)基本思想
希尔排序也叫缩小增量排序,与插入排序思想类似,将数组中的数插入到其前面的数组中,不同的地方是,每次按一定间隔来比较两个元素,该间隔逐渐减少到相邻两元素的间隔;该思路可以进行长距离的元素交换,最终可以减少总的元素交换次数,其效率比插入排序高些;具体看下代码就清楚了。
2)代码实现

int shell_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    int increment = len >> 2;
    int i = 0;
    int j = 0;
    int tmp = 0;

    for (; increment >= 1; increment /= 2)
    {
        for (i = increment; i < len; ++i)
        {
            tmp = array[i];
            for (j = i; j >= increment && array[j - increment] >= tmp; j -= increment)
            {
                array[j] = array[j - increment];
            }
            array[j] = tmp;
        }
    }

    return TRUE;
}

3)时间复杂度,平均O(n2),空间复杂度,O(1)

5,归并排序
1)基本思想
前面的几种排序算法都是简单算法,平均时间复杂度大,下面的几种排序算法则在时间复杂度上有所改进。
归并排序的思想是,运用递归的思想,先假设数组前半部分和后半部分各自有序,然后把前后部分有序的合并在一起;此时前提是数组的前半部分和后半部分是有序的,因此分别使用前面一样的思想,对各半部分继续之前的处理;一直递归下去,直到各数组只有一个元素为止。
2)代码实现

int merge(int array[], int tmp[], int first, int mid, int last)
{
    if (NULL == array || NULL == tmp || first > mid || mid > last)
    {
        return FALSE;
    }

    if (first == last)
    {
        return TRUE;
    }

    int front_i = first;
    int front_end = mid;
    int later_i = mid + 1;
    int later_end = last;
    int k = first;

    while(front_i <= front_end && later_i <= later_end)
    {
        if (array[front_i] <= array[later_i])
        {
            tmp[k++] = array[front_i++];
        }
        else
        {
            tmp[k++] = array[later_i++];
        }
    }

    while (front_i <= front_end)
    {
        tmp[k++] = array[front_i++];
    }

    while (later_i <= later_end)
    {
        tmp[k++] = array[later_i++];
    }

    for (int k2 = first; k2 <= last; ++k2)
    {
        array[k2] = tmp[k2];
    }

    return TRUE;
}

int m_sort(int array[], int tmp[], int first, int last)
{
    if (NULL == array || NULL == tmp)
    {
        return FALSE;
    }

    if (first >= last)
    {
        return FALSE;
    }

    int mid = (first + last) / 2;

    m_sort(array, tmp, first, mid);
    m_sort(array, tmp, mid + 1, last);

    /* merge two ordered array */
    return merge(array, tmp, first, mid, last);
}

int merge_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    int ret;
    int *tmp_array = new int[len];

    ret = m_sort(array, tmp_array, 0, len - 1);

    delete [] tmp_array;

    return ret;
}

3)时间复杂度,平均O(n*log2n)(注,是以2为底,下同),空间复杂度O(n)

6,快速排序
1)基本思想
一个数组,选择其中一个元素作为哨兵,使得一个循环后,原始数组变为三部分,第一部分元素值都小于哨兵元素,第二部分为哨兵元素,第三部分元素值都大于哨兵元素;随后迭代,对第一部分和第二部分依然通过前面的思想进行处理,直到要处理的部分,元素个数为1。
需要注意的一点是哨兵的选取,选取的哨兵值最好是要处理的当前部分数组中的中间值;否则若简单的选择第一个元素作为哨兵,而要排序的数组又是有序的,则通过快排思想将花费最大的O(n2)的时间复杂度;因此我们采取一个方法是,选择数组第一个元素、中间元素、最优元素的中间值作为哨兵。
2)代码实现

int select_flag(int array[], int first, int last)
{
    if (NULL == array || first >= last)
    {
        return FALSE;
    }

    int mid = (first + last) / 2;

    if ((array[first] >= array[mid] && array[first] <= array[last])
        || (array[first] <= array[mid] && array[first] >= array[last]))
    {
        return TRUE;
    }
    else if ((array[mid] >= array[first] && array[mid] <= array[last])
        || (array[mid] <= array[first] && array[mid] >= array[last]))
    {
        swap(array[mid], array[first]); // swap define in bubble_sort
    }
    else
    {
        swap(array[last], array[first]);
    }

    return TRUE;
}

int q_sort(int array[], int first, int last)
{
    if (NULL == array)
    {
        return FALSE;
    }

    if (first == last)
    {
        return TRUE;
    }

    int ret;

    /* select the flag element, after the func, first element will be the flag */
    ret = select_flag(array, first, last);
    if (TRUE != ret)
    {
        return ret;
    }

    int i = first + 1;
    int j = last;
    int flag = array[first];

    while (i <= j)
    {
        while (array[i] <= flag && i <= j) {++i;}
        while (array[j] >= flag && i <= j) {--j;}

        if (i <= j)
        {
            swap(array[i], array[j]);
        }
    }
    if (first != j)
    {
        swap(array[first], array[j]);
    }

    q_sort(array, first, j - 1);
    q_sort(array, j + 1, last);

    return TRUE;
}

int quick_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    return q_sort(array, 0, len - 1);
}

3)时间复杂度,平均O(nlog2n), 空间复杂度O(1);

7,堆排序
1)基本思想
堆是一棵完全二叉树,且每个节点的值大于左右子节点的值。堆排序的算法就是,先将原始数组构造为一个最大/小堆;随后每次取堆的根节点,并维护剩余节点的堆性质;这样按这种顺序取出来的元素就是有序的了。看代码实现就清楚了。
2)实现代码

#define CHILD(i) (2*(i) + 1)
int perc_down(int array[], int pos, int len)
{
    if (NULL == array || pos < 0 || pos >= len || len <=0)
    {
        return FALSE;
    }

    int i;
    int child;
    int flag = array[pos];
    for (i = pos; CHILD(i) <= len - 1; i = child)
    {
        child = CHILD(i);
        if ((child != len - 1) && (array[child] < array[child + 1]))
        {
            ++child;
        }

        if (array[child] > flag)
        {
            array[i] = array[child];
        }
        else
        {
            break;
        }
    }
    array[i] = flag;

    return TRUE;
}

int heap_sort(int array[], int len)
{
    if (NULL == array || len <= 0)
    {
        return FALSE;
    }

    int i;

    /* build heap */
    for (i = len / 2; i >= 0; --i)
    {
        perc_down(array, i, len);
    }

    /* mv the max element in root to the last position */
    for (i = len - 1; i >= 1; --i)
    {
        swap(array[0], array[i]);
        perc_down(array, 0, i);
    }

    return TRUE;
}

3)时间复杂度,O(nlog2n),空间复杂度O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值