数据结构单链表之链表的归并排序 | 第十一套

简介: 数据结构单链表之链表的归并排序 | 第十一套

合并排序通常用于对链表进行排序。链表缓慢的随机访问性能使得其他一些算法(如快速排序)表现不佳,而其他算法(如堆排序)则完全不可能。

令 head 为链表的第一个要排序的节点,headRef 为 head 的指针。请注意,我们需要在 MergeSort() 中引用 head,因为下面的实现更改了下一个链接以对链表进行排序(不是节点上的数据),因此如果原始头部的数据不是链表中的最小值。

合并排序(headRef)
1)如果head为NULL或链表中只有一个元素 
    然后返回。
2)否则将链表分成两半。  
      FrontBackSplit(head, &a, &b); /* a 和 b 是两半 */
3) 将 a 和 b 的两半排序。
      归并排序(一);
      归并排序(b);
4) 合并已排序的 a 和 b(使用此处讨论的 SortedMerge() )
   并使用 headRef 更新头指针。
     *headRef = SortedMerge(a, b);
#include <bits/stdc++.h>
using namespace std;
class Node {
public:
  int data;
  Node* next;
};
Node* SortedMerge(Node* a, Node* b);
void FrontBackSplit(Node* source,
          Node** frontRef, Node** backRef);
void MergeSort(Node** headRef)
{
  Node* head = *headRef;
  Node* a;
  Node* b;
  if ((head == NULL) || (head->next == NULL)) {
    return;
  }
  FrontBackSplit(head, &a, &b);
  MergeSort(&a);
  MergeSort(&b);
  *headRef = SortedMerge(a, b);
}
Node* SortedMerge(Node* a, Node* b)
{
  Node* result = NULL;
  if (a == NULL)
    return (b);
  else if (b == NULL)
    return (a);
  if (a->data <= b->data) {
    result = a;
    result->next = SortedMerge(a->next, b);
  }
  else {
    result = b;
    result->next = SortedMerge(a, b->next);
  }
  return (result);
}
void FrontBackSplit(Node* source,
          Node** frontRef, Node** backRef)
{
  Node* fast;
  Node* slow;
  slow = source;
  fast = source->next;
  while (fast != NULL) {
    fast = fast->next;
    if (fast != NULL) {
      slow = slow->next;
      fast = fast->next;
    }
  }
  *frontRef = source;
  *backRef = slow->next;
  slow->next = NULL;
}
void printList(Node* node)
{
  while (node != NULL) {
    cout << node->data << " ";
    node = node->next;
  }
}
void push(Node** head_ref, int new_data)
{
  Node* new_node = new Node();
  new_node->data = new_data;
  new_node->next = (*head_ref);
  (*head_ref) = new_node;
}
int main()
{
  Node* res = NULL;
  Node* a = NULL;
  push(&a, 15);
  push(&a, 10);
  push(&a, 5);
  push(&a, 20);
  push(&a, 3);
  push(&a, 2);
  MergeSort(&a);
  cout << "Sorted Linked List is: \n";
  printList(a);
  return 0;
}

输出:

Sorted Linked List is: 
2 3 5 10 15 20

时间复杂度:  O(n*log n)\

空间复杂度:  O(n*log n)

方法 2: 这种方法更简单,使用 log n 空间。

合并排序():

  1. 如果链表的大小为 1,则返回头部
  2. 使用乌龟和兔子的方法找到中间
  3. 将 mid 的 next 存储在 head2 中,即右子链表。
  4. 现在使下一个中点为空。
  5. 对左右子链表递归调用mergeSort(),存储左右子链表的新头。
  6. 给定左右子链表的新头的参数调用 merge() 并存储合并后返回的最终头。
  7. 返回合并链表的最终头。

合并(头1,头2):

  1. 取一个指针 say merge 将合并的列表存储在其中并在其中存储一个虚拟节点。
  2. 取一个指针 temp 并为其分配合并。
  3. 如果 head1 的数据小于 head2 的数据,则将 head1 存储在 temp 的 next 并将 head1 移动到 head1 的 next。
  4. 否则将 head2 存储在 temp 的下一个并将 head2 移动到 head2 的下一个。
  5. 将 temp 移动到 temp 的下一个。
  6. 重复步骤 3、4 和 5,直到 head1 不等于 null 且 head2 不等于 null。
  7. 现在将第一个或第二个链表的任何剩余节点添加到合并的链表中。
  8. 返回合并的下一个(这将忽略虚拟并返回最终合并链表的头部)
#include<iostream>
using namespace std;
struct Node{
  int data;
  Node *next;
};
void insert(int x,Node **head) {
  if(*head == NULL){
    *head = new Node;
    (*head)->data = x;
    (*head)->next = NULL;
    return;
  }
  Node *temp = new Node;
  temp->data = (*head)->data;
  temp->next = (*head)->next;
  (*head)->data=x;
  (*head)->next=temp;
}
void print(Node *head) {
  Node *temp=head;
  while(temp!=NULL) {
    cout<<temp->data<<" ";
    temp = temp->next;
  }
}
Node *merge(Node *firstNode,Node *secondNode) {
  Node *merged = new Node;
  Node *temp = new Node;
  merged = temp;
  while(firstNode != NULL && secondNode != NULL) {
    if(firstNode->data<=secondNode->data) {
      temp->next = firstNode;
      firstNode = firstNode->next;
    }
    else {
      temp->next = secondNode;
      secondNode = secondNode->next;
    }
    temp = temp->next;
  }
  while(firstNode!=NULL) {
    temp->next = firstNode;
    firstNode = firstNode->next;
    temp = temp->next;
  }
  while(secondNode!=NULL) {
    temp->next = secondNode;
    secondNode = secondNode->next;
    temp = temp->next;
  }
  return merged->next;
}
Node *middle(Node *head) {
  Node *slow = head;
  Node *fast = head->next;
  while(slow->next != NULL && (fast!=NULL && fast->next!=NULL)) {
    slow = slow->next;
    fast = fast->next->next;
  }
  return slow;
}
Node *sort(Node *head){
  if(head->next == NULL) {
    return head;
  }
  Node *mid = new Node;
  Node *head2 = new Node;
  mid = middle(head);
  head2 = mid->next;
  mid->next = NULL;
  Node *finalhead = merge(sort(head),sort(head2));
  return finalhead;
}
int main(void) {
  Node *head = NULL;
  int n[]={7,10,5,20,3,2};
  for(int i=0;i<6;i++) {
    insert(n[i],&head); 
  }
cout<<"\nSorted Linked List is: \n";
  print(sort(head));  
  return 0;
}

输出:

Sorted Linked List is: 
2 3 5 7 10 20 

时间复杂度:O(n*log n)

空间复杂度:  O(log n)

目录
相关文章
|
20天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
48 4
|
21天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
21天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
存储
[数据结构] -- 单链表
[数据结构] -- 单链表
26 1
|
20天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
39 0
|
1月前
|
存储
[数据结构] -- 双向循环链表
[数据结构] -- 双向循环链表
23 0
|
1月前
|
存储
数据结构(单链表)
数据结构(单链表)
14 0
|
1月前
|
存储
探索数据结构:便捷的双向链表
探索数据结构:便捷的双向链表
|
18天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
94 9
|
9天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
19 1

热门文章

最新文章

下一篇
无影云桌面