首先,回忆一下数组的快排步骤:
函数参数: 数组arr, 起点nLow, 终点nHigh
功能: 把nLow 至 nHigh 段的数组进行一次快排。
结果:【比nMark小的元素】nMark【比nMark大的元素】
#区间分割法
1. 取最后一个元素为标记,用nSmall标记小区间大小,i 用来遍历数组;
2. 遍历值小于标记值:小区间的下一个不是当前位置,需要交换,否则直接小区间位置+1
3. 如此循环到 标记位置(最后一个元素)
4. 将标记元素(即最后一个元素)放到 s+1 的位置;
数组快排的链接: 快速排序(不稳定)挖坑填补法+区间分割法+3种优化方法
由此,我们写一下链表的步骤:
函数参数: List*pBegin,List*pEnd
1. 取第一个元素作为标记元素, 用pSmall指针,指向首元素;
2. 使用pTemp遍历链表, 找到找到小于标记的元素,将pSmall移到下一位置;
若下一个位置不是当前位置, 将pSmall 和 pTemp 的元素进行交换;
3. 重复 2, 直到pTemp == pEnd;
4. 此时pSmall 指向小链表的最后一个元素, 将这个元素和标记元素(链表头)进行交换;
5. 分别递归前半部分和后半部分;
先上代码,再分析:
//链表的快排
void QuickSort2(List* pBegin, List* pEnd)
{
if (pBegin == NULL)
return;
if (pBegin == pEnd)
return;
int nTemp;
//1. 取第一个元素作为标记元素, 用pSmall指针,指向首元素;
int nMark = pBegin->nvalue;
List* pSmall = pBegin;
//2. 使用pTemp遍历链表
List* pTemp = pBegin->pNext;
while (pTemp != pEnd)
{
//2.1 找到找到小于标记的元素,将pSmall移到下一位置;
if (pTemp->nvalue < nMark)
{
pSmall = pSmall->pNext;
//2.2 若下一个位置不是当前位置, 将pSmall 和 pTemp 的元素进行交换;
if (pSmall != pTemp)
{
nTemp = pTemp->nvalue;
pTemp->nvalue = pSmall->nvalue;
pSmall->nvalue = nTemp;
}
}
pTemp = pTemp->pNext;
}
//把小链表的最后一个元素和头(标记)交换
//这样pSmall 前就是比nMark小的,后就是比nMark大的;
//这块就是把: 16,9,5,15,8,23 --》 8,9,5,15,16,23;
nTemp = pSmall->nvalue;
pSmall->nvalue = pBegin->nvalue;
pBegin->nvalue = nTemp;
//while 循环判断,刚好不需要判断最后一个元素,因此就把pSmall 跳过了;
QuickSort2(pBegin, pSmall);
QuickSort2(pSmall->pNext, pEnd);
}
int main()
{
int arr[] = {16,9,23,5,15,8};
Mylist list;
List* pList = list.CreateaaList(arr, sizeof(arr) / 4);
list.Print(pList);
QuickSort2(pList, NULL);
list.Print(pList);
system("pause");
return 0;
}
解释一下, 递归的参数问题:
在数组的快排中,我们是这样写的,先一次快排,将元素分成两部分, 前部分比标记小,后部分比标记大。返回标记位置,再对 标记的前后两部分进行排序;标记元素已经确定位置,不需要再参加排序了。
int nStandard = Sort(arr, nLow, nHigh);
QuickSort(arr, nLow, nStandard -1);
QuickSort(arr, nStandard+1, nHigh);
在 链表的快排中, 循环判断条件是 pTemp != pEnd, 也就是pEnd 这一位没有进行判断是否加入小链表中。
在函数调用时,我们传入的参数是 QuickSort2(pList, NULL);
所以刚好pTemp是从头判断到尾。
进行递归的时候, 传入的pSmall 刚好就不被判断。完成“已经确定位置的标记元素,不需要再参加排序了。” 这一规则。
QuickSort2(pBegin, pSmall);
QuickSort2(pSmall->pNext, pEnd);
在开始写的时候, 总想着需要记录pSmall 的前一个元素, 然后递归的时候传
头 — small 前一个, small 后一个—-尾;
这样看似没问题,但是总是跳过元素,然后程序就崩了, 就是没理解好这块。