单向链表排序:快速排序和归并排序

对单向链表的排序有2种形式,只改变节点的值只改变链接

// 节点
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int v, ListNode* n = NULL) {
        val = v;
        next = n;
    }
};

本文链接:单向链表排序:快速排序和归并排序

参考资料链接:
链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)

1. 归并排序(改变链接)

归并排序是最适合单链表排序的算法,因为两个链表的归并比较简单,和数组的归并过程思路相同。
需要注意的是:如何找到链表的中点?
通过2个快慢指针,快指针每一步走2个节点,慢指针每一步走1个节点,当快指针到达链表尾部时,慢指针到达链表的中间节点。

class MergeSort {
public:
    static ListNode* ListMergeSort(ListNode* pHead) {
        if (pHead == NULL || pHead->next == NULL)
            return pHead;
        ListNode* pFast = pHead;
        ListNode* pSlow = pHead;
        // 找到位于中间的节点,快慢指针,快的到结尾,慢的到中点
        while (pFast != NULL && pFast->next == NULL) {
            pFast = pFast->next->next;
            pSlow = pSlow->next;
        }
        pFast = pSlow;
        pSlow = pSlow->next; // 分割成2段
        pFast->next = NULL;
        pHead = ListMergeSort(pHead);
        pSlow = ListMergeSort(pSlow);
        pHead = merge(pHead, pSlow);
        return pHead;
    };
private:
    static ListNode* merge(ListNode* pHead1, ListNode* pHead2) {
        if (pHead1 == NULL || pHead2 == NULL) {
            return pHead1 == NULL ? pHead2 : pHead1;
        }
        // 找出合并后的链表头
        ListNode* pHead = NULL;
        if (pHead1->val > pHead2->val) {
            pHead = pHead2;
            pHead2 = pHead2->next;
        } else {
            pHead = pHead1;
            pHead1 = pHead1->next;
        }
        // 将2个链表中值较小的节点依次链接到结果链表中
        ListNode* pHead_orgin = pHead;
        while (pHead1 != NULL && pHead2 != NULL) {
            if (pHead1->val > pHead2->val) {
                pHead->next = pHead2;
                pHead2 = pHead2->next;
            } else {
                pHead->next = pHead1;
                pHead1 = pHead1->next;
            }
            pHead = pHead->next;
        }
        if (pHead1 == NULL)
            pHead->next = pHead2;
        else
            pHead->next = pHead1;
        return pHead_orgin;
    };
};

2. 快速排序(改变链接)

只改变链接的指向。
将比基元(第一个节点)小的值,链接到一个左链表中;比基元大的值,链接到一个右链表中;
将左链表,基元,右链表链接起来。

对一段链表执行划分过程时,头节点可能发生改变以及终止节点可能是非空的,因此对一段链表的划分过程需要给出
前驱节点(指向头节点)和终止节点(标志结束)
故对传入划分函数的一次链表划分处理不包括第一个节点和最后一个节点的。(双开区间)

class QuickSort {
public:
    static ListNode* ListQuickSort(ListNode* pHead) {
        if (pHead == NULL || pHead->next == NULL)
            return pHead;
        ListNode headPrev(0, pHead); // pHead 的前驱节点
        ListQuickSort(&headPrev, pHead, NULL);
        return headPrev.next;
    }
private:
    static void ListQuickSort(ListNode* pHeadPrev, ListNode* pHead, ListNode* pTail) {
        // 处理过程不涉及 pHeadPrev 和 pTail
        if (pHead == pTail || pHead->next == pTail)
            return;
        ListNode* pMid = Partation(pHeadPrev, pHead, pTail);
        ListQuickSort(pHeadPrev, pHeadPrev->next, pMid);
        ListQuickSort(pMid, pMid->next, pTail);
    };

    static ListNode* Partation(ListNode* pHeadPrev, ListNode* pHead, ListNode* pTail) {
        int key = pHead->val; // 选第一个节点为基元
        ListNode nodeL(0), nodeR(0);
        ListNode* pLeftPrev = &nodeL; // 小于基元的链表
        ListNode* pRightPrev = &nodeR; // 大于等于基元的链表
        for (ListNode* pNode = pHead->next; pNode != pTail; pNode = pNode->next) {
            if (pNode->val < key) {
                pLeftPrev->next = pNode;
                pLeftPrev = pNode;
            } else {
                pRightPrev->next = pNode;
                pRightPrev = pNode;
            }
        }
        pRightPrev->next = pTail; // 保证子链表继续和后面的相连
        pLeftPrev->next = pHead; // 基元节点链接
        pHead->next = nodeR.next;
        pHeadPrev->next = nodeL.next; // 链表头节点
        return pHead;
    };
};

3. 快速排序(改变节点值)

由于链表只能顺序索引,故不能使用数组划分的方法。
将比基元小的节点的值,依次和基元后的节点的值交换。
如 5->3->6->4->7->2
5 为基元
3 < 5: swap(3, 3) ,(起始交换位置为基元的下一个节点,即第2个节点)
6 > 5: continue;
4 < 5: swap(6, 4) (交换位置后移,交换4和第3个节点的值)
7 > 5: continue
2 < 5: swap(4, 2) (交换位置后移,交换2和第4个节点的值)

循环结束 swap(5, 2) (交换基元值和第4个节点的值)

最后得到:2->3->4->5->6->7

class QuickSortValue {
public:
    static void QuickSort(ListNode* pHead) {
        if (pHead == NULL || pHead->next == NULL)
            return;
        QuickSort(pHead, NULL);
    }
private:
    static void QuickSort(ListNode* pHead, ListNode* pTail) {
        if (pHead == pTail || pHead->next == pTail)
            return;
        ListNode* pMid = Partation(pHead, pTail);
        QuickSort(pHead, pMid);
        QuickSort(pMid->next, pTail);
    }
    static ListNode* Partation(ListNode* pHead, ListNode* pTail) {
        if (pHead == pTail || pHead->next == pTail)
            return pHead;
        ListNode* pPivot = pHead; // 基元
        int key = pHead->val;
        for (ListNode* pNode = pHead->next; pNode != pTail; pNode = pNode->next) {
            if (pNode->val < key) {
                pPivot = pPivot->next; // 将比基元小的值,依次交换到前面
                swap(pNode, pPivot);
            }
        }
        swap(pHead, pPivot);
        return pPivot;
    }
    static void swap(ListNode* pA, ListNode* pB) {
        int temp = pA->val; pA->val = pB->val; pB->val = temp;
    }
};

4. 所有源码和测试函数

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int v, ListNode* n = NULL) {
        val = v;
        next = n;
    }
};
void PrintList(ListNode* pHead) {
    while (pHead != NULL) {
        cout << pHead->val << " ";
        pHead = pHead->next;
    }
    cout << endl;
    return;
}
class MergeSort {
public:
    static ListNode* ListMergeSort(ListNode* pHead) {
        if (pHead == NULL || pHead->next == NULL)
            return pHead;
        ListNode* pFast = pHead;
        ListNode* pSlow = pHead;
        // 找到位于中间的节点,快慢指针,快的到结尾,慢的到中点
        while (pFast != NULL && pFast->next == NULL) {
            pFast = pFast->next->next;
            pSlow = pSlow->next;
        }
        pFast = pSlow;
        pSlow = pSlow->next; // 分割成2段
        pFast->next = NULL;
        pHead = ListMergeSort(pHead);
        pSlow = ListMergeSort(pSlow);
        pHead = merge(pHead, pSlow);
        return pHead;
    };
private:
    static ListNode* merge(ListNode* pHead1, ListNode* pHead2) {
        if (pHead1 == NULL || pHead2 == NULL) {
            return pHead1 == NULL ? pHead2 : pHead1;
        }
        // 找出合并后的链表头
        ListNode* pHead = NULL;
        if (pHead1->val > pHead2->val) {
            pHead = pHead2;
            pHead2 = pHead2->next;
        } else {
            pHead = pHead1;
            pHead1 = pHead1->next;
        }
        ListNode* pHead_orgin = pHead;
        while (pHead1 != NULL && pHead2 != NULL) {
            if (pHead1->val > pHead2->val) {
                pHead->next = pHead2;
                pHead2 = pHead2->next;
            } else {
                pHead->next = pHead1;
                pHead1 = pHead1->next;
            }
            pHead = pHead->next;
        }
        if (pHead1 == NULL)
            pHead->next = pHead2;
        else
            pHead->next = pHead1;
        return pHead_orgin;
    };
};

class QuickSort {
public:
    static ListNode* ListQuickSort(ListNode* pHead) {
        if (pHead == NULL || pHead->next == NULL)
            return pHead;
        ListNode headPrev(0, pHead); // pHead 的前驱节点
        ListQuickSort(&headPrev, pHead, NULL);
        return headPrev.next;
    }
private:
    static void ListQuickSort(ListNode* pHeadPrev, ListNode* pHead, ListNode* pTail) {
        if (pHead == pTail || pHead->next == pTail)
            return;
        ListNode* pMid = Partation(pHeadPrev, pHead, pTail);
        ListQuickSort(pHeadPrev, pHeadPrev->next, pMid);
        ListQuickSort(pMid, pMid->next, pTail);
    };

    static ListNode* Partation(ListNode* pHeadPrev, ListNode* pHead, ListNode* pTail) {
        int key = pHead->val; // 选第一个节点为基元
        ListNode nodeL(0), nodeR(0);
        ListNode* pLeftPrev = &nodeL;
        ListNode* pRightPrev = &nodeR;
        for (ListNode* pNode = pHead->next; pNode != pTail; pNode = pNode->next) {
            if (pNode->val < key) {
                pLeftPrev->next = pNode;
                pLeftPrev = pNode;
            } else {
                pRightPrev->next = pNode;
                pRightPrev = pNode;
            }
        }
        pRightPrev->next = pTail; // 保证子链表继续和后面的相连
        pLeftPrev->next = pHead; // 基元节点链接
        pHead->next = nodeR.next;
        pHeadPrev->next = nodeL.next;
        return pHead;
    };
};

class QuickSortValue {
public:
    static void QuickSort(ListNode* pHead) {
        if (pHead == NULL || pHead->next == NULL)
            return;
        QuickSort(pHead, NULL);
    }
private:
    static void QuickSort(ListNode* pHead, ListNode* pTail) {
        if (pHead == pTail || pHead->next == pTail)
            return;
        ListNode* pMid = Partation(pHead, pTail);
        QuickSort(pHead, pMid);
        QuickSort(pMid->next, pTail);
    }
    static ListNode* Partation(ListNode* pHead, ListNode* pTail) {
        if (pHead == pTail || pHead->next == pTail)
            return pHead;
        ListNode* pPivot = pHead; // 基元
        int key = pHead->val;
        for (ListNode* pNode = pHead->next; pNode != pTail; pNode = pNode->next) {
            if (pNode->val < key) {
                pPivot = pPivot->next; // 将比基元小的值,依次交换到前面
                swap(pNode, pPivot);
            }
        }
        swap(pHead, pPivot);
        return pPivot;
    }
    static void swap(ListNode* pA, ListNode* pB) {
        int temp = pA->val; pA->val = pB->val; pB->val = temp;
    }
};

int main() 
{
    srand(time(0));
    const int N = 30;
    ListNode* pHead = NULL;
    for (int i = 0; i < N; i++) {
        if (pHead == NULL) {
            pHead = new ListNode(rand() % 100 + 1, NULL);
        }
        else {
            ListNode* tempNode = new ListNode(rand() % 100 + 1, pHead);
            pHead = tempNode;
        }
    }
    PrintList(pHead);
    pHead = MergeSort::ListMergeSort(pHead); // merge-sort
   // pHead = QuickSort::ListQuickSort(pHead); // quick-sort-swap-node
   //QuickSortValue::QuickSort(pHead); // quick-sort-swap-value
    PrintList(pHead);
}
单向链表排序可以通过不同的排序算法来实现,比如冒泡排序、插入排序、选择排序归并排序快速排序等。下面以冒泡排序为例进行说明。 冒泡排序是一种简单的排序算法,它重复地遍历链表,并通过两个相邻节点的比较来交换节点的位置,直到整个链表有序为止。具体实现步骤如下: 1. 设置两个指针,一个指向链表的头节点,另一个指向头节点的下一个节点。 2. 遍历链表,比较当前节点和下一个节点的值,如果需要交换位置则交换。 3. 继续遍历链表,直到指针指向链表的尾节点。 4. 如果有任何节点交换了位置,则重新开始遍历链表,直到没有节点需要交换位置为止。 5. 链表排序完成。 下面是使用C语言实现的单向链表排序代码: ```c #include <stdio.h> // 定义链表节点结构 struct ListNode { int val; struct ListNode *next; }; // 冒泡排序函数 void bubbleSort(struct ListNode *head) { if (head == NULL || head->next == NULL) { return; } int swapped; struct ListNode *ptr1; struct ListNode *lptr = NULL; do { swapped = 0; ptr1 = head; while (ptr1->next != lptr) { if (ptr1->val > ptr1->next->val) { // 交换节点位置 int tmp = ptr1->val; ptr1->val = ptr1->next->val; ptr1->next->val = tmp; swapped = 1; } ptr1 = ptr1->next; } lptr = ptr1; } while (swapped); } // 测试用例 int main() { // 创建链表 struct ListNode n1, n2, n3, n4; n1.val = 4; n2.val = 2; n3.val = 1; n4.val = 3; n1.next = &n2; n2.next = &n3; n3.next = &n4; n4.next = NULL; // 排序链表 bubbleSort(&n1); // 输出排序结果 struct ListNode *ptr = &n1; while (ptr != NULL) { printf("%d ", ptr->val); ptr = ptr->next; } return 0; } ``` 以上代码中,我们首先定义了一个链表节点结构 `struct ListNode`,包含一个整数值 `val` 和一个指向下一个节点的指针 `next`。然后我们使用冒泡排序算法实现了 `bubbleSort` 函数,最后在 `main` 函数中创建了一个测试用例并进行排序输出。输出结果为 `1 2 3 4`,表示链表已经按照从小到大的顺序进行了排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值