详解C++实现快速排序的原理、时间复杂度及优化方法

快速排序是一种高效的排序算法,它的时间复杂度为Θ(n log n)。快排的核心思想是分治法,通过将一个大问题分解成多个小问题来实现排序。下面我们来介绍如何使用 C++ 实现快速排序。

一、快速排序的原理

快速排序的基本思想是:从待排序的序列中选出一个基准元素(通常选择第一个元素),然后将序列划分成两个子序列,一个子序列所有元素小于或等于基准元素,另一个子序列所有元素大于基准元素,最后递归地对两个子序列进行排序。当子序列中只有一个元素时,排序完成。

二、C++ 实现快速排序

下面是 C++ 实现快速排序的代码:

void quickSort(int arr[], int left, int right) {
    int i = left, j = right;
    int pivot = arr[(left + right) / 2];
    
    while (i <= j) {
        while (arr[i] < pivot)
            i++;
        while (arr[j] > pivot)
            j--;
        if (i <= j) {
            swap(arr[i], arr[j]);
            i++;
            j--;
        }
    }
    
    if (left < j)
        quickSort(arr, left, j);
    if (i < right)
        quickSort(arr, i, right);
}

三、快速排序的工作原理

快速排序的核心是 partition 函数,它将序列划分成两个子序列。在上面的实现中,我们使用左右指针 i 和 j 分别指向序列的左右两端,然后从两端开始扫描序列,如果左指针指向的元素小于基准元素,则向右移动左指针;如果右指针指向的元素大于基准元素,则向左移动右指针;如果左指针指向的元素大于等于基准元素且右指针指向的元素小于等于基准元素,则交换这两个元素并同时向中间移动。

上面的代码中,我们使用 arr[(left + right) / 2] 作为基准元素,也可以选择其他元素作为基准元素,比如第一个、最后一个或中间的元素。

四、快速排序的时间复杂度

在最坏情况下,快速排序的时间复杂度是 Θ(n^2),即当序列已经有序或接近有序时,分治法会失效,此时需要进行 n-1 次比较,排序的时间复杂度退化为 O(n^2)。在平均情况下,快速排序的时间复杂度是 Θ(n log n)。在最好情况下,即每次 pivot 都恰好为中位数的情况下,时间复杂度是 O(n log n)。

五、快速排序的优化

为了提高快速排序的性能,我们可以通过以下方式对其进行优化:

  • 随机选择基准元素:可以避免最坏情况的出现;
  • 三数取中法:在 left、mid 和 right 三个位置上取一个数来作为基准元素,可以减少最坏情况的出现;
  • 插入排序:当序列长度小于一定阈值时,可以使用插入排序代替快速排序;
  • 尾递归消除:可以优化递归过程,避免栈溢出的风险。

对快速排序进行优化可以提高其性能,下面是一个经过优化的快速排序实现:

// 三数取中法选择基准元素
int median3(int arr[], int left, int right) {
    int center = (left + right) / 2;
    if (arr[left] > arr[center])
        swap(arr[left], arr[center]);
    if (arr[left] > arr[right])
        swap(arr[left], arr[right]);
    if (arr[center] > arr[right])
        swap(arr[center], arr[right]);
    swap(arr[center], arr[right - 1]); // 将基准元素放到倒数第二个位置
    return arr[right - 1];
}

// 插入排序
void insertionSort(int arr[], int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int temp = arr[i];
        int j = i - 1;
        while (j >= left && arr[j] > temp) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

// 递归快速排序
void quickSort(int arr[], int left, int right) {
    const int THRESHOLD = 10; // 阈值
    if (right - left >= THRESHOLD) {
        int pivot = median3(arr, left, right); // 选择基准元素
        int i = left, j = right - 1;
        while (true) {
            while (arr[++i] < pivot) {}
            while (arr[--j] > pivot) {}
            if (i < j)
                swap(arr[i], arr[j]);
            else
                break;
        }
        swap(arr[i], arr[right - 1]); // 将基准元素放到正确的位置
        quickSort(arr, left, i - 1); // 递归排序左子序列
        quickSort(arr, i + 1, right); // 递归排序右子序列
    } else {
        insertionSort(arr, left, right); // 使用插入排序对小序列排序
    }
}

上述代码中,我们使用了三数取中法来选择基准元素,在选定了基准元素之后,我们将其放到倒数第二个位置,这是为了方便处理右边界的情况。在递归排序左右子序列之前,我们先判断序列长度是否小于一个阈值(这里设置为 10),如果小于等于阈值,则使用插入排序代替快速排序。通过上述优化,快速排序的性能得到了提升。

六、总结

快速排序是一种高效的排序算法,其时间复杂度为 Θ(n log n)。在实现快速排序时,需要注意选择合适的基准元素,并对其进行随机化或者三数取中来避免最坏情况的出现。同时可以使用插入排序等优化方法来提高排序性能。

猜你喜欢

转载自blog.csdn.net/m0_62338174/article/details/130458786
今日推荐