首页 > 编程语言 >链表排序算法(C++):数组辅助排序、插入排序、归并排序

链表排序算法(C++):数组辅助排序、插入排序、归并排序

时间:2024-10-12 18:49:41浏览次数:3  
标签:head NULL listNode nums 插入排序 next 链表 排序

文章目录

数组排序算法参考:冒泡排序、插入排序、选择排序、归并排序、快速排序算法(C++实现)-CSDN博客
链表排序算法参考:链表排序总结(全)(C++)-CSDN博客

这里主要介绍三种链表排序方法,第一种是借助数组进行排序的方法,第二种是插入排序,第三种是归并排序,当然链表排序也可以用快排等其他方法,这里就不一一介绍了。

借助数组排序

顾名思义,就是通过数组来实现间接的链表排序,大概思路就是先将链表里每个节点的值存在数组里,对数组进行排序(数组排序方法有很多种,也可以直接调用sort函数来排序),将排序好的数组再依次赋值给链表的每个节点。
这个排序方法算是一种不那么正统的排序,时间复杂度和空间复杂度会受数组排序算法的影响。如果基于快排,那时间复杂度就是O(nlogn),空间复杂度最好为O(logn),最差为O(n)

/**
 * 数组快排
 */
void quickSort(vector<int> &nums, int begin, int end)
{
    if (begin >= end)
    {
        return;
    }
    int left = begin;
    int right = end;
    int key = begin;

    while (begin < end)
    {
        // 选取了左key,一定要右边先移动
        while (begin < end && nums[end] >= nums[key])
        {
            end--;
        }
        while (begin < end && nums[begin] <= nums[key])
        {
            begin++;
        }
        swap(nums[begin], nums[end]);
    }
    swap(nums[begin], nums[key]);
    key = begin;
    quickSort(nums, left, key - 1);
    quickSort(nums, key + 1, right);
}

/**
 * 借助数组排序(基于快排)
 * 时间复杂度O(nlogn)
 * 空间复杂度跟快排一样:最好为O(logn),最差为O(n)
 */
void trickSort(listNode *head)
{
    if (head == NULL)
    {
        cout << "link list is empty" << endl;
        return;
    }
    vector<int> nums;
    listNode *p = head;
    while (p != NULL)
    {
        nums.push_back(p->val);
        p = p->next;
    }
    quickSort(nums, 0, nums.size() - 1);
    p = head;
    for (auto num : nums)
    {
        p->val = num;
        p = p->next;
    }
}

插入排序

链表的插入排序和数组的插入排序本质上是一样的,但是实现方法上有些不同
时间复杂度:O(n^2);空间复杂度:O(1)

/**
 * 链表插入排序
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1)
 */
void linkInsertSort(listNode **head)
{
    if (*head == NULL || (*head)->next == NULL)
    {
        return;
    }
    // 创建一个哑节点(辅助节点)
    listNode *dummy = new listNode(-1);
    dummy->next = *head;
    listNode *pre = *head;
    listNode *cur = (*head)->next;
    listNode *temp = dummy;
    while (cur != NULL)
    {
        // 如果当前值比上一次插入点的值要小,就只能从头查找插入点,否则,插入点的查找可以从上一个插入点之后开始查找
        if (temp->val > cur->val)
        {
            temp = dummy;
        }

        if (cur->val >= pre->val)
        {
            pre = pre->next;
            cur = cur->next;
        }
        else
        {
            while (temp->next->val < cur->val)
            {
                temp = temp->next;
            }
            pre->next = cur->next;
            cur->next = temp->next;
            temp->next = cur;
            cur = pre->next;
        }
    }
    *head = dummy->next;
}

归并排序

归并排序似乎是链表排序中最常用到的一种排序方法,时间复杂度:O(nlogn),空间复杂度:O(logn),由递归决定?也有文章说空间复杂度是O(1)

/**
 * 归并排序
 * 时间复杂度:O(nlogn)
 * 空间复杂度:O(logn),由递归决定?也有文章说空间复杂度是O(1)
 */
class MergeSort
{
public:
    listNode *merge(listNode *left, listNode *right)
    {
        auto dummy = new listNode(-1);
        auto h = dummy;
        while (left != NULL && right != NULL)
        {
            if (left->val < right->val)
            {
                h->next = left;
                left = left->next;
            }
            else
            {
                h->next = right;
                right = right->next;
            }
            h = h->next;
        }
        h->next = left == NULL ? right : left;
        return dummy->next;
    }

    listNode *mergeSort(listNode *head)
    {
        // 这个是终止条件,一定要有!
        if (head->next == NULL)
            return head;
        listNode *slow = head, *fast = head->next;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        auto r_head = slow->next;
        slow->next = NULL;
        auto left = mergeSort(head);
        auto right = mergeSort(r_head);
        return merge(left, right);
    }

    listNode *sortList(listNode *head)
    {
        if (head == NULL || head->next == NULL)
        {
            return head;
        }
        return mergeSort(head);
    }
};

测试用例

#include <iostream>
#include <cstdlib>
#include <random>
#include <algorithm>
#include <vector>

using namespace std;

struct listNode
{
    int val;
    listNode *next;

    // 构造函数
    listNode(int x) : val(x), next(nullptr) {}
};

/**
 * 生成随机数
 */
int randNum(void)
{
    random_device rd;
    int gen(rd());
    return gen % 100;
}

/**
 * 创建单链表
 */
void linkCreate(listNode **head, listNode *p_new)
{
    if (*head == NULL)
    {
        *head = p_new;
        return;
    }
    listNode *p = *head;
    while (p->next != NULL)
    {
        p = p->next;
    }
    p->next = p_new;
    p_new->next = NULL;
}

/**
 * 遍历链表
 */
void linkPrint(listNode *head)
{
    if (head == NULL)
    {
        cout << "link list is empty" << endl;
        return;
    }
    listNode *p = head;
    while (p != NULL)
    {
        cout << p->val << " -> ";
        p = p->next;
    }
    cout << "NULL" << endl;
}

/**
 * 数组快排
 */
void quickSort(vector<int> &nums, int begin, int end)
{
    if (begin >= end)
    {
        return;
    }
    int left = begin;
    int right = end;
    int key = begin;

    while (begin < end)
    {
        // 选取了左key,一定要右边先移动
        while (begin < end && nums[end] >= nums[key])
        {
            end--;
        }
        while (begin < end && nums[begin] <= nums[key])
        {
            begin++;
        }
        swap(nums[begin], nums[end]);
    }
    swap(nums[begin], nums[key]);
    key = begin;
    quickSort(nums, left, key - 1);
    quickSort(nums, key + 1, right);
}

/**
 * 借助数组排序(基于快排)
 * 时间复杂度O(nlogn)
 * 空间复杂度跟快排一样:最好为O(logn),最差为O(n)
 */
void trickSort(listNode *head)
{
    if (head == NULL)
    {
        cout << "link list is empty" << endl;
        return;
    }
    vector<int> nums;
    listNode *p = head;
    while (p != NULL)
    {
        nums.push_back(p->val);
        p = p->next;
    }
    quickSort(nums, 0, nums.size() - 1);
    p = head;
    for (auto num : nums)
    {
        p->val = num;
        p = p->next;
    }
}

/**
 * 链表插入排序
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1)
 */
void linkInsertSort(listNode **head)
{
    if (*head == NULL || (*head)->next == NULL)
    {
        return;
    }
    // 创建一个哑节点(辅助节点)
    listNode *dummy = new listNode(-1);
    dummy->next = *head;
    listNode *pre = *head;
    listNode *cur = (*head)->next;
    listNode *temp = dummy;
    while (cur != NULL)
    {
        // 如果当前值比上一次插入点的值要小,就只能从头查找插入点,否则,插入点的查找可以从上一个插入点之后开始查找
        if (temp->val > cur->val)
        {
            temp = dummy;
        }

        if (cur->val >= pre->val)
        {
            pre = pre->next;
            cur = cur->next;
        }
        else
        {
            while (temp->next->val < cur->val)
            {
                temp = temp->next;
            }
            pre->next = cur->next;
            cur->next = temp->next;
            temp->next = cur;
            cur = pre->next;
        }
    }
    *head = dummy->next;
}

/**
 * 归并排序
 * 时间复杂度:O(nlogn)
 * 空间复杂度:O(logn),由递归决定?也有文章说空间复杂度是O(1)
 */
class MergeSort
{
public:
    listNode *merge(listNode *left, listNode *right)
    {
        auto dummy = new listNode(-1);
        auto h = dummy;
        while (left != NULL && right != NULL)
        {
            if (left->val < right->val)
            {
                h->next = left;
                left = left->next;
            }
            else
            {
                h->next = right;
                right = right->next;
            }
            h = h->next;
        }
        h->next = left == NULL ? right : left;
        return dummy->next;
    }

    listNode *mergeSort(listNode *head)
    {
        // 这个是终止条件,一定要有!
        if (head->next == NULL)
            return head;
        listNode *slow = head, *fast = head->next;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        auto r_head = slow->next;
        slow->next = NULL;
        auto left = mergeSort(head);
        auto right = mergeSort(r_head);
        return merge(left, right);
    }

    listNode *sortList(listNode *head)
    {
        if (head == NULL || head->next == NULL)
        {
            return head;
        }
        return mergeSort(head);
    }
};

int main(int argc, char const *argv[])
{
    listNode *head = NULL, *p_new = NULL;
    MergeSort mergeTool;
    // 创建链表
    for (int i = 0; i < 10; i++)
    {
        p_new = (listNode *)malloc(sizeof(listNode));
        p_new->val = randNum();
        linkCreate(&head, p_new);
    }
    // 遍历链表
    linkPrint(head);
    head = mergeTool.sortList(head);
    linkPrint(head);

    return 0;
}

标签:head,NULL,listNode,nums,插入排序,next,链表,排序
From: https://blog.csdn.net/S13352784013/article/details/142862049

相关文章

  • 冒泡排序、插入排序、选择排序、归并排序、快速排序算法(C++实现)
    文章目录一、冒泡排序上浮法冒泡排序(从小到大排序)下浮法冒泡排序(从大到小排序)二、选择排序三、插入排序四、归并排序五、快速排序参考一、冒泡排序冒泡排序应该算是最经典最简单的排序算法,我一开始学习排序算法就是从冒泡排序开始入门的。冒泡排序算法的基本思路:(......
  • c语言链表-学生管理系统
    include<stdio.h>include<stdlib.h>include<string.h>//定义结构体structSTU{charnum[8];//学号charname[5];//姓名intscore;//成绩};//定义链表structtemp{structSTU*s;structtemp*next;};voidadd(structtemp**head);//......
  • 【反转链表】【K个一组翻转链表】两个问题具体思路,文中含多种解法(附完整代码)
    文章目录前言一、如何理解反转链表?二、反转链表1.方法一(递归)方法二(迭代)三、K个一组翻转链表前言本文将围绕【反转链表】问题展开详细论述。采用【递归法】【迭代法】同时,还将进一步升级该问题,讨论【K个一组翻转链表】一、如何理解反转链表?题目链接:[点击......
  • 链表-数据结构
    链表的连接简单题目描述:创建两个链表:S1,S2;让s1和s2实现合并连接;连接要求:输入s1节点的数值下标和输入s2的数值下标,如果数值相同实现连接;比如:cin>>s1(2),cin>>s2(0);就让s1的下标2:数值1和s2的下标0:数值1比较相同--------321123s1数值1在跟s......
  • 数据结构与算法 - 单链表 & 双链表 -- 概念+实现
    文章目录前言一、顺序表的缺陷二、链表是如何设计的?三、链表的分类四、链表的概念及其结构1、链表的概念:2、链表的结构五、不带头单向不循环链表的实现(一)、SList.h的实现(二)、SList.c的实现1、初始化2、创建结点3、头插4、尾插4、头删5、尾删6、指定p......
  • 七大排序详解
    大家好呀,在今天我们学习目前阶段中,我们最常用的七种排序——插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,强烈建议大家配合代码和图片一起食用一,排序简介二,插入排序排序思想直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小......
  • c++知识点多关键字排序
    在C++中,可以使用std::sort函数结合自定义的比较函数对多关键字进行排序。以下是一个示例代码,演示如何根据两个关键字对结构体进行排序:#include<iostream>#include<vector>#include<algorithm>structItem{intfirstKey;intsecondKey;std::stringname;};//自定......
  • java算法OJ(2)链表
    目录1.前言2.正文2.1合并俩个有序链表2.1.1题目描述2.1.2示例2.1.3代码2.2俩数相加2.2.1题目描述2.2.2示例2.2.3代码2.3分割链表2.3.1题目描述2.3.2示例2.3.3代码3.小结1.前言哈喽大家好吖,今天来对先前学习的链表进行巩固,做几道算法题,如果大家有更加优良的......
  • C代码随笔——冒泡排序
    题目:对一串乱序数字排序并且进行重复元素去重冒泡排序的基本规则:        比较相邻的元素。如果第一个比第二个大(升序排序),就交换它们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。重复以上的步骤,除了最后已......
  • 【数据结构】深度解析堆排序
    目录......