参考文章
https://blog.csdn.net/opooc/article/details/80994353
https://blog.csdn.net/qq_37941471/article/details/80710099
https://www.cnblogs.com/zyb428/p/5673738.html
一、冒泡排序
思想:重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
特性:交换排序; 时间A:N^2 , B:N^2 , E:N; 空间1; 稳定。
void BubbleSort(int data[], int size) {
for (int i = 1; i < size; i++) {
bool sorted = true;
for (int j = 0; j < size - i; j++) {
if (data[j] > data[j + 1]) {
swap(data[j], data[j + 1]); //#include <utility>
sorted = false;
}
}
if (sorted)
break;
}
}
二、快速排序
思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
特性:交换排序,时间A:N logN,W:N logN,B:N logN,空间:logN,不稳定。
int OnceQuickSort(int data[], int left, int right) {
int key = data[right];
//left和right是下一个要填的位置
while (left < right) {
//从左边找大于等于pivot的数
while (left < right && data[left] < key)
left++;
if (left < right)
data[right--] = data[left];
//从右边找小于pivot的数
while (left < right && data[right] >= key)
right--;
if (left < right)
data[left++] = data[right];
}
data[left] = key;
return left;
}
void QuickSort(int data[], int left, int right) {
if (left < right) {
srand(time(NULL)); //#include <ctime>
int ranPos = left + rand() % (right - left + 1); //#include <cstdlib>
swap(data[ranPos], data[right]); //#include <utility>
int pivot = OnceQuickSort(data, left, right);
QuickSort(data, left, pivot - 1);
QuickSort(data, pivot + 1, right);
}
}
三、插入排序
思想:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
特性:插入排序,时间A:N^2,W:N^2,B:N,空间:1,稳定
void InsertSort(int data[], int size) {
for (int i = 1; i < size; i++) {
int key = data[i], prePos;
for (prePos = i - 1; prePos >= 0 && data[j] > key; prePos--) {
data[prePos + 1] = data[prePos];
}
data[prePos + 1] = key;
}
}
四、希尔排序
思想:缩小增量。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序,随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时(用gap = gap/3+1 控制,一般gap不要超过数组大小的一半),保证了最后一次进行直接插入排序,算法终止。(其中直接插入排序是希尔排序gap=1的特例)另外,gap越大,值越大的容易到最后面,但是不太接近有序。
特性:插入排序;时间A:N^1.3 , B:N^2 , E:N;空间1;不稳定。
void ShellSort(int data[], int size) {
int gap = size;
//1. gap > 1 预排序
//2. gap == 1 直接插入排序
//3. gap = gap/3 + 1; 保证最后一次排序是直接插入排序
while (gap > 1) {
gap = gap / 3 + 1;
for (int i = 0; i < size - gap; i++) {
int key = data[i + gap], prePos = i;
while (prePos >= 0 && data[prePos] > key) {
data[prePos + gap] = data[prePos];
prePos = prePos - gap;
}
data[prePos + gap] = key;
}
}
}
五、选择排序
思想:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
特性:选择排序;时间A:N^2 , B:N^2 , E:N^2 ; 空间1;稳定
void SelectSort(int data[], int size) {
for (int i = 0; i < size - 1; i ++) {
int minPos = i;
for (int j = i + 1; j < size; j ++) {
if (data[j] < data[minPos])
minPos = j;
}
swap(data[i], data[minPos]); //#include <utility>
}
}
六、堆排序
思想:堆排序过程将待排序的序列构造成一个堆,选出堆中最大的移走,再把剩余的元素调整成堆,找出最大的再移走,重复直至有序。升序建大堆,降序建小堆。
特性:选择排序;时间A:N logN,B:N logN,E:N logN;空间1;不稳定。
void HeapSort(int data[], int size) {
//从最后一个父节点开始,建大堆
for (int i = size / 2 - 1; i >= 0; --i) {
Heapify(data, i, size);
}
//从堆中的取出最大的元素data[0]放到数组末尾,再调整堆
for (int i = size - 1; i > 0; --i) {
swap(data[0], data[i]);
int temp = data[i];
Heapify(data, 0, i);
}
}
void Heapify(int data[], int root, int n) {
int parent = root;
int child = parent * 2 + 1; //左孩子
while (child < n) {
//找出较大的孩子,再和父亲比
if (child + 1 < n && data[child + 1] > data[child])
child ++;
//左子树和右子树已经是堆
if (data[parent] < data[child]) {
swap(data[parent], data[child]);
parent = child;
child = parent * 2 + 1;
//交换父亲和较大孩子之后,只需要将原本较大孩子的子树调整成堆
}
else
break;
}
}
七、归并排序
思想:分治。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
特性:插入排序;时间A:N logN,B:N logN,E:N logN;空间N;稳定。
void MergeSort(int data[], int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
MergeSort(data, left, mid);
MergeSort(data, mid + 1, right);
Merge(data, left, mid, right);
}
}
void Merge(int data[], int left, int mid, int right) {
int size = right - left + 1;
int* merged = new int[size];
int p1 = left, p2 = mid + 1, p3 = 0;
while (p1 <= mid && p2 <= right) {
merged[p3++] = data[p1] >= data[p2] ? data[p1++] : data[p2++];
}
while (p1 <= mid) {
merged[p3++] = data[p1++];
}
while (p2 <= right) {
merged[p3++] = data[p2++];
}
for (int i = 0; i < size; i++) {
data[left + i] = merged[i];
}
}
测试题
在以下排序算法中,关键字比较的次数与记录的初始排列次序无关的是()。
A. 希尔排序
B. 冒泡排序
C. 插入排序
D. 直接选择排序
解析:选择排序每次都需要遍历剩下的序列 找出最大或最小值进行与当前位置的交换 无论什么情况下时间复杂度都为O(N2) **
答案:D
待排序元素规模较小时,宜选取哪种排序算法效率最高( )
A. 堆排序
B. 归并排序
C. 冒泡排序
D. 希尔排序
解析:递归时间可能长于比较时间
答案:C
适合并行处理的排序算法是()
A. 选择排序
B. 快速排序
C. 希尔排序
D. 基数排序
解析:基数排序:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。可以同时操作多个元素,从而实现了并行处理。
答案:D