串行&并行快速排序
(一)串行快速排序
程序中 j = right - 1; 语句说明:此句直接从倒数第二个开始,同时由于--j会判断倒数第三个。这是因为在使用三数中值分割后:最后一个一定大于枢纽元,经过交换,倒数第二个就是枢纽元。
#include <iostream> #define CUTOFF 10 // 定义快速排序的数组规模下界 // 交换两个整数 void swap(int *fir, int *sec) { int temp = 0; temp = *fir; *fir = *sec; *sec = temp; } // 插入排序算法 void InsertionSort(int A[], int n) { int j, p; int tmp; for (p = 1; p < n; p++) { tmp = A[p]; for (j = p; j > 0 && A[j - 1] > tmp; j--) A[j] = A[j - 1]; A[j] = tmp; } } // 三数中值分割 int Median3(int A[], int left, int right) { // 计算中间位置 int center = (left + right) / 2; if (A[left] > A[center]) swap(&A[left], &A[center]); if (A[left] > A[right]) swap(&A[left], &A[right]); if (A[center] > A[right]) swap(&A[center], &A[right]); /* 不变等式: A[left] <= A[center] <= A[right] */ swap(&A[center], &A[right - 1]); // 隐藏枢纽元 return A[right - 1]; } // 快速排序算法 void Qsort(int A[], int left, int right) { int i, j; // 枢纽元 int pivot; if (left + CUTOFF <= right) { pivot = Median3(A, left, right); // 初始化i,j的位置 i = left; j = right - 1; for (;;) { while (A[++i] < pivot) { } while (A[--j] > pivot) { } if (i < j) swap(&A[i], &A[j]); else break; } // 将枢纽元与i所指向的元素交换 swap(&A[i], &A[right - 1]); // 以i为分界线,分别对左右两端递归的进行快速排序 Qsort(A, left, i - 1); Qsort(A, i + 1, right); } else { // 在该数组上做一次插入排序 InsertionSort(A + left, right - left + 1); } } // 打印数组 void printArray(int A[], int len) { int length = len; int index; for (index = 0; index <= len - 1; index++) { printf("%d ", A[index]); } } int main() { // 函数声明 void swap(int *fir, int *sec); void InsertionSort(int A[], int n); int Median3(int A[], int left, int right); void Qsort(int A[], int left, int right); void printArray(int A[], int len); int A[15] = {2, 5, 1, 7, 6, 19, 30, 9, 4, 8, 21, 11, 18, 22, 47}; printf("快速排序之前: \n"); printArray(A, 15); Qsort(A, 0, 14); printf("\n"); printf("快速排序之后: \n"); printArray(A, 15); system("pause"); return 0; }
(二)并行快速排序
并行化可以优化许多算法,前提是并行化的任务之间不会产生冲突。
快速排序的核心逻辑为分治,通过一次遍历,使得区间分成了两个子区间,其中一个子区间的元素恒小于等于另一个区间的元素,再用同样的方法分别处理这两个子区间。可以发现,这两个子区间不重叠,并且后续的操作都是在一个区间内继续完成,不会跨区间,因此满足了并行化的条件。
使用OpenMP(Linux&Windows环境)
#include <stdio.h> #include <omp.h> #include <time.h> #include <fstream> #include <stdlib.h> #define CUTOFF 10 // 定义快速排序的数组规模下界 // 交换两个整数 void swap(int *fir, int *sec) { int temp = 0; temp = *fir; *fir = *sec; *sec = temp; } // 三数中值分割 int Median3(int A[], int left, int right) { // 计算中间位置 int center = (left + right) / 2; if (A[left] > A[center]) { swap(&A[left], &A[center]); } if (A[left] > A[right]) { swap(&A[left], &A[right]); } if (A[center] > A[right]) { swap(&A[center], &A[right]); } /* 不变等式: A[left] <= A[center] <= A[right] */ swap(&A[center], &A[right - 1]); // 隐藏枢纽元 return A[right - 1]; } // 插入排序算法 void InsertionSort(int A[], int n) { int j, p; int tmp; for (p = 1; p < n; p++) { tmp = A[p]; for (j = p; j > 0 && A[j - 1] > tmp; j--) { A[j] = A[j - 1]; } A[j] = tmp; } } void Qsort(int A[], int left, int right) { int i, j; // 枢纽元 int pivot; if (left + CUTOFF <= right) { pivot = Median3(A, left, right); // 初始化i,j的位置 i = left; j = right - 1; for (;;) { while (A[++i] < pivot) { } while (A[--j] > pivot) { } if (i < j) swap(&A[i], &A[j]); else break; } // 将枢纽元与i所指向的元素交换 swap(&A[i], &A[right - 1]); // 以i为分界线,分别对左右两端递归的进行快速排序 #pragma omp parallel sections { #pragma omp section Qsort(A, left, i - 1); #pragma omp section Qsort(A, i + 1, right); } } else { // 在该数组上做一次插入排序 InsertionSort(A + left, right - left + 1); } } int main() { int n = 4; int size = 1000000; int *A = (int *)malloc(sizeof(int) * size); int *B = (int *)malloc(sizeof(int) * size); srand(time(NULL)); for (int i = 0; i < size; i++) { A[i] = rand() % size; B[i] = A[i]; } /*********以下为并行**************/ double starttime1 = omp_get_wtime(); omp_set_num_threads(n); Qsort(A, 0, size - 1); double endtime1 = omp_get_wtime(); std::ofstream fout1("outA.txt"); for (int i = 0; i < size; i++) { fout1 << A[i] << ' '; } fout1 << std::endl; fout1.close(); printf("\nparallel time : %lf s\n", endtime1 - starttime1); /*********以上为并行*************/ /*********以下为串行*************/ double starttime2 = omp_get_wtime(); omp_set_num_threads(1); Qsort(B, 0, size - 1); double endtime2 = omp_get_wtime(); std::ofstream fout2("outB.txt"); for (int i = 0; i < size; i++) { fout2 << B[i] << ' '; } fout2 << std::endl; fout2.close(); printf("\nserial time : %lf s\n", endtime2 - starttime2); /*********以上为串行*************/ system("pause"); return 0; }
和归并排序相比,为什么快速排序可以采用多线程?{两个算法思路似乎都是分治去处理)
归并排序必须要同一层同时归并。但是快速排序是彻彻底底的分治,对一个区间操作,快速排序可以产生此区间:1.一定是父区间以枢纽元分界结束(并不是父区间的进程结束)。一个进程顺序可能是把 父区间分界结束,左子进程加入任务,左子进程开始,右子进程加入任务,父区间进程结束;2.不会影响同级区间(分治);3.没有分好界之前不会产生子进程。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了