本想先用快速排序算法对一个数组的元素从小到大进行排序,然后把快速排序算法用于链表元素从小到大进行排序的,但是发现一个问题改变了我的想法,先把问题呈上。
volatile uint16_t count = 0;//统计递归执行的次数
volatile uint16_t num = 0;//统计快速算法进行排序时执行的次数
//快速排序算法--从小到大依次排序
void quickSort(uint8_t *dataBuf,uint8_t leftSub,uint8_t rightSub)
{
uint8_t left,right,temp;
uint8_t keyNum;
left = leftSub;
right = rightSub;
keyNum = dataBuf[(leftSub+rightSub)/2];
++count;
while(left <= right)
{
++num;
//从左到右找比keyNum大的元素
while((dataBuf[left] < keyNum) && (left < rightSub))
{
left++;
}
//从右到左找比keyNum小的元素
while((dataBuf[right] > keyNum) && (right > leftSub))
{
right--;
}
//若找到且满足条件的,则交换
if(left <= right)
{
temp = dataBuf[left];
dataBuf[left] = dataBuf[right];
dataBuf[right] = temp;
left++;
right--;
}
}
if(left < rightSub)
{
quickSort(dataBuf,left,rightSub);
}
if(right >leftSub)
{
quickSort(dataBuf,leftSub,right);
}
}
int main()
{
uint8_t data[]={8,2,5,3,4,6,9,1,7};//组1:8,2,5,3,4,6,9,1,7 组2:9,7,8,6,5,4,2,3,1
quickSort(data,0,sizeof(data)-1);//把数组,最左下标和最有下标作为参数传入快速排序函数中
printf("count=%d,num=%d \r\n",count,num);
for(uint8_t index = 0;index < sizeof(data);index++)
{
printf("%d ",data[index]);
}
return 0;
}
数组元素经过从小到大的排序后,输出打印显示如下所示,
我们可以看到,对这样一组数据data[]={8,2,5,3,4,6,9,1,7}进行排序,调用快速排序算法函数调了6次,内部总共执行了9次完成了从小到大的排序,这种情况是比较理想的,如果我们把data数组的元素换成组2的元素,再打印结果,出乎了我们的预料,打印结果如下:
我们可以看到,用快速算法对数组data[]={9,7,8,6,5,4,2,3,1}从小到大进行排序,发现调用快速算法函数72次,内部总共执行510次,程序没有继续执行,发生异常了,如果在程序中使用快速算法,会导致程序内存溢出,程序崩溃等故障,所以在这里(单向链表元素数据项进行排序)就不用快速排序算法, 网上查了一下几种排序算法的稳定性,可以看到,插入、冒泡、堆、归并和基数排序是稳定的。
这里我们用选择排序对单向链表的元素数据项从小到大进行排序,首先还是先用选择排序对一个数组进行从小到大进行排序,然后在这个选择排序算法函数中把数组改为单向链表。
//选择排序算法--从小到大依次排序
void SelectSort(uint8_t *dataBuf,uint16_t length)
{
uint8_t temp=0;
printf("未排序的数组:\r\n");
ArrayPrintShow(dataBuf,length);//排一次序
printf("\r\n正在排序的数组:\r\n");
for(uint8_t i = 0;i<length-1;i++)
{
for(uint8_t j = i+1;j<length;j++)
{
if(dataBuf[i] > dataBuf[j])
{
temp = dataBuf[i];
dataBuf[i] = dataBuf[j];
dataBuf[j] = temp;
}
}
ArrayPrintShow(dataBuf,length);//排一次序
}
}
//信息打印显示
void ArrayPrintShow(uint8_t *data,uint16_t len)
{
for(uint8_t index = 0;index < len;index++)
{
printf("%d ",data[index]);
}
printf("\r\n");
}
int main()
{
uint8_t data[]={9,7,8,6,5,4,2,3,1};
SelectSort(data,sizeof(data));
return 0;
}
数组经过从小到大进行排序后,打印结果如下所示:
下面我们就在这个选择排序算法函数中对前面讲的单向链表中的图书价格进行从小到达进行排序,最后再输出打印显示。
根据算法画出流程图如下图所示:
选择排序算法程序实现如下所示:
int SingLinkList_SelectSort(BOOKLT *bookHeadNode)
{
BOOKLT *pFront = bookHeadNode;
BOOKLT *p = pFront->next;
BOOKLT *qFront = NULL;
BOOKLT *q = NULL;
BOOKLT * temp = NULL;
if((NULL==pFront->next)||(NULL==p->next))//链表只有头结点或者链表只有一个结点不需要排序
{
return -1;
}
while(p)//pFront->next不等于空
{
qFront = p;
q = qFront->next;
while(q)//qFront->next不等于空
{
if((p->bookInfo.price) > (q->bookInfo.price))
{
/*
//方式一:结点内存数据拷贝,不交换结点
memcpy(temp->bookInfo.ISBN,q->bookInfo.ISBN,sizeof(q->bookInfo.ISBN));
memcpy(temp->bookInfo.bookName,q->bookInfo.bookName,sizeof(q->bookInfo.bookName));
temp->bookInfo.price = q->bookInfo.price;
memcpy(q->bookInfo.ISBN,p->bookInfo.ISBN,sizeof(q->bookInfo.ISBN));
memcpy(q->bookInfo.bookName,p->bookInfo.bookName,sizeof(q->bookInfo.bookName));
q->bookInfo.price = p->bookInfo.price;
memcpy(p->bookInfo.ISBN,temp->bookInfo.ISBN,sizeof(q->bookInfo.ISBN));
memcpy(p->bookInfo.bookName,temp->bookInfo.bookName,sizeof(q->bookInfo.bookName));
p->bookInfo.price = temp->bookInfo.price;
*/
//方式二:只移动指针指向,不拷贝数据;断开结点,结点交换,再重新插入节点
if(qFront == p)//情况1:p结点和q结点相邻,且p结点元素中的价格数据项大于q结点元素中的价格数据项
{
//主链表断开p结点和q结点
qFront->next=q->next;
q->next = NULL;
qFront=pFront;//重置qFront
pFront->next=p->next;
p->next = NULL;
//两个结点相互切换指向
temp = q;
q = p;
p = temp;
//重新把新p结点插入到pFront之后
p->next = pFront->next;
pFront->next = p;
qFront = p; //重置qFront
//重新把新q结点插入到qFront之后
q->next=qFront->next;
qFront->next=q;
}
else//情况2
{
//主链表断开p结点和q结点
qFront->next = q->next;
q->next = NULL;
pFront->next = p->next;
p->next = NULL;
//两个结点相互切换指向
temp = q;
q = p;
p = temp;
//重新把新p结点插入到pFront之后
p->next = pFront->next;
pFront->next = p;
//重新把新q结点插入到qFront之后
q->next = qFront->next;
qFront->next=q;
}
}
//内循环--往下一个结点移动
qFront = q;
q = qFront->next;
}
//外循环--往下一个结点移动
pFront = p;
p = pFront->next;
}
return true;
}
main.c主文件实现如下所示:
int main()
{
BOOKLT *myBookHeadNode;//定义一个BOOKLT类型的头结点指针
myBookHeadNode = SingLinkList_Create();//上面的链表内存已经被释,重新放创建一个头结点
SingLinkList_TailInsert_method2(myBookHeadNode,sizeof(bookStock)/sizeof(BOOKINFO),bookStock);//后插法插入节点
printf("=============所有图书信息打印如下=================\r\n");
SingLinkList_Traverse(myBookHeadNode);//遍历单向链表
printf("\r\n===========所有图书价格从低到高排序如下===========\r\n");
//价格从低到高进行排序再输出
SingLinkList_SelectSort(myBookHeadNode);//选择排序调用
SingLinkList_Traverse(myBookHeadNode);//遍历单向链表
SingLinkList_Destory(myBookHeadNode);//释放各个结点的内存和头结点的内存
return 0;
}
图书价格排序前和排序后如下图所示:
到此,图书价格按低到高顺序排序功能已完成。
2022.06.19结。