浅谈单链表快速排序

        这几天笔者在参加学院的企业实训,其中有一个模块需要用到单链表的快速排序,因为之前的快速排序是针对于数组而言的,而对于链表则不支持下标的访问,因此单链表的排序就需要点技巧。

        对于数组的快速排序,它的基本思路就是说选择一个数据为基准,将小于基准的元素放在这个元素的左边,大于基准的放在这个元素的右边,然后通过递归来对左右两个子数组分别进行快速排序。

        笔者从以下两篇文章中获得启发:

        单链表快速排序(作者PinkRobin): http://blog.csdn.net/PinkRobin/article/details/5456094

        单链表快速排序算法实现(作者yangalbert):http://blog.csdn.net/yangalbert/article/details/7577782

        了解到链表的快速排序可以用指针的指针来改变每个链表的下一个节点所指向的地址,从而实现选择一个数据为基准,小于基准的放在左边的子链表,大于基准的放在右边的子链表,然后通过递归来对左右两个子链表进行快速排序。

        首先先摆上笔者所写的快速排序算法:

        在此之前定义结构体:

typedef struct _LinkList
{
	int key;
	struct _LinkList *pNext;
}LinkList;

        算法部分如下:

void QuickSortForLinkList(LinkList **pHead,LinkList *pEnd)
{
	LinkList **Left,**Right,*Walk,*pPivot,*LLRight;
	Left=pHead;
	pPivot=*pHead;
	Right=&((*Left)->pNext);
	LLRight=*Right;
	if (*pHead==pEnd||(*pHead)->pNext==pEnd)
	{
		return;
	}
	for (Walk=(*Left)->pNext;Walk!=pEnd;Walk=Walk->pNext)
	{
		if (Walk->key < pPivot->key)
		{
			*Left=Walk;
			Left=&(Walk->pNext);
		}
		else
		{
			*Right=Walk;
			Right=&(Walk->pNext);
		}
	}
	*Right=pEnd;
	*Left=pPivot;
	QuickSortForLinkList(pHead,pPivot);
	QuickSortForLinkList(&(pPivot->pNext),pEnd);
}

        代码第一行为函数头,函数无返回值,有两个参数,第一个参数的类型为对于LinkList指针的指针类型,之所以传递来对于LinkList指针的指针,并没有别的意思,只是为了在函数调用时能将原链表的头地址变为排序的链表的头地址,说白了就是传址调用,如果是LinkList的指针类型,那么在函数调用完毕时,函数内部的指针所占用的内存空间会被系统收回,那么此时,调用函数时指向的链表头地址的指针就没有办法得到更新,而指向的是原链表头结点。读者可以自行将代码改为LinkList的指针试试看便知。

        第二个参数为链表尾节点的下一个节点,设置这个参数的目的在于方便递归调用。

        第三行代码,分别声明了两个对于LinkList指针的指针Left和Right,此外还声明了Walk这个遍历链表的指针,pPivot为基准点指针,LLRight为右子链表的头指针。

        如果指针p1可以这样理解:

        那么对于LinkList指针的指针p1则可以这样理解:

        不妨假设上图的Key所在的节点为LL2,且LL1.pNext指向的是LL2,于是有*(LL1.pNext)代表LL2,而我们不难发现,(**p1)也代表的是LL2,于是做个等价的代换,**p1也就是*(LL1.pNext),那么p1指向的位置,就可以简单的理解为LL1.pNext,*p1指向的位置,就是LL2这个结构体。也就是说,如果我们修改了*p1的值,也就是修改LL1.pNext,让LL1连接到了别的节点上。

        明白了这一点,算法部分的代码就很好理解了。第四行到第七行分别是初始化各个指针,第八行到第十一行代表递归结束的条件。12行到24行是判断其它节点与基准的大小,把比基准小的放到左边的子链表中,把比基准大的放到右边的子链表中。最后25到26行,将整个链表串接起来,这样,初步排好序的链表就形成了。

        接下来的27行与28行分别是对两个子链表进行快速排序。

        这样就完成了对链表的快速排序。

        附:笔者所写的DEMO:

#include <iostream>
using namespace std;
typedef struct _LinkList
{
	int key;
	struct _LinkList *pNext;
}LinkList;

void QuickSortForLinkList(LinkList **pHead,LinkList *pEnd);

int main()
{
	LinkList *p1,*p2,*pHead;
	int LKarray[]={5,2,4,6,1,9};
	int n=0;
	p1=(LinkList*)malloc(sizeof(LinkList));
	for (int i=0;i<6;i++)
	{
		n++;
		if (n==1)
		{
			pHead=p1;
		}
		p1->key = LKarray[i];
		p2=p1;
		p1=(LinkList*)malloc(sizeof(LinkList));
		p2->pNext=p1;
	}
	p2->pNext=NULL;
	free(p1);
	QuickSortForLinkList(&pHead,NULL);
	p1=pHead;
	while (p1!=NULL)
	{
		cout <<p1->key <<endl;
		p1=p1->pNext;
	}
	return 0;
}

void QuickSortForLinkList(LinkList **pHead,LinkList *pEnd)
{
	LinkList **Left,**Right,*Walk,*pPivot,*LLRight;
	Left=pHead;
	pPivot=*pHead;
	Right=&((*Left)->pNext);
	LLRight=*Right;
	if (*pHead==pEnd||(*pHead)->pNext==pEnd)
	{
		return;
	}
	for (Walk=(*Left)->pNext;Walk!=pEnd;Walk=Walk->pNext)
	{
		if (Walk->key < pPivot->key)
		{
			*Left=Walk;
			Left=&(Walk->pNext);
		}
		else
		{
			*Right=Walk;
			Right=&(Walk->pNext);
		}
	}
	*Right=pEnd;
	*Left=pPivot;
	QuickSortForLinkList(pHead,pPivot);
	QuickSortForLinkList(&(pPivot->pNext),pEnd);
}


        最后想说的:以上为笔者的个人理解,可能与实际情况有所偏差,亦可能逻辑上或者代码上存在问题,欢迎大家指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值