链表的快速排序 - 区间分割法

本文介绍了如何将快速排序算法应用于链表,通过区间分割法进行排序。首先回顾数组的快速排序步骤,然后详细说明链表快速排序的函数参数、递归过程以及避免元素遗漏的关键点。通过代码示例和逻辑分析,帮助理解链表快排的工作原理。

首先,回忆一下数组的快排步骤:

函数参数: 数组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 后一个—-尾;
这样看似没问题,但是总是跳过元素,然后程序就崩了, 就是没理解好这块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值