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

    • 归并排序改变链接
    • 快速排序改变链接
    • 快速排序改变节点值
    • 所有源码和测试函数

对单向链表的排序有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);
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-23 07:47:55

单向链表排序:快速排序和归并排序的相关文章

单向链表排序

一.冒泡排序简述 1.概念 冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法. 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字由来是因为越大的元素会经由交换慢慢"浮"到数列的顶端. 2.实例分析 以数组为例进行说明: 数组a[4] = {4,3,2,1} 从前向后依次比较两个元素的大小,如果顺序错误就交换它们.经过这样一轮,最大的元素就被

C# 插入排序 冒泡排序 选择排序 快速排序 堆排序 归并排序 基数排序 希尔排序

下面列出了数据结构与算法的八种基本排序:插入排序 冒泡排序 选择排序 快速排序 堆排序 归并排序 基数排序 希尔排序,然后是测试的例子.代码位置:http://download.csdn.net/detail/luozuolincool/8040027 排序类: public class Sortings { //插入排序 public void insertSort(int[] array) { int temp = 0; int index = 0; for (int i = 0; i <

Java实现单链表的快速排序和归并排序

本文描述了LeetCode 148题 sort-list 的解法. 题目描述如下: Sort a linked list in O(n log n) time using constant space complexity. 题目要求我们在O(n log n)时间复杂度下完成对单链表的排序,我们知道平均时间复杂度为O(n log n)的排序方法有快速排序.归并排序和堆排序.而一般是用数组来实现二叉堆,当然可以用二叉树来实现,但是这么做太麻烦,还得花费额外的空间构建二叉树,于是不采用堆排序. 故本

单链表排序——快速排序实现

利用快速排序,同向一前一后两个指针 #ifndef LIST_H_ #define LIST_H_ #include <iostream> #include <utility> class List { private: struct ListNode { int _value; ListNode* _next; }; public: List(): _head(nullptr) {} ~List() { while (nullptr != _head) { auto tmp =

单向链表排序时间复杂度比较低的一种排序算法

归并排序:   来自网上一篇博文,先贴上链接,后期将会上传个人见解: http://blog.sina.com.cn/s/blog_78a4bd490101fow8.html

【模板小程序】链表排序(qsort/insert_sort/merge_sort)

前言 本文章整理了链表排序的三种方法,分别是快速排序.插入排序.归并排序.为适应不同用途,先给出常用的int版本,再在此基础上抽象出类模板. 目录 一.针对整数的版本(常用) 文中链表定义 链表相关操作 三种排序方法 完整测试程序 二.模板版本(适用性广泛) 文中链表定义 链表相关操作 三种排序方法 完整测试程序 总结 参考文章 一.针对整数的版本(常用) 文中链表定义: 1 //definition for singly-linked list. 2 struct ListNode 3 { 4

单链表的排序 快速排序 归并排序 quicksort mergesort

原理都很简单,关键是某些边界能否正确写对: #include<iostream> #include<stdio.h> using namespace std; class Node { public: int val; Node* next; Node(int val = 0):val(val),next(NULL){ } }; Node* quicksort(Node* head, Node* tail) { Node *res1 = NULL, *res2 = NULL; No

七大内部排序算法总结(插入排序、希尔排序、冒泡排序、简单选择排序、快速排序、归并排序、堆排序)

 写在前面: 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的任意序列,重新排列成一个按关键字有序的序列.因此排序掌握各种排序算法非常重要.对下面介绍的各个排序,我们假定所有排序的关键字都是整数.对传入函数的参数默认是已经检查好了的.只是简单的描述各个算法并给出了具体实现代码,并未做其他深究探讨. 基础知识: 由于待排序的记录数量不同,使得排序过程中设计的存储器不同,可将排序方法分为两大类:一类是内部排序,指的是待排序记录存放在计算机随机存储器中进行的排序过程.另一类是外部排序,

排序算法的C语言实现(上 比较类排序:插入排序、快速排序与归并排序)

总述:排序是指将元素集合按规定的顺序排列.通常有两种排序方法:升序排列和降序排列.例如,如整数集{6,8,9,5}进行升序排列,结果为{5,6,8,9},对其进行降序排列结果为{9,8,6,5}.虽然排序的显著目的是排列数据以显示它,但它往往可以用来解决其他的问题,特别是作为某些成型算法的一部分. 总的来说,排序算法分为两大类:比较排序 和 线性时间排序. 比较排序依赖于比较和交换来将元素移动到正确的位置上.它们的运行时间往往不可能小于O(nlgn). 对于线性时间排序,它的运行时间往往与它处理