【算法笔记3】快速排序

终于讲到快速排序(quick sort)了 

 

假设有下面这样一串数字,你想要将它们从小到大排序:

 

 

现在请用上一个笔记刚刚学到的分而治之思想,来解决这个问题。

回想一下,在上一个笔记中,分而治之的关键两点:

(1)找到基线条件。

(2)找到一种方法,它可以不断使用以缩小问题的规模,使其符合基线条件。

 

那么排序问题最简单的情况是什么呢?那必须是简单到可以一眼看出顺序的数组呀,大概是这样吧:

 

 

那么问题来了,怎样才可以把这个排序问题逐渐转化成这个简单的问题呢?我们可以把这个数字串分成两部分,把这两部分分别排序,最后再合成一个数字串,就是排序后的数字串。在对子串排序时,如果这个子串不符合基准条件,就再把它分成两部分,分别排序,这样不断递归,直到遇到基线条件。

 

算法原理

对于上面的数字串,随机选择一个数字,称其为基准值(base)。对于上面的数字串,我们可以选择第一个数字3,接下来将3与其余的数字比较,比它大的放在其左边,比它小的放在数组右边,经过一轮比较后,就变成了这样:

 

 

 好了,现在原来的数组被分成了两部分,分别是基准值3左边的部分和3右边的部分。对于这其中一个子串,随机选择一个基准值,再与子串中其它的数字比较,比基准值小的放在左边,比它大的放在右边,对于另一个子串也执行同样的操作,直至遇到基线条件。

 

 

至此,通过几次递归,我们得到了一个有序数组。容易理解的是,不管选择哪个数作为基准值,最后都能得到这个有序数组,只不过递归的次数有可能不同。

 

快速排序的运行时间

想必你已经发现,快速排序的递归次数与选择的基准值有关。在最糟糕的情况下,快速排序的运行时间为O(n2), 与选择排序一样,比如在每次递归中,选择最大值或者最小值为基准值,但是在平均情况下,快速排序可以达到O(nlogn)。还有一种叫做合并排序的排序算法,它的运行时间也是O(nlogn),和平均情况下的快排一样,但是,由于快排的O(nlogn)中的常量比合并排序小,所以在实际情况中,快排基本上是最好的选择。

需要注意的是,只有在两种算法的大O表示法在一个量级时,比较它们其中的常量才有意义。

 

练习

POJ 2388 3664

// 2388

#include <iostream>

using namespace std;

void exchange( int milk[], int id1, int id2)
{
    int tmp;
    tmp = milk[id1];
    milk[id1] = milk[id2];
    milk[id2] = tmp;
}

void quickSort( int milk[], int head, int tail)
{
    int base;
    int left;
    if( head < tail )
    {
        base = milk[head];
        left = head;
        for( int i = head+1; i <= tail; i++ )
        {
            if( milk[i] < base )
            {
                left++;
                exchange(milk, i, left);
            }
        }
        exchange(milk, left, head);
        quickSort( milk, head, left-1 );
        quickSort( milk, left+1, tail );
        
    }
} 



int main()
{
    int milk[10000];
    int milk_num;
    while(cin >> milk_num)
    {
        for( int a = 0; a < milk_num; a++)
        {
            cin >> milk[a];
        }
        quickSort( milk, 0, milk_num-1);
        int re = milk[(milk_num-1) / 2];
        cout << re << endl;
    }
    return 0;
}

 

// 3664

#include <iostream>

using namespace std;

void exchange(int num[], int idx1, int idx2)
{
    int tmp = num[idx1];
    num[idx1] = num[idx2];
    num[idx2] = tmp;
}

void quickSort( int num[], int id[], int head, int tail)
{
    int left;
    int base;
    if( head >= tail )
    {
        return;
    }
    else
    {
        base = num[head];
        left = head;
        for( int a = head + 1; a <= tail; a++ )
        {
            if( num[a] < base )
            {
                left++;
                exchange(num, a, left);
                exchange(id, a, left);
            }
        }
        exchange( num, head, left );
        exchange( id, head, left );
        quickSort( num, id, head, left-1 );
        quickSort( num, id, left+1, tail );
    }
}

int main()
{
    int n_cow;
    int k_cow;
    int a_id[50010];
    int b_id[50010]; 
    int a_vote[50010];
    int b_vote[50010];
    while( cin >> n_cow && cin >> k_cow )
    {
        for( int a = 0; a < n_cow; a++ )
        {
            a_id[a] = a;
            b_id[a] = a;
            cin >> a_vote[a];
            cin >> b_vote[a];
        }
        quickSort( a_vote, a_id, 0, n_cow-1 );
        for( int b = n_cow - k_cow-1; b >= 0; b-- )
        {
            b_vote[ a_id[b] ] = 0;
        }
        quickSort( b_vote, b_id, 0, n_cow-1 );
        cout << b_id[n_cow-1] + 1 << endl;
        /*
        for( int c = 0; c < n_cow; c++ )
        {
            cout << a_id[c] << "  " << a_vote[c] << endl;
        }
        cout << "&&&&&&&&&&&" << endl;
        for( int c = 0; c < n_cow; c++ )
        {
            cout << b_id[c] << "  " << b_vote[c] << endl;
        }
        */
    }
    return 0;
}

 

 

 

参考资料:算法图解》[美] Aditya Bhargava 译者:袁国忠

 

posted @ 2020-09-18 15:48  平平的圆圆  阅读(329)  评论(0)    收藏  举报