目录
1--概述
十大经典排序算法包括:冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、计数排序、基数排序 和 桶排序;
2--冒泡排序
主要思想:视频讲解参考
每次将最大的元素移动到最后;
时间复杂度为 O (n^2);
#include <iostream>
#include <vector>
#include <queue>
class Solution{
public:
// 冒泡排序
void bubSort(std::vector<int> &input){
int len = input.size();
while(--len){ // len 一开始指向最后一个元素
for(int i = 0; i < len; i++){
if(input[i] > input[i + 1]){
int tmp = input[i];
input[i] = input[i + 1];
input[i + 1] = tmp;
}
}
}
}
// 优化的冒泡排序
void Better_bubSort(std::vector<int> &input){
int len = input.size();
int flag = 1;
while(--len && flag){ // 当前面的已有序,即上一轮没有发生交换,就不会进入循环体
flag = 0;
for(int i = 0; i < len; i++){
if(input[i] > input[i + 1]){
flag = 1;
int tmp = input[i];
input[i] = input[i + 1];
input[i + 1] = tmp;
}
}
}
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
S1.bubSort(input);
S1.Print(input);
return 0;
}
3--选择排序
主要思想:视频讲解参考
选择排序基于冒泡排序的思想,每一轮找到最小值(用 k 指向),将最小值与循环的第一个数(用 i 指向)交换,这样可以确保每一轮将最小值选择排序到前面;
时间复杂度为 O (n^2);
#include <iostream>
#include <vector>
#include <queue>
class Solution{
public:
// 选择排序
void selectSort(std::vector<int>& input){
int len = input.size();
int k = 0; // 指向本次循环最小值的位置
for(int i = 0; i < len; i++){
k = i;
for(int j = i + 1; j < len; j++){
if(input[j] < input[k]) k = j; // 更新本次循环最小值的位置
}
int tmp = input[i];
input[i] = input[k]; // 最小值的位置移动到前面
input[k] = tmp;
}
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
S1.selectSort(input);
S1.Print(input);
return 0;
}
4--插入排序
主要思想:视频讲解参考
遍历每一个元素,将其插入到前面已经排好序的序列中,即与前面的数逐一比较,直到找到比其小的数,将元素插入到其后面;
时间复杂度为 O (n^2);
#include <iostream>
#include <vector>
#include <queue>
class Solution{
public:
// 插入排序
void insertSort(std::vector<int>& input){
int len = input.size();
int key; // key表示本次循环中要插入的值
for(int i = 1; i < len; i++){ // i 从 1 开始,因为input[0]已经是有序的
key = input[i];
int j = i-1; // 从前一个数开始比较
while(j >= 0 && key < input[j]){ // 向前寻找,直到找到第一个比 key 小的数
input[j+1] = input[j]; // 比较过的元素向后移动,因为要空出一个位置
j--; // 继续向前寻找
}
input[j+1] = key; // 将 key 插入到第一个比它小的数的后面
}
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
S1.insertSort(input);
S1.Print(input);
return 0;
}
5--希尔排序
主要思想:视频讲解参考
希尔排序是插入排序的进阶版,实质上是使用多次插入排序,只是每次插入比较的步长是step,当 step 为1时,希尔排序就退化为普通的插入排序;
希尔排序主要应用在数据量很大的排序问题中,本质上是减少交换的次数,因为当 step 退化为 1 时,序列已经大部分是有序的,所以交换的次数很少。
希尔排序的时间复杂度为:O(n^(1.3--2)),是一种不稳定的排序;
#include <iostream>
#include <vector>
#include <queue>
class Solution{
public:
// 希尔排序
void shellSort(std::vector<int>& input){
int len = input.size();
//初始比较的 step 为 len/2,每一轮退化为 step/2;
for(int step = len / 2; step > 0; step = step / 2){
// 每一轮使用插入排序
for(int i = step; i < len; i++){
int key = input[i]; // 要插入的数
int j = i - step; // 与前一个数进行比较
while(j >= 0 && key < input[j]){
input[j + step] = input[j];
j = j - step; // 不断前移
}
input[j + step] = key;
}
}
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
S1.shellSort(input);
S1.Print(input);
return 0;
}
6--快速排序
主要思路:
每次选定一个基准轴元素 (pivot),从两边开始扫描,比 pivot 小的元素放在轴的左边,比 pivot 大的元素放在轴的右边;
经过一次遍历之后,pivot 左边的元素都比 pivot 小,pivot 右边的元素都比 pivot 大,这样对于最终的结果来说,pivot 所在的位置是排好序的,只需递归排序 pivot 左边的元素和右边的元素即可;
快速排序的时间复杂度为:O(nlogn) ~ O(n^2)
#include <iostream>
#include <vector>
#include <queue>
class Solution{
public:
// 快速排序
void quickSort(std::vector<int>& input, int left, int right){
if(left >= right) return; // 递归结束条件
int len = input.size();
int pivot = input[left]; // 记录基准轴元素
int i = left, j = right;
while(i < j){
while(i < j && input[j] >= pivot) j--; // 从右往左扫描直到找到第一个比 pivot 小的元素停下
input[i] = input[j]; // 将小的数放到 pivot 的左边
while(i < j && input[i] <= pivot) i++; // 从左往右扫描直到找到第一个比 pivot 大的元素停下
input[j] = input[i]; // 将大的数放到 pivot 的右边
}
// 一趟遍历之后,将基准元素 pivot 放在 i 的位置(i == j)
input[i] = pivot;
// 递归排序pivot左边的元素和右边的元素
quickSort(input, left, i - 1);
quickSort(input, i+1, right);
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
S1.quickSort(input, 0, input.size()-1);
S1.Print(input);
return 0;
}
7--归并排序
普通归并排序将两个有序的序列归并为一个有序的新序列;视频讲解
#include <iostream>
#include <vector>
class Solution{
public:
std::vector<int> mergeSort(std::vector<int> input1, std::vector<int> input2){
std::vector<int> Res;
int i = 0, j = 0;
while(i < input1.size() && j < input2.size()){
if(input1[i] < input2[j]){
Res.push_back(input1[i]);
i++;
}
else{
Res.push_back(input2[j]);
j++;
}
}
while(i < input1.size()){
Res.push_back(input1[i]);
i++;
}
while(j < input2.size()){
Res.push_back(input2[j]);
j++;
}
return Res;
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input1 = {1, 3, 4, 6};
std::vector<int> input2 = {2, 5, 7, 8};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input1);
S1.Print(input2);
std::cout << "After Sort: " << std::endl;
std::vector<int> output = S1.mergeSort(input1, input2);
S1.Print(output);
return 0;
}
进阶归并排序将一个无序的序列归并排序为一个有序的系列;通过递归将无序的序列二分,从底层开始将二分的序列归并排序为有序序列;
#include <iostream>
#include <vector>
class Solution{
public:
// 归并排序
std::vector<int> mergeSort(std::vector<int> input){
std::vector<int> Res = split(input, 0, input.size() - 1);
return Res;
}
// 二分序列
std::vector<int> split(std::vector<int> input, int left, int right){
if(left == right){ // 只剩下一个元素,返回由该元素构成的有序序列
std::vector<int> Res = {input[left]};
return Res;
}
int mid = left + (right - left) / 2;
std::vector<int> input1 = split(input, left, mid);
std::vector<int> input2 = split(input, mid+1, right);
std::vector<int> Res = merge(input1, input2); // 二分至底层,进行归并排序再回溯
return Res;
}
// 将两个有序的序列归并排序
std::vector<int> merge(std::vector<int> input1, std::vector<int> input2){
std::vector<int> Res;
int i = 0, j = 0;
while(i < input1.size() && j < input2.size()){
if(input1[i] < input2[j]){
Res.push_back(input1[i]);
i++;
}
else{
Res.push_back(input2[j]);
j++;
}
}
while(i < input1.size()){
Res.push_back(input1[i]);
i++;
}
while(j < input2.size()){
Res.push_back(input2[j]);
j++;
}
return Res;
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 4, 6, 3, 5, 2, 8, 7, 10, 9, 11, 12};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
std::vector<int> output = S1.mergeSort(input);
S1.Print(output);
return 0;
}
8--堆排序
关于堆的知识:从堆的定义到优先队列、堆排序(强烈推荐)
堆排序基本知识:视频讲解参考
① 堆排序中的堆分为大根堆和小根堆,小根堆中孩子结点的值必须大于父亲结点的值(即堆顶是最小值),大根堆中孩子结点的值必须小于父亲结点的值(即堆顶是最大值);
② 大根堆和小根堆本质上是一个完全二叉树,满足以下性质:结点连续存储,假设结点从0开始编号,则左孩子的位置为 2*parent + 1,右孩子的位置为 2*parent + 2;
③ (外堆且基于小顶堆)堆排序本质上分为入堆和出堆两步:
入堆时新加入结点,结点需要自底向上与父亲结点进行比较,重构大根堆和小根堆,确保满足上面的性质;
出堆时返回堆顶元素,并将最后一个元素放在堆顶位置,自顶向下与较小的孩子结点进行比较,重构大根堆和小根堆,确保满足上面的性质;
#include <iostream>
#include <vector>
#include <queue>
class Solution{
public:
void heapSort(std::vector<int>& input){
int size = input.size();
// 入堆
for(int data : input){
pushHeap(data);
}
// 出堆
for(int i = 0; i < size; i++){
input[i] = popheap();
}
}
// 入堆
void pushHeap(int data){
if(len == 0){
heap.push_back(data); // 入堆
len++;
return;
}
heap.push_back(data); // 入堆
len++; // 元素个数 +1
// 重新排序堆
int cur = len - 1; // 当前堆中最后一个元素的位置
int parent = (cur-1) / 2; // 父亲节点的位置
while(parent != cur){ // 直到根节点
if(heap[cur] < heap[parent]){
swap(cur, parent); // 交换两个位置的值
cur = parent;
parent = (cur-1) / 2; // 从下往上比较,不断交换
}
else break;
}
}
// 出堆
int popheap(){
int res = heap[0]; // 堆顶元素
int cur = len - 1; // 最后一个元素的位置
heap[0] = heap[cur]; // 最后一个元素的位置放到堆顶中
len--; // 堆中元素 -1
// 重新排序堆
int start = 0;
while(start <= len){ // 从根节点开始排序
int l_child = 2*start+1;
int r_child = 2*start+2;
if(l_child > len || r_child > len) break; // 防止溢出
int small = heap[l_child] < heap[r_child] ? l_child : r_child; // 较小的孩子
if (heap[small] < heap[start]){ // 从上到下与较小的孩子交换位置
swap(small, start);
start = small;
}
else break;
}
return res;
}
void swap(int index1, int index2){
int temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
private:
std::vector<int> heap; // 小顶堆
int len = 0; // 堆中元素的个数
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5, 8, 7, 10, 9};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
S1.heapSort(input);
S1.Print(input);
return 0;
}
9--计数排序
在计数排序中,用一个数组的下标表示原数组的真值,数组下标对应的值表示真值出现的次数;遍历记录的数组,当下标对应的值不为0时,输入其下标(值多大就输出多少次);
在 C++ 中可以通过哈希表的形式来实现计数排序,key 表示原数组的真值,value 表示真值出现的次数;
经典利用数组实现计数排序可以参考视频讲解
#include <iostream>
#include <vector>
#include <map>
class Solution{
public:
std::vector<int> countSort(std::vector<int> input){
for(int item : input){ // 遍历每一个数
M.insert(std::pair<int, int>(item, M.count(item)+1));
}
for(auto item : M){ // map自动根据key排好序了,因此直接遍历即可
int num = item.second; // 出现的次数
while(num != 0){
Res.push_back(item.first);
num--;
}
}
return Res;
}
// 打印函数
void Print(const std::vector<int>& input){
for(int item : input){
std::cout << item << " ";
}
std::cout << std::endl;
}
private:
std::map<int, int> M; // <真值,出现的次数>
std::vector<int> Res;
};
int main(int argc, char *argv[]){
std::vector<int> input = {1, 3, 6, 4, 2, 5, 8, 7, 10, 9};
Solution S1;
std::cout << "Before Sort: " << std::endl;
S1.Print(input);
std::cout << "After Sort: " << std::endl;
std::vector<int> output = S1.countSort(input);
S1.Print(output);
return 0;
}
10--基数排序
在基数排序中,对每一个数依次按照个位、十位、百位 ... 来排序,对最高位排序后即为有序结果,返回即可;