本文主要介绍常用的排序方法——快速排序,归并排序,堆排序,选择排序,插入排序,冒泡排序,基数排序,桶排序。
核心图
1.快速排序
核心: partition 过程, 一个大于区和一个小于区和一个划分值,最初的划分值为数组的最后一位,小于区指针最初为 l-1,大于区指针为r。
逻辑: 1.当前值小于划分值时, 将小于区的下一位和当前值交换,小于区向右扩一个位置(less++), 当前值跳下一个位置(l++)。
2.当前值大于划分值时, 将大于区的前一位和当前值交换,大于区向左扩一个位置(more--)。
3. 当前值等于划分值时,当前值直接跳下一个位置(l++)。
#include <iostream>
#include <vector>
using namespace std;
class QuickSort {
public:
vector<int> partition(vector<int>& arr1, int l, int r) {
int less = l - 1;// 向左扩 开始的位置
int more = r;
int s;
while (l < more) {
if (arr1[l] < arr1[r]) {
//该元素小于arr[r],将之放置在小于区域
//放置方法:将该元素与小于区域的右边界的下一个元素(l+1)交换,
//然后将l指针与less指针后移 (当前数与小于区域后一个数交换
++less;
s = arr1[less];
arr1[less] = arr1[l];
arr1[l] = s;
l++;
}
else if (arr1[l] > arr1[r]) {
//该元素大于arr[r],将之放置在大于区域
//放置方法:将该元素与大于区域的左边界的下一个元素(more-1)交换,
//more指针前进,这便完成了大于区域的扩大
--more;
s = arr1[more];
arr1[more] = arr1[l];
arr1[l] = s;
}
else {
l++; // 如果相等就直接 将当前值跳到下一个
}
}
swap(arr1[more], arr1[r]);
vector<int> p(2);
p[0] = less + 1;
p[1] = more;
return p;
}
void SortProcess(vector<int>& arr1, int l, int r)
{
if (l < r) {
swap(arr1[l + rand() % (r - l)], arr1[r]); // 随机快排
vector<int> p = partition(arr1, l, r);//返回的是位置,小于值得右边和大于值得左边
//cout<< p.size()<<endl;
SortProcess(arr1, l, p[0] - 1);
SortProcess(arr1, p[1] + 1, r);
}
}
void quickSort(vector<int>& arr1) {
if (arr1.empty() == true || arr1.size() < 2)
{
return;
}
SortProcess(arr1, 0, arr1.size() - 1);
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
2.归并排序
#include<iostream>
#include <vector>
using namespace std;
class Solution {
public:
void mergeSort(vector<int> &arr) {
if (arr.size() == 0 || arr.size() < 2) return;
SortProcess(arr, 0, arr.size()-1);
}
//归并排序主体过程
void SortProcess(vector<int> &arr, int l, int r) {
if (r == l) return; // 递归停止
int mid = l + (r - l)/2;
SortProcess(arr, l, mid);
SortProcess(arr, mid + 1, r);
merge(arr, l, mid, r);
}
// 合并过程
void merge(vector<int> &arr, int l, int mid, int r) {
vector<int> helper(r - l + 1); // 辅助数组用来存放排序后的数组
int i = 0;
// 两个指针
int p1 = l;
int p2 = mid + 1;
while (p1 <= mid && p2 <= r) {
if (arr[p1] < arr[p2]) {
helper[i++] = arr[p1++];
}
else {
helper[i++] = arr[p2++];
}
}
while (p1 <= mid) {// 注意这里可以等于
helper[i++] = arr[p1++];
}
while (p2 <= r) {
helper[i++] = arr[p2++];
}
// 将辅助数组元素位置拷贝会arr
for (int i = 0; i < helper.size(); i++) {
arr[l + i] = helper[i];
}
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
3.堆排序
核心(这里直接说大根堆):
heapinsert() : 将数组元素加入大根堆,即建立大根堆过程。
heapify() : 大根堆中有值发生变化,调整大根堆过程。
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
// 建立大根堆
void heapInsert(vector<int>& array, int index) {
while (array[index] > array[(index - 1) / 2]) {
swap(array[index], array[(index - 1) / 2]);// (index - 1) / 2 是 根节点, index是儿子
index = (index - 1) / 2;
}
}
//大跟堆中有值发生变化,调整成依旧是大根堆
void heapify(vector<int>& arr, int index, int heapsize) {
int left = index * 2 + 1;//左孩子节点
while (left < heapsize) {
int largest;
if (left + 1 < heapsize && arr[left + 1] > arr[left])
largest = left + 1; // 较大的子树
else
largest = left;
if (arr[largest] < arr[index]) {
largest = index;
}
if (largest == index)// 已经在堆顶了
break;
swap(arr[largest], arr[index]);
// 交换根节点 与 最大儿子 的值,某个孩子比我大,那个孩子的位置就是largest
index = largest;
left = index * 2 + 1;
}
}
void heapSort(vector<int>& arr)
{
if (arr.empty() == true || arr.size() < 2) {
return;
}
for (int i = 0; i < arr.size(); i++) {
heapInsert(arr, i);//变成大根堆
}
int heaplength = arr.size();
swap(arr[0], arr[--heaplength]);
while (heaplength > 0) {
heapify(arr, 0, heaplength);
swap(arr[0], arr[--heaplength]);
}
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
4.选择排序
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
void SelectionSort(vector<int>& arr) {
for (int i = 0; i < arr.size()-1; i++) {
int min_index = i;
for (int j = i + 1; j < arr.size(); j++) {
if ( arr[j] < arr[min_index] ) {
min_index = arr[j] < arr[min_index] ? j : min_index;
}
swap(arr[j], arr[min_index]);
}
}
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
5.插入排序
#include<iostream>
using namespace std;
class Solution {
public:
void insertSort(vector<int>& arr) {
if (arr.size() == 0) {
return;
}
for (int i = 1; i < arr.size(); i++) {
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
}
}
}
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
6.冒泡排序
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
void BubbleSort(vector<int>& arr) { //优化版的的冒泡
for (int i = 0; i < arr.size(); i++) {
bool swap_flag = false;
for (int j = arr.size()-1; j > i; j--) {
if (arr[j-1] > arr[j]) {
swap(arr[j], arr[j -1]);
swap_flag = true;
}
}
if (!swap_flag) {
return;
}
}
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
//也可以这样, 大数往后面提
class Solution2 {
public:
void BubbleSort(vector<int>& arr) {
for (int i = 0; i < arr.size(); i++) {
for (int j = 0; j < arr.size()-i-1; j++) {
if (arr[j] > arr[j+1]) {
swap(arr[j], arr[j +1 ]);
}
}
}
}
vector<int> generateRandomArr(int maxsize, int minsize, int length) {
vector<int> arr(length);
for (int i = 0; i < length; i++) {
arr[i] = rand() % (maxsize - minsize) + minsize + 1;
}
return arr;
}
};
7.基数排序
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
/**求数据的最大位数,决定排序次数*/
int maxbit(int data[], int n)
{
int d = 1; //保存最大的位数
int p = 10;
for(int i = 0; i < n; ++i)
{
while(data[i] >= p)
{
p *= 10;
++d;
}
}
return d;
}
void radixsort(int data[], int n) //基数排序
{
int d = maxbit(data, n);
int tmp[n];
int count[10]; //计数器
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++) //进行d次排序
{
for(j = 0; j < 10; j++)
count[j] = 0; //每次分配前清空计数器
for(j = 0; j < n; j++)
{
k = (data[j] / radix) % 10; //统计每个桶中的记录数
count[k]++;
}
//计算累加频数,用户计数排序
for(j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j];
for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
{
k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j]; //将tmp中的位置依次分配给每个桶
count[k]--;
}
for(j = 0; j < n; j++) //将临时数组的内容复制到data中
data[j] = tmp[j];
radix = radix * 10;
}
}
};
8.桶排序(这里写了python版的,感觉更加简洁)
'''
假设待排序的一组数统一的分布在一个范围中,并将这一范围划分成几个子范围,也就是桶
将待排序的一组数,分档规入这些子桶,并将桶中的数据进行排序
将各个桶中的数据有序的合并起来
'''
def bucket_sort(nums,n=3):
max_value = max(nums)
min_value = min(nums)
## 桶的个数
bucket_count = int((max_value-min_value)/n) + 1
### 创建数量为桶个数的空列表
buckets = [[] for _ in range(bucket_count)]
## 将原数组的元素放入到每个桶中
for i in nums:
bucket_index = int((i - min_value) // bucket_count)
buckets[bucket_index].append(i)
## 创建返回的排序数组
sort_nums = []
for j in range(len(buckets)):
##对每个桶中的元素进行排序
buckets[j].sort()
for i in range(len(buckets[j])):
sort_nums.append(buckets[j][i])
return sort_nums
总结:这几个排序经常容易会忘记,个人觉得除了桶排序和基数排序不太平易近人,其他所有的排序算法最好掌握,特别是快排,归并,堆排。