連結列表合併排序

  1. 連結串列合併排序演算法
  2. 連結串列合併排序圖
  3. 連結串列合併排序實現
  4. 連結串列合併排序演算法的複雜度
連結列表合併排序

在本教程中,我們將學習如何使用合併排序演算法對連結列表進行排序。它是對連結串列進行排序的最優選演算法之一,因為緩慢的指標隨機訪問使其他演算法的效能變差(例如:quicksort 和 heapsort)。

連結串列合併排序演算法

head 成為要排序的連結串列的第一個節點。

mergeSort(head)

getMid(head)

我們使用兩個指標,一個為 slow,另一個為 fastfast 指標以 slow 的兩倍速度覆蓋連結列表,當 fast 節點落在連結列表的末端時,slow 指標落在 mid 節點。我們還使用一個 prev 節點來處理 MergeSort() 函式中的連結列表的拆分。

merge(F,B)

F 是連結列表的第一部分的開頭,而 B 是連結列表的第二部分的開頭。我們合併兩個排序的子列表 F 和 B,以獲得最終的排序連結串列。

連結串列合併排序圖

連結串列合併排序實現

C
++ cCopy#include <bits/stdc++.h>
using namespace std;

class Node {
 public:
  int data;
  Node* next;
  Node(int x) {
    this->data = x;
    this->next = NULL;
  }
};

pair<Node*, Node*> getMid(Node* head) {
  Node* mid = head;
  Node* fast = head;
  Node* prev = NULL;

  while (fast != NULL && fast->next != NULL) {
    prev = mid;
    mid = mid->next;
    fast = fast->next->next;
  }

  pair<Node*, Node*> result(prev, mid);
  return result;
}

Node* merge(Node* F, Node* B) {
  Node* first = F;
  Node* second = B;
  Node* merged = NULL;
  Node* tail = NULL;

  while ((first != NULL) && (second != NULL)) {
    Node* insertedNode = NULL;

    if (first->data < second->data) {
      insertedNode = first;
      first = first->next;
    } else {
      insertedNode = second;
      second = second->next;
    }

    if (merged) {
      tail->next = insertedNode;
      tail = insertedNode;
    } else {
      merged = tail = insertedNode;
    }
  }
  while (first != NULL) {
    tail->next = first;
    tail = first;
    first = first->next;
  }

  while (second != NULL) {
    tail->next = second;
    tail = second;
    second = second->next;
  }
  if (tail) {
    tail->next = NULL;
  }

  return merged;
}

void mergeSort(Node*& head) {
  if ((head == NULL) || (head->next == NULL)) {
    return;
  }
  pair<Node*, Node*> a = getMid(head);
  Node* prev = a.first;
  Node* mid = a.second;

  if (prev) {
    prev->next = NULL;
  }

  mergeSort(head);
  mergeSort(mid);
  head = merge(head, mid);
}

void printList(Node* head) {
  Node* curr = head;
  while (curr != NULL) {
    cout << curr->data << " ";
    curr = curr->next;
  }
  cout << "\n";
}

int main() {
  Node* head = new Node(5);
  head->next = new Node(6);
  head->next->next = new Node(4);
  head->next->next->next = new Node(3);
  head->next->next->next->next = new Node(2);
  head->next->next->next->next->next = new Node(1);
  printList(head);
  mergeSort(head);
  printList(head);
  return 0;
}

連結串列合併排序演算法的複雜度

時間複雜度

  • 平均情況

合併排序是一種遞迴演算法。下面的遞迴關係給出了 Merge 排序的時間複雜度表示式。

Bash
 bashCopyT(n) = 2T(n/2) + θ(n)

該遞迴關係的結果為 T(n) = nLogn。我們還可以將其視為大小為 n 的連結列表,該列表被劃分為最多 Logn 部分。排序每個部分併合並每個部分需要 O(n) 時間。

因此,時間複雜度約為[大 Theta]:O(nlogn)

  • 最壞情況

最壞情況下的時間複雜度是 [Big O]:O(nlogn)。它與平均情況下的時間複雜度相同。

  • 最佳情況

最佳情況下的時間複雜度是 [Big Omega]:O(nlogn)。它與最壞情況下的時間複雜度相同。但是,如果已對連結串列進行排序,則可以將時間複雜度降低為 O(n)

空間複雜度

由於堆疊中遞迴呼叫佔用的空間,因此連結串列上的合併排序演算法的空間複雜度為 O(logn)。如果我們忽略遞迴呼叫佔用的空間,則空間複雜度可以視為 O(1)

Enjoying our tutorials? Subscribe to DelftStack on YouTube to support us in creating more high-quality video guides. Subscribe
作者: Harshit Jindal
Harshit Jindal avatar Harshit Jindal avatar

Harshit Jindal has done his Bachelors in Computer Science Engineering(2021) from DTU. He has always been a problem solver and now turned that into his profession. Currently working at M365 Cloud Security team(Torus) on Cloud Security Services and Datacenter Buildout Automation.

LinkedIn

相關文章 - Data Structure

相關文章 - Linked List