数据结构算法--排序详解
引言
程序 = 数据结构 + 算法
数据结构:相互之间存在一种或多种特定关系的数据元素的集合。计算机存储、组织数据的方式。
算法:解决方案准确、完整地描述。
排序
排序:使得一串记录按照其中某个或某些关键字大小递增或递减的排列起来。
稳定 :原始序列,a在b前面且a=b,排序之后a仍然在b前面。
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡 | 稳定 | ||||
选择 | 不稳定 | ||||
插入 | 稳定 | ||||
快速 | 不稳定 | ||||
希尔 | 不稳定 | ||||
归并 | 稳定 | ||||
计数 | 稳定 | ||||
基数 | 稳定 | ||||
堆 | 不稳定 | ||||
桶 | 稳定 |
冒泡排序
算法描述:
- 从第0个元素开始,依次两两比较一对元素;
- 前一个元素大于后一个元素,则交换两个元素;
- 重复上述步骤n - 1趟直到序列递增。
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void bubbleSort(std::vector<T>& arr, int len) {
if (len < 2) {
return;
}
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
}
}
}
}
选择排序
算法描述:
- 记录第0个元素下标为curr_min;
- 在第1个元素~第n个元素中,选取最小元素和第1个元素进行元素交换和下标交换;
- 之后第1、2……个元素重复上述步骤直到序列递增,共n-1趟。
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void selectSort(std::vector<T>& arr, int len) {
if (len < 2) {
return;
}
for (int i = 0; i < len - 1; i++) {
cur_min = i;
for (int j = i + 1; j < len; j++) {
if (arr[j] < arr[cur_min]) {
cur_min = j;
swap(arr[i], arr[cur_min]);
}
}
}
}
插入排序
算法描述:
- 第0个元素默认为已排序;
- 取下一新元素,在已排序元素的序列中从后向前扫描;
- 如果上述新元素小于已排序元素,则已排序元素移到下一位;
- 重复上述步骤,直到新元素等于或大于已排序元素,则新元素插入此已排序元素前;
- 重复2 ~ 4步骤,直到序列递增。
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void insertSort(std::vector<T>& arr, int len) {
if (len < 2) {
return;
}
for (int i = 1; i < len; i++) {
int temp = arr[i];
for (int j = i; j > 0 && arr[j - 1] > temp; j--) {
if (arr[j] > temp) {
arr[j] = arr[j - 1];
} else break;
}
arr[j] = temp;
}
}
快速排序
算法描述:
- 从头选取元素为基准元素(pivot);
- 遍历序列,比基准小的放左边,比基准大的放右边;
- 递归重复上述2个步骤,直到序列递增。
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void quickSort(std::vector<T>& arr, int low, int high) {
if (len < 2) {
return;
}
if (low < high) {
int i = low, j = high, index = low;
T pivot = arr[low];
while (i < j) {
while (i <= j) {
if (pivot > arr[j]) {
arr[i] = arr[j];
index = j;
i++;
break;
}
j--;
}
while (i <= j) {
if (pivot > arr[i]) {
arr[i] = arr[j];
index = i;
j--;
break;
}
i++;
}
arr[index] = pivot;
quickSort(arr, low, index - 1);
quickSort(arr, index + 1, high);
}
}
希尔排序
算法描述:
- 选择一个递减序列,每个值为下标增量因子;
- 按下标增量因子序列个数k,对序列进行k趟排序;
- 每趟排序的待排序列为若干长度子序列,子序列内部为直接插入排序;
- 下标增量因子为1时,整个序列作为一个表处理。
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void shellSort(std::vector<T>& arr, int len) {
if (len < 2) {
return;
}
int temp, index, gap = len / 2;
while (gap > 0) {
for (int i = gap; i < len; i++) {
temp = arr[i];
index = i - gap;
while (index >= 0 && arr[index] > arr[index + gap]) {
arr[index + gap] = arr[index];
index = gap - index;
}
arr[index + gap] = temp;
}
gap = gap / 2;
}
}
归并排序
算法描述:
- 序列分为两个长度为len/2的子序列;
- 对子序列分别归并排序;
- 两个子序列合为最终序列。
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void mergeSort(std::vector<T>& arr, int low, int high) {
if (len < 2) {
return;
}
std::vector<T> new_arr = std::vector<T>((high - low + 1), 0);
int i = low, j = mid + 1; k = 0;
while(i <= mid && j <= r) {
if (arr[i] <= arr[j]) {
new_arr[k++] = arr[i++];
} else {
new_arr[k++] = arr[j++];
}
}
while (i <= mid) {
new_arr[k++] = arr[i++];
}
k = 0;
for (int i = low; i <= high; i++) {
arr[i] = new_arr[k++];
}
}
计数排序
算法描述:
- 获取最大和最小元素,为下标数组首元素和尾元素;
- 统计每个下标为i的元素出现的次数存入下标数组第i项,并且计数+1
- 将每个元素i放在新数组第i项,每放一个新数组,对应下标数组计数-1
算法图解:
算法伪代码--c++:
#include <iostream>
using namespace std;
template<typename T>
void countingSort(std::vector<T>& arr) {
if (len < 2) {
return;
}
int max = arr[0], min = arr[0];
for (auto a: arr) {
max = std::max(a, max);
min = std::min(a, min);
}
int bias = 0 - min;
std::vector<T> new_count = std::vector<T>((max - min + 1), 0);
for (int i = 0; i < len; ++i) {
new_count[arr[i] + bias]++;
}
int index = 0;
for (int i = 0; i < new_count.size(); i++) {
for (int j = 0; j < new_count[i]; j++) {
arr[index++] = i + bias;
}
}
}
基数排序
算法描述:
- 获取最大数,获取每个元素位数
- 从最低位开始,每位组成count序列
- 对count序列基计数排序
算法图解:
算法伪代码--c++:
// 基数排序
#include<iostream>
#include<cstring> //memset, memcpy
#include<time.h>
using namespace std;
int getMaxDigit(std::vector<T>& arr, int len){
if (len < 2) {
return;
}
int max = arr[0];
for (auto a: arr) {
max = std::max(a, max);
}
int maxdigit = 0; //最大位数
while(max){
max /= 10;
maxdigit++;
}
return maxdigit;
}
template<typename T>
void radixSort(std::vector<T>& arr, int len){
int base = 1, digit = GetMaxDigit(arr, len);
std::vector<T> temp = std::vector<T>(len, 0);
std::vector<T> count = std::vector<T>(len, 0);
std::vector<T> start = std::vector<T>(len, 0);
while(digit--){
for(int i = 0; i < len; i++){
int index = a[i] / base % 10; //每一位数字
count[index]++;
}
for(int i = 1; i < len; i++) {
start[i] = count[i - 1] + start[i - 1];
}
for(int i = 0; i < len; i++){
int index = a[i] / base % 10;
tmp[start[index]++] = a[i];
}
memcpy(a, temp, len * sizeof(T));
// next radix
base *= 10;
}
}
堆排序
算法描述:
- 初始待排关键字序列构建大顶堆,次序列无序
- 堆顶元素与最后一个元素交换,得到有序区的堆顶元素和无序区的其余元素
- 对无序区调整为新堆,再次交换堆顶元素与最后一个元素
- 重复上述步骤,直到序列递增
算法图解:
算法伪代码--c++:
// 堆排序
#include <iostream>
using namespace std;
template<typename T>
void heapSort(std::vector<T>& arr, int len) {
if (len < 2) {
return;
}
buildMaxHeap(arr, len);
while (len > 1) {
int temp = arr[0];
arr[0] = arr[len - 1];
arr[len - 1] = temp;
len--;
popBuildMaxHeap(arr, len);
}
}
void popBuildMaxHeap(std::vector<T>& arr, int len) {
int parent_index = 0;
int temp = arr[parent_index];
int child_index = 2 * parent_index + 1;
while (child_index < len) {
if (child_index + 1 < len && arr[child_index] < arr[child_index + 1]) {
child_index++;
}
if (arr[child_index] > temp) {
arr[parent_index] = arr[child_index];
parent_index = child_index;
child_index = 2 * parent_index + 1;
} else {
break;
}
}
arr[parent_index] = temp;
}
void buildMaxHeap(std::vector<T>& arr, int len) {
for (int i = 0; i < len / 2; i++) {
downAdjustHeap(arr, i, len);
}
}
void downAdjustHeap(std::vector<T>& arr, int parent_index, int len) {
int child_index = 2 * parent_index + 1;
if (child_index + 1 < len && arr[child_index] < arr[child_index + 1]) {
child_index++;
}
if (arr[child_index] > arr[parent_index]) {
int temp = arr[parent_index];
arr[parent_index] = arr[child_index];
arr[child_index] = temp;
}
}
桶排序
算法描述:
- 设置bucket_size,如bucket_size = 10,则桶序列可以存放{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
- 遍历序列,按照桶序列关键字,将原序列放入不同桶
- 每个不为空的桶排序
- 不为空的桶拼接
算法图解:
算法伪代码--c++:
// 桶排序
#include <iostream>
using namespace std;
template<typename T>
void bucketSort(std::vector<T>& arr, int len, int bucket_size) {
if (len < 2) {
return arr;
}
int max = arr[0], min = arr[0];
for (auto a: arr) {
max = std::max(a, max);
min = std::min(a, min);
}
int bucket_count = (max - min) / bucket_size + 1;
std::vector<T> origin_arr = arr;
std::vector<std::vector<T>> bucket_arr(bucket_size, vector<T>(len, 0));
for (int i = 0; i < len; i++) {
bucket_arr[(origin_arr[i] - min) / bucket_size] = origin_arr[i];
}
int index = 0;
for (auto bucket : bucket_arr) {
for (int i = 0; i < bucket.size(), i++) {
for (int j = 0; j < bucket.size() - i; j++) {
if (bucket[i] > bucket[j]) {
int temp = bucket[i];
bucket[i] = bucket[j];
bucket[j] = temp;
}
}
}
for (auto b: bucket) {
arr[index++] = b;
}
}
}