知其所以然-- partition 算法

快速排序 是一个很经典的排序算法,它采用了分而治之和递归的思想,大概思路如下:
1. 对于一个无序数组,我们想对其中元素进行排序,最简单的做法是冒泡排序,就是每一个元素都要和其他元素比较一遍,这样扫描次数和比较次数是最多的,时间复杂度是O(n^2). 如何减少扫描次数呢。在一遍扫描的时候,如何能使这一遍扫描做的事情更多呢?
2. partition就是这样,如果给定一个数组里的元素a, 我想要把小于a的放一边, 大于a的放一边,那么扫描一次数组就够了,这一次的过程就叫partition。如果我继续在a的两边做同样的操作,扫描,划分,最终我会得到一个排好序的数组。
3. partition 的思想就是基于扫描进行分类, 而且一次扫描能得到分类后指定数的位置。排序只是它应用比较广泛的一个例子而已,另外比较常见的是寻找无序数组中第K大的数,另外荷兰三色旗也是partition算法一个高效的应用。


以下是一个例子

对以下数进行分类:
5,10, 324, 86, 7, 26, 78, 90, 8, 0, -1, 20, 30, -5

  1. 选择基准数, 一般是一个随机的数, 比如26。
  2. 26 放到数组首位,然后开始扫描。
  3. 扫描一遍后, 变成
    26, 5, 10, 7, 8, 0, -1, 20, 324, 86, 78, 90, 30
  4. 26 和 20互换,
    2, 5, 10, 7, 8, 0, -1, 26, 324, 86, 78, 90, 30

快排代码实现

/* scan once, return the position for exchange */
int partition(int *array, int start, int end)
{
    int i, spec;
    int change_pos = start;

    /* we treat first number as pivot */
    spec = array[start];

    for(i = start+1; i < end; i++) {
        /* compare */
        if (array[i] <= spec) {
            /* the position to be changed should be in the right of start, that to say, we have to left one position for pivot */
            change_pos ++;

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

    swap(&array[change_pos], &array[start]);

    return change_pos;
}

void quicksort(int *array, int start, int end)
{
    int index;

    assert(((start <= end) && (array != NULL)) &&  "Given invaild input"); 

    /* stop of recursion  */
    if (start == end) {
        return;
    }

    index = partition(array, start, end);

    if (index > start) {
        quicksort(array, start, index);
    }

    if (index < end) {
        quicksort(array, index+1, end);
    }
}

这里选择第一个数为基准数,随机选择一个数作为基准数要更好一些,快排时间复杂度最好的情况是O(n), 就是每次划分,总能划分出相等数量的两部分。最坏的情况是O(n^2), 每次划分就是N-1 和 N两部分。


寻找K大数代码实现

通过一次扫描,可以得到指定数的位置pos, 如果pos == k , 那么我们就得到了第 K 大的数,如果不相等,我们就继续扫描。

int get_kleast_number(int *array, int k, int total, int *output)
{
    int index;
    int start = 0;
    int end = total - 1;

    for (; ;) {
        index = partition(array, start, end);
        if (index == k-1) {
            break;

        } else if (index < k-1) {
            start = index + 1;
        } else {
            end = index - 1;
        }
    }

    for (index = 0; index < k; index++) {
        output[index] = array[index];
    }


    return output[index-1];

}

Dutch National Flag

The flag of the Netherlands consists of three colours: red, white and blue. Given balls of these three colours arranged randomly in a line (the actual number of balls does not matter), the task is to arrange them such that all balls of the same colour are together and their collective colour groups are in the correct order.
Given an array A[] consisting 0s, 1s and 2s, write a function that sorts A[]. The functions should put all 0s first, then all 1s and all 2s in last.
Example
Input = {0, 1, 1, 0, 1, 2, 1, 2, 0, 0, 0, 1};
Output = {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2};

荷兰三色旗的一个小题目,本质还是根据扫描来划分, 根据不同的条件,把数组分成三份。这里就需要三个指针,也可以是下标,来标识元素移动位置。我们写为low, mid, hi.
low 即是小于target的部分,我们取第一位,
hi即是大于target的部分,我们取最后一位
mid 即是等于target的部分。我们用mid来界定,所以根据mid 来移动元素。

int Threepartition(int *array, int start, int end)
{
    int low, mid, hi;

    assert(((start <= end) && (array != NULL)) &&  "Given invaild input"); 

    low = start;
    hi = end;
    mid = low;

    /* start scanning */
    while (mid <= hi) {
        if (array[mid] == 0) {
            swap(&array[mid], &array[low]);
            low ++;
            mid ++;
        } else if(array[mid] == 1) {
            mid ++;
        } else if(array[mid] == 2) {
            swap(&array[mid], &array[hi]);
            hi --;
        }
    }

    return 0;
}

当然, 如果是排序的话,counting sort更快一些,但是如果题目变一下,不是 0, 1, 2 这样的顺序呢?
partition的本质不是排序,而是分类。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值