快排大法好,不说日常数据处理的巨大优势,面试时能手写快排更是装X一大利器。
不过传统的快排有一大缺陷:当出现大量相同值或数据已经有序时,由于对相同值的重复递归,排序效率会急剧降低乃至O(N^2)。
为避免此情况笔者编写了快排PRO 优化算法(三明治法):
将传统快排的二分法(小于中点值、大于等于中点值)更改为三分(小于中点值、等于中点值、大于中点值)。且对等于中点值部分不再递归,因此即使存在大量重复数据依然能保持O(nlogn)的时间复杂度。
代码实现如下:
#define NODE 要排序的结构体名字
#define ATTR 这里填要排序的属性
#define NEXT 这里填存储下一个结点的变量名
/**
参数一为待排序的链表头结点
参数二返回的为排序以后的尾节点地址
*/
NODE* QUICK_SORT (NODE* SOUR,NODE **REAR)
{
if(SOUR==NULL) //为空结点
{
//此情况只有一种可能:要排序的链表是一个空链表
return NULL;
}
NODE *MID=SOUR,*L=NULL,*R=NULL; //中点\左侧\右侧
NODE *RR,*LR,*MR=SOUR; //左侧尾结点,右侧尾结点,中点链
NODE *TEMP,*P=SOUR->NEXT; //定位主串剩余串
while(P) //循环拆解原串
{
TEMP=P->NEXT;
if(P->ATTR<MID->ATTR) /**这里填排序规则(默认字符串排序strcmp(P->ATTR,MID->ATTR)<0)*/
{
if(L==NULL) //L为空串
{
L=LR=P;
L->NEXT=NULL;
}else
{
P->NEXT=L;
L=P;
}
}else if(P->ATTR>MID->ATTR) //排序规则+1,说明排在MID后
{
if(R==NULL)
{
R=RR=P;
R->NEXT=NULL;
}else
{
P->NEXT=R;
R=P;
}
}else //说明值与MID相同
{
P->NEXT=MR;
MR=P;
}
P=TEMP;
}
MID->NEXT=NULL; //防止排序结束后尾结点不为空
//拆解完毕,进行递归拆解
if(L!=NULL&&R!=NULL)
{
L=QUICK_SORT(L,&LR);
R=QUICK_SORT(R,&RR);
//合并已有序的链表
LR->NEXT=MR;
MID->NEXT=R;
*REAR=RR; //上传尾结点
return L;
}else if(R!=NULL)
{
R=QUICK_SORT(R,&RR);
MID->NEXT=R;
*REAR=RR;
return MR;
}else if(L!=NULL)
{
L=QUICK_SORT(L,&LR);
LR->NEXT=MR;
*REAR=MID;
return L;
}
//说明只有MID一个元素
*REAR=MID;
return MID;
}