单向链表快速排序

第一次在这里发,排版不行,见谅啊,sorry,我就没排版。

对于快速排序,我以前只是用来排数组,从来没有排过链表,直到有一天,我使用了算法导论上 N.Lomoto版本的快速排序,发现这个版本也许可以用来排链表,于是就尝试了下,发现真的可以.

在此先说一下 N.Lomoto 版本和国内普遍使用的 Hoare(霍尔)版本的区别.

霍尔版本应该算是快速排序的正式版本,他改进了之前的算法,然后称之为快速排序,而后来 N.Lomoto 做了优化,即写在算法导论一书中的快速排序.

这是快速排序的基本模样,而这两个算法不同在于 partition:

QUICKSORT(A, p, r)

1 if p < r

2     then q PARTITION(A, p, r)    //关键

3          QUICKSORT(A, p, q - 1)

4          QUICKSORT(A, q + 1, r)

 

HOARE版本的 partition:

HOARE-PARTITION(A, p, r)

 1   x A[p]

 2   i p - 1

 3   j r + 1

 4   while TRUE

 5       do repeat j j - 1

 6            until A[j] x

 7          repeat i i + 1

 8            until A[i] x

 9          if i < j

10             then exchange A[i] ↔ A[j]

11             else return j

 

算法导论版本的 partition:

PARTITION(A, p, r)

1   key A[r]         //以最后一个元素,A[r]为主元

2   i p - 1

3   for j p to r - 1    //注,j p指向的是r-1,不是r

4        do if A[j] key and  i != j  // 原来版本没有 I != j ,加上是为了避免不必要的交换

5              then i i + 1

6                   exchange A[i] <-> A[j]

7   exchange A[i + 1] <-> A[r]  // key

8   return i + 1

 

从上面可以看出,霍尔版本是双向扫描的,而算法导论的版本是单向的,两个算法各有利弊吧, 但是本人习惯用算法导论版本.现在就重点讲述这个版本.

首先,第一步,他将数组A的最后一个下标作为一个比较值,姑且称之为key,这个过程我们可以这么认为,我们拿最后一个元素作为key, 现在,我们想把整个数组分为2部分, key的左边的值要小于 key, key的右边的值要大于key.为了实现这个过程, 我们用 jpr来一次循环,i (初始值为 P-1) ,i的作用,是用来记录比key小的左边的坐标,也就是说在小于等于i的坐标里,其值肯定是小于等于 key.

下面用一个例子来描述一下

数组   7   4        1      8      2

 

                                               key

          7   4   1      8      2

i     j

i                    j

i            j                             // A[j] <= key,  i++, 并且交换 A[i] <-> A[j], 因为此时,j左边的肯定是比key大的

          1   4   7      8      2

          i               j

          i           j

          i                           j

          1   2   7      8      4        //到此,j已经到达了下标r,即数组的最后,循环结束,这时,下标小于等于 i 的值是肯定小于 key,所以此时

               i    i+1                     把key值放到正确的位置,即下标 i+1 , 交换 A[i+1] <-> A[r]

最后return i+1,至此,partition 结束,然后在递归这个过程.

此过程可以简单的理解为把数组最后一个元素放到一个正确的位置,使其左边的元素不大于他,使其右边的元素不小于他, 由此将数组分割为2个部分.

         下标J的作用是把数组从头到尾遍历,而下标i的作用是记录比key值小的最右边的下标.

 

         下面是一个简单的代码实现.

#include    <stdio.h>

 

void swap_array(int *a, int *b);

void sort_quick(int *array,int l, int r);

int  sort_quick_apart(int *array, int l, int r);

 

void sort_quick(int *array,int l, int r)

{

    if(l<r)

    {

        int p = sort_quick_apart(array, l, r);

        sort_quick(array, l, p-1); 

        sort_quick(array, p+1, r);

    }

}

 

int sort_quick_apart(int *array, int l, int r)

{

    long key = array[r];

    int i = l -1;

    for(int j=l; j<r; j++)

    {

        if( array[j] <= key)   

        {

            i ++;

            swap_array(&array[i], &array[j]);

        }

    }

    swap_array(&array[i+1], &array[r]);

    return i+1;

}

 

void swap_array(int *a, int *b)

{

    int t = *a;

    *a = *b;

    *b =t;

}

 

/* */

void print_array(int *arr, int num)

{

    for(int i=0; i<num; i++)

    {

        printf("%d ", arr[i] /* i%5 ==4 ?'\n' : ' ' */ );

    }

    printf("\n");

}

 

int main()

{

    int array[10]={ 9,3,2,5,1,8,7,4,0,6   };

    printf("before sort\n");

    print_array(array,10);

    sort_quick(array,0,9);

    printf("after sort\n"); 

    print_array(array,10);

}


 

输出结果结果:

before sort

9 3 2 5 1 8 7 4 0 6

after sort

0 1 2 3 4 5 6 7 8 9

 

到此,我们的为链表排序的铺垫数组排序就先粗略的讲完了,下面才是我们的主题,为链表排序(单向链表,当然双向链表其实更可以).

 

         单向链表的排序,很大程度上是跟这数组排序时很相似的,只是稍作修改.

Partition现在只需要2个参数,第一个是链表的头指针,2个是尾部.

由于我们是单向链表,所以我们把链表的第一个元素来作为 key .

typedef struct nnode{

    int elem;

    struct nnode *next;

}Node; /*链表的元素*/

QUICKSORT(head, tail )

1 if  head != tail

2     then  p PARTITION( head, tail)    //关键

3          QUICKSORT(head, p)

4          QUICKSORT(p->next, tail)

 

PARTITION( head,  tail)

1   key head->elem          //链表的第一个元素为 key

2   p1   head->next            // p1作用类似数组排序中的i,用来记录key左边的

    P2  head            // p2作用类似j,用来遍历

3   while  p1  !=  tail

4       if p1->elem key

5              then p2 p2->next

6                   exchange  p1->elem  <->  p2->elem  //交换元素

7           P1 p1->next

8   exchange  p2->elem  <->  head->elem

9   return  p2

 

 

代码:

#include    <stdio.h>

#include    <time.h>

 

/* 链表的快速排顺 */

typedef struct nnode{

    int elem;

    struct nnode *next;

}Node;

 

Node *listInit()

{

    Node *p = (Node*)malloc(sizeof(Node));

    p->next = 0;

    p->elem = -1;

    return p;

}

 

/* 这个是个头指针而已,不算是元素 */

void listAdd(Node *ls, int elem)

{

    if(!ls)

        return;

    Node *p = ls;

    while(p->next)

        p = p->next;

    p->next = (Node *)malloc(sizeof(Node));

    p->next->elem = elem;

    p->next->next = 0;

}

 

void print_list(Node *ls)

{

    if(!ls)

        return;

    Node *p =ls->next;

    while(p)

    {

        printf("%d ", p->elem);

        p = p->next;

    }

    printf("\n");

}

 

Node * sortListApart(Node *phead, Node *pend); /* 分隔*/

void swapListApart(Node *p1, Node *p2); /* 元素交换*/

 

/* sort list  quick */

void sortListQuick(Node *phead, Node *pend)

{

    if( phead != pend)

    {

        Node *p = sortListApart(phead, pend);

        sortListQuick(phead, p);

        sortListQuick(p->next, pend);

    }

}

 

Node * sortListApart(Node *phead, Node *pend)

{

    Node *p1 = phead->next;

    Node *p2 = phead;

    int key = phead->elem;

    

    while(p1 != pend)

    {

        if( p1->elem <= key)

        {

            p2 = p2->next;

            swapListApart(p1, p2);

        }

        p1 = p1->next;

    }

    swapListApart(p2, phead);

    

    return p2;

}

void swapListApart(Node *p1, Node *p2)

{

    int tmp = p1->elem;

    p1->elem = p2->elem;

    p2->elem = tmp;

}

 

int main()

{

    /* 快速排序,对于链表 */

    Node *phead = listInit();

    listAdd(phead, 56);

    listAdd(phead, 32);

    listAdd(phead, 5);

    listAdd(phead, 12);

    listAdd(phead, 22);

    listAdd(phead, 4);

    listAdd(phead, 2);

    listAdd(phead, 9);

    listAdd(phead, 232);

    listAdd(phead, 51);

    listAdd(phead, 28);

    listAdd(phead, 32);

    listAdd(phead, 37);

    

    print_list(phead);

    sortListQuick(phead->next, NULL);

    print_list(phead);

    free(phead);

}

 


 

运行结果:

56 32 5 12 22 4 2 9 232 51 28 32 37

2 4 5 9 12 22 28 32 32 37 51 56 232

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值