链表的快速排序和归并排序

Posted 清水寺扫地僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表的快速排序和归并排序相关的知识,希望对你有一定的参考价值。

不同于数组快排和归并排序(见排序归纳总结(插入排序、归并排序、堆排序、快速排序、桶排序)),链表不能够随机访问,只能从链表头遍历至链表尾,针对这一特性,需要对数组的排序算法做相应的调整。


1. 快速排序

对于链表的快速排序,使用两个指针p和q,使得p和q都只向其next方向进行遍历,同时保证p之前的链表节点的val都小于head的val值,p和q之间的链表节点的val都大于head的val值,则当q遍历到链表末尾时,再将head的val和p的val值进行互换,则就进行了 一次paritition操作。

代码如下:

// ListQucikSort.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>

using namespace std;

struct ListNode {
	int val;
	ListNode* next;
	ListNode(int x) : val(x), next(nullptr) {}
};

void insertNode(ListNode* head, ListNode* cur) {
	cur->next = head->next;
	head->next = cur;
	return;
}

ListNode* partition(ListNode* head, ListNode* tail) {
	ListNode *p = head, *q = head;
	int pivot = head->val;
	while (q != tail) {
		if (q->val < pivot) {
			p = p->next;
			swap(p->val, q->val);
		}
		q = q->next;
	}
	swap(p->val, head->val);
	return p;
}

ListNode* quickSort(ListNode* head, ListNode* tail) {
	if (head == tail) return head;
	ListNode* mid = partition(head, tail);
	head = quickSort(head, mid);
	quickSort(mid->next, tail);
	return head;
}

ListNode* quickSortAll(ListNode* head) {
	if (!head || !head->next) return head;
	return quickSort(head, nullptr);
}

int main()
{
	ListNode* head = new ListNode(-1);
	insertNode(head, new ListNode(10));
	insertNode(head, new ListNode(4));
	insertNode(head, new ListNode(11));
	insertNode(head, new ListNode(6));
	insertNode(head, new ListNode(3));
	insertNode(head, new ListNode(5));
	insertNode(head, new ListNode(1));
	head = quickSortAll(head->next);
	ListNode* node = head;
	while (node) {
		cout << node->val << ' ';
		node = node->next;
	}
	int x; cin >> x;
	cout << endl;
	return  0;
}



2. 归并排序

链表归并排序的重点在于,有序链表的合并(即merge操作,剑指 Offer 25. 合并两个排序的链表)和链表中点的查找(查找到中间节点以便进行归并,运用快慢指针,876. 链表的中间结点)。

代码如下:

// 链表的归并排序.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};


void insertNode(ListNode* head, ListNode* cur) {
    cur->next = head->next;
    head->next = cur;
    return;
}

ListNode* mergeList(ListNode* l1, ListNode* l2) { //进行链表合并
    if (!l1) return l2;
    if (!l2) return l1;
    ListNode* head = l1->val > l2->val ? l2 : l1;  //head节点作为合并后链表的头节点
    l1 = head == l1 ? l1->next : l1;
    l2 = head == l2 ? l2->next : l2;
    ListNode* tail = head; //tail节点作为helper指针,向合并的链表添加节点
    while (l1 && l2) {
        if (l1->val > l2->val) { tail->next = l2; l2 = l2->next; }
        else { tail->next = l1; l1 = l1->next; }
        tail = tail->next;
    }
    while(l1) { tail->next = l1; l1 = l1->next; tail = tail->next; }
    while(l2) { tail->next = l2; l2 = l2->next; tail = tail->next; }
    return head;
}

ListNode* mergeSortList(ListNode* head) {
    if (!head || !head->next) return head;

    ListNode *fast = head, *slow = head;
    //查找中间节点,要保证链表的长度大于2,
    //若是等于2,则无需寻找中点,即下面赋值操作后
    //l = head和r = head->next是正确的,
    //否则fast = null,slow->next = nullptr
    //则下面语句中的slow->next出错
    while (fast->next && fast->next->next) { 
        fast = fast->next->next;
        slow = slow->next;
    }

    ListNode* l = head, * r = slow->next; 
    //中间节点后的第一个节点作为需合并链表头节点

    slow->next = nullptr; //中间节点的next指向空,以适应merge操作
    l = mergeSortList(l);
    r = mergeSortList(r);
    return mergeList(l, r);
}

int main()
{
    ListNode* head = new ListNode(-1);
    insertNode(head, new ListNode(10));
    insertNode(head, new ListNode(4));
    insertNode(head, new ListNode(11));
    insertNode(head, new ListNode(6)); 
    insertNode(head, new ListNode(3)); 
    insertNode(head, new ListNode(5)); 
    insertNode(head, new ListNode(1));
    head = mergeSortList(head->next);
    ListNode* node = head;
    while (node) {
        cout << node->val << ' ';
        node = node->next;
    }
    cout << endl;
    return  0;
}

以上是关于链表的快速排序和归并排序的主要内容,如果未能解决你的问题,请参考以下文章

算法链表的快速排序和归并排序

链表的排序(归并排序+快慢指针)

精益求精单链表归并排序与快速排序

链表排序-归并排序和快速排序

148 Sort List 链表上的归并排序和快速排序

链表归并排序插入排序