在单链表上实现快排,需要考虑以下几个因素:
- 单链表存取随机结点需要从头遍历
- 指针单向,取前驱结点需要从头遍历
- 不能像数组那样以下标判断越界
因此,我们使用一趟排序中的策略三,其好处是:只要维护足够多(实际上也并不多)的指针,就能只遍历一次即完成一趟排序
但和数组快排不同的是:
- 为了方便,不使用三数中值分割法选取pivot(需要遍历一次链表),而直接使用当前子链表的第一个结点作为pivot。这样性能肯定受到影响
- 在一趟排序中,pivot被放置在最左端,最后和div-1交换(因为放置在最右端同样需要遍历一次链表)
代码如下:
typedef struct node{
int data;
struct node * next;
} Node;
void swap(int * a, int * b){
assert(a != NULL);
assert(b != NULL);
if(a != b){
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}
void linklist_qsort(Node * left, Node * right){
assert(left != NULL);
assert(right != NULL);
if(left != right){
Node * pivot, * div, * div_prior, * div_prior_prior, * p; //div_piror用于一趟排序最后和pivot交换,div_prior_prior用于表示左子链表的右边界(用于递归和越界判断)
pivot = left;
div_prior = left;
p = left->next;
div = left->next;
div_prior_prior = NULL;
while(p != right->next){
if(p->data < pivot->data){
swap(&p->data, &div->data);
div_prior_prior = div_prior;
div_prior = div;
div = div->next;
}
p = p->next;
}
swap(&pivot->data, &div_prior->data);
if(div_prior_prior != NULL) //若pivot在最左端,跳过
linklist_qsort(left, div_prior_prior);
if(div_prior != right) //若pivot在最右端,跳过
linklist_qsort(div, right);
}
}