MST

星途 面试题库

面试题:C++ 并行化归并排序

使用C++ 的多线程库(如std::thread 或OpenMP)对归并排序进行并行化实现。要求考虑任务划分、线程同步等问题,以提高排序的效率。同时分析并行化后的时间复杂度和可能存在的性能瓶颈。
21.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

使用 std::thread 实现并行归并排序

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>

std::mutex mtx;

// 合并两个有序数组
void merge(std::vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    std::vector<int> L(n1), R(n2);

    for (int i = 0; i < n1; i++)
        L[i] = arr[left + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[mid + 1 + j];

    int i = 0, j = 0, k = left;

    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

// 并行归并排序
void parallelMergeSort(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;

        std::thread leftThread(parallelMergeSort, std::ref(arr), left, mid);
        std::thread rightThread(parallelMergeSort, std::ref(arr), mid + 1, right);

        leftThread.join();
        rightThread.join();

        merge(arr, left, mid, right);
    }
}

使用 OpenMP 实现并行归并排序

#include <iostream>
#include <vector>
#include <omp.h>

// 合并两个有序数组
void merge(std::vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    std::vector<int> L(n1), R(n2);

    for (int i = 0; i < n1; i++)
        L[i] = arr[left + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[mid + 1 + j];

    int i = 0, j = 0, k = left;

    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

// 并行归并排序
void parallelMergeSortOpenMP(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;

#pragma omp parallel sections
        {
#pragma omp section
            {
                parallelMergeSortOpenMP(arr, left, mid);
            }
#pragma omp section
            {
                parallelMergeSortOpenMP(arr, mid + 1, right);
            }
        }

        merge(arr, left, mid, right);
    }
}

时间复杂度分析

  • 顺序归并排序:时间复杂度为 (O(n \log n)),其中 (n) 是数组的长度。每次递归将数组分成两部分,共需要 (\log n) 层递归,每层递归的合并操作时间复杂度为 (O(n))。
  • 并行归并排序:理想情况下,并行化后的时间复杂度仍然是 (O(n \log n)),但由于线程创建、同步等开销,实际运行时间可能会在接近 (O(n \log n)) 的范围内波动。并行化主要是利用多核处理器并行处理子问题,理论上可以将时间复杂度中的线性部分(合并操作)并行化,从而减少总运行时间。

性能瓶颈分析

  • 线程创建和销毁开销:频繁创建和销毁线程(如 std::thread 实现中)会带来额外的开销,这可能会抵消并行化带来的性能提升。可以通过线程池技术来缓解这一问题。
  • 线程同步开销:在合并操作时,虽然数据划分相对简单,但线程同步(如 std::mutex 或者 OpenMP 的隐式同步)会带来一定的开销。如果同步操作过于频繁或者粒度太粗,会影响并行效率。
  • 负载均衡:如果任务划分不均匀,可能导致部分线程早早完成任务,而其他线程仍在忙碌,无法充分利用多核处理器的性能。在上述实现中,任务划分是均匀的,但在更复杂的场景下需要注意这一问题。