排序是一个学习程序绕不开的问题,这里我们来探讨一下排序的方式。
排序
这里使用有系统自带的sort
函数、冒泡排序(Bubble_sort)、基数排序(LSD & MSD)及其改进版、有快速排序(Quick_sort)、快速排序改进版(Quick_sort_pro)、安全快速排序(Quick_sort_pro_safe)、基数排序(LSD & MSD)。
程序
Source.cpp
#include <iostream>
#include <vector>
#include <random>
#include <ctime>
#include <algorithm>
#include "Sort.h"
using namespace std;
vector<int> ran;
int ck = 0;
void random(long long n, int min_one, int max_one)
{
uniform_int_distribution<int> u(min_one, max_one);
default_random_engine e;
for (size_t i = 0; i != n; i++)
{
ran.push_back(u(e));
}
}
// tset whether the sort is correct
void test(vector<int> to_test)
{
int ck_now = 0;
for (auto iter = to_test.cbegin(); iter != to_test.cend() - 1; iter++)
{
if (*iter > * (iter + 1))
{
cerr << "(failed) ";
return;
}
}
for (auto c : to_test)
ck_now ^= c;
if (ck_now != ck)
{
cerr << "(failed) ";
return;
}
cout << "(succeeded) ";
}
int main(int argc, int** argv)
{
vector<vector<int>> Standard_sort;
long long length;
int MIN_one = -1000000, MAX_one = 1000000;
cout << "This is the sorting programme.\nDesigned by Teddy van Jerry.\n" << endl;
cout << "Please input the range of random numbers: ";
cin >> MIN_one >> MAX_one;
cout << "Please input the length you want to sort: ";
cin >> length;
random(length, MIN_one, MAX_one);
for (auto c : ran)
ck ^= c; // calculate the initial result
// standard algorithm sort defined in C++ library
{
vector<int> temp_vec = ran;
cout << "Algorithm_sort: ";
clock_t begin_time = clock();
sort(temp_vec.begin(), temp_vec.end());
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// LSD sort pro heap1
{
vector<int> temp_vec = ran;
cout << "LSD_sort_pro_heap1: ";
clock_t begin_time = clock();
LSD_sort_pro_heap1(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// LSD sort pro heap2
{
vector<int> temp_vec = ran;
cout << "LSD_sort_pro_heap2: ";
if (length > 2E7)
{
cout << "(skipped) The number of elements is too large." << endl;
}
else
{
clock_t begin_time = clock();
LSD_sort_pro_heap2(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
}
// LSD sort pro
{
vector<int> temp_vec = ran;
cout << "LSD_sort_pro: ";
clock_t begin_time = clock();
LSD_sort_pro(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// LSD sort
{
vector<int> temp_vec = ran;
cout << "LSD_sort: ";
clock_t begin_time = clock();
LSD_sort(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// MSD sort pro
{
vector<int> temp_vec = ran;
cout << "MSD_sort_pro: ";
clock_t begin_time = clock();
MSD_sort_pro(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// MSD sort
{
vector<int> temp_vec = ran;
cout << "MSD_sort: ";
clock_t begin_time = clock();
MSD_sort(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// quick sort pro safe
{
vector<int> temp_vec = ran;
cout << "Quick_sort_pro_safe: ";
clock_t begin_time = clock();
quick_sort_pro_safe(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// quick sort pro
{
vector<int> temp_vec = ran;
cout << "Quick_sort_pro: ";
clock_t begin_time = clock();
quick_sort_pro(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// quick sort
{
vector<int> temp_vec = ran;
cout << "Quick_sort: ";
clock_t begin_time = clock();
quick_sort(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// insertion sort
{
vector<int> temp_vec = ran;
cout << "Insertion_sort: ";
clock_t begin_time = clock();
insertion_sort(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
// bubble sort
{
vector<int> temp_vec = ran;
cout << "Bubble_sort: ";
clock_t begin_time = clock();
bubble_sort(temp_vec);
clock_t total_time = clock() - begin_time;
test(temp_vec);
cout << static_cast<double>(total_time) / 1000 << " seconds." << endl;
}
return 0;
}
// ALL RIGHTS RESERVED (C) 2020 Teddy van Jerry
Sort.h
#pragma once
#define _SORT_
#include <vector>
#include <string>
#include "HeapVector.h" // using dynamic array
#define my_max(i, j) ((i > j) ? i : j)
#define my_min(i, j) ((i < j) ? i : j)
#define radix_index 256
#define radix_binary 8
void my_swap(int& i, int& j)
{
int temp = j;
j = i;
i = temp;
}
int my_pow(int n, int m)
{
int ret = 1;
for (int i = 0; i != m; i++)
ret *= n;
return ret;
}
// return the medium one of the three
std::vector<int>::iterator medium(std::vector<int>::iterator a, std::vector<int>::iterator b, std::vector<int>::iterator c)
{
if ((*a - *b) * (*a - *c) <= 0) return a;
else if ((*b - *a) * (*b - *c) <= 0) return b;
else return c;
}
void bubble_sort(std::vector<int>& vint)
{
size_t up = 0, k = 0;
while (up < vint.size())
{
k = vint.size();
for (size_t i = vint.size() - 1; i > up; i--)
{
if (vint[i] < vint[i - 1])
{
my_swap(vint[i], vint[i - 1]);
k = i;
}
}
up = k;
}
return;
}
void insertion_sort(std::vector<int>::iterator i_beg, std::vector<int>::iterator i_end)
{
for (std::vector<int>::iterator i = i_beg + 1; i != i_end; i++)
{
int temp = *i;
// slide elements right to make room for v[i]
std::vector<int>::iterator j = i;
while (j >= i_beg + 1 && *(j - 1) > temp)
{
*(j--) = *(j - 1);
}
*j = temp;
}
}
void quick_sort(std::vector<int>& vint, std::vector<int>::iterator i, std::vector<int>::iterator j)
{
if (j - i <= 1) return;
else
{
int standard_number = *i;
std::vector<int>::iterator init_begin = i;
std::vector<int>::iterator init_end = j;
while (i != j)
{
do { --j; } while (*j > standard_number && i < j);
if (i == j) break;
else
{
do { ++i; } while (*i < standard_number && i < j);
my_swap(*i, *j);
}
}
my_swap(*init_begin, *i);
quick_sort(vint, init_begin, i);
quick_sort(vint, i + 1, init_end);
}
}
void quick_sort_pro(std::vector<int>::iterator i, std::vector<int>::iterator j)
{
if (j - i <= 1) return;
else if (j - i == 2)
{
if (*i > * (j - 1))
{
my_swap(*i, *(j - 1));
}
return;
}
else if (j - i <= 7)
{
// If the number is not large,
// insertion sort can be more efficient.
insertion_sort(i, j);
return;
}
else
{
std::vector<int>::iterator init_begin = i;
std::vector<int>::iterator init_end = j;
int Standard_defined = *init_begin;
while (i != j)
{
do { --j; } while (*j > Standard_defined && i < j);
if (i == j) break;
else
{
do { ++i; } while (*i < Standard_defined && i < j);
my_swap(*i, *j);
}
}
my_swap(*init_begin, *i);
quick_sort_pro(init_begin, i);
quick_sort_pro(i + 1, init_end);
}
}
void quick_sort_pro_safe(std::vector<int>::iterator i, std::vector<int>::iterator j)
{
if (j - i <= 1) return;
else if (j - i == 2)
{
if (*i > * (j - 1))
{
my_swap(*i, *(j - 1));
}
}
else if (j - i <= 7)
{
// If the number is not large,
// insertion sort can be more efficient.
insertion_sort(i, j);
return;
}
else
{
// Choose the medium one of the three numbers,
// in order to avoid the circumstance that
// the standard number is too large or too small
// when quicksort can be reduced from o(nlog(n)) to o(N^2).
std::vector<int>::iterator standard_number = medium(i, j - 1, i + ((j - i) - 1) / 2);
std::vector<int>::iterator init_begin = i;
std::vector<int>::iterator init_end = j;
int Standard_defined = *standard_number;
my_swap(*standard_number, *init_begin);
while (i != j)
{
do { --j; } while (*j > Standard_defined && i < j);
if (i == j) break;
else
{
do { ++i; } while (*i < Standard_defined && i < j);
my_swap(*i, *j);
}
}
my_swap(*init_begin, *i);
quick_sort_pro_safe(init_begin, i);
quick_sort_pro_safe(i + 1, init_end);
}
}
void counting_sort_one(std::vector<int>& vint, int n)
{
std::vector<int>bucket[10];
size_t before_number[10]{ 0 };
for (auto c : vint)
{
bucket[c / my_pow(10, n) % 10].push_back(c);
}
for (int i = 1; i != 10; i++)
{
before_number[i] = before_number[i - 1] + bucket[i - 1].size();
}
for (int i = 0; i != 10; i++)
{
for (int j = 0; j != bucket[i].size(); j++)
{
// store the numbers by sequence
vint[before_number[i] + j] = bucket[i][j];
}
}
}
void counting_sort_one_pro(std::vector<int>& vint, int n)
{
std::vector<int>bucket[radix_index];
size_t before_number[radix_index]{ 0 };
for (auto c : vint)
{
bucket[c >> (n * radix_binary) & (radix_index - 1)].push_back(c);
}
for (int i = 1; i != radix_index; i++)
{
before_number[i] = before_number[i - 1] + bucket[i - 1].size();
}
for (int i = 0; i != radix_index; i++)
{
for (int j = 0; j != bucket[i].size(); j++)
{
// store the numbers by sequence
vint[before_number[i] + j] = bucket[i][j];
}
}
}
void counting_sort_one_pro_heap1(std::vector<int>& vint, int n)
{
if (vint.size() < 7)
{
// If the number is not large,
// insertion sort can be more efficient.
insertion_sort(vint.begin(), vint.end());
}
else
{
Heap_Vector<int> bucket[radix_index];
size_t before_number[radix_index]{ 0 };
for (auto c : vint)
{
// equivalent to:
// bucket[c / my_pow(radix_index, n) % radix_index].push_back(c);
// but using the operator >> and & can be more efficient
bucket[c >> (n * radix_binary) & (radix_index - 1)].push_back(c);
}
for (int i = 1; i != radix_index; i++)
{
before_number[i] = before_number[i - 1] + bucket[i - 1].size();
}
for (int i = 0; i != radix_index; i++)
{
for (int j = 0; j != bucket[i].size(); j++)
{
// store the numbers by sequence
vint[before_number[i] + j] = bucket[i][j];
}
}
}
}
void counting_sort_one_pro_heap2(std::vector<int>& vint, int n)
{
int** bucket = new int* [radix_index]; // define a dynamic array of arrays
for (int i = 0; i != radix_index; i++)
bucket[i] = new int[vint.size()]; // define a dynamic array
size_t element_number[radix_index]{ 0 }; // initialize to 0
size_t before_number[radix_index]{ 0 }; // initialize to 0
for (auto c : vint)
{
// equivalent to:
// int bucket_number = c / my_pow(radix_index, n) % radix_index;
// but using the operator >> and & can be more efficient
int bucket_number = c >> (n * radix_binary) & (radix_index - 1);
// increment the element_number at the same time
bucket[bucket_number][element_number[bucket_number]++] = c;
}
for (int i = 1; i != radix_index; i++)
{
before_number[i] = before_number[i - 1] + element_number[i - 1];
}
for (int i = 0; i != radix_index; i++)
{
for (size_t j = 0; j != element_number[i]; j++)
{
// store the numbers by sequence
vint[before_number[i] + j] = bucket[i][j];
}
}
for (int i = 0; i != radix_index; i++)
delete[] bucket[i]; // free the dynamic arrays
}
void counting_sort_multi(std::vector<int>& vint, int n)
{
if (n != -1)
{
std::vector<int>bucket[10];
size_t before_number[10]{ 0 };
for (auto c : vint)
{
bucket[c / my_pow(10, n) % 10].push_back(c);
}
for (auto& c : bucket)
{
counting_sort_multi(c, n - 1);
}
for (int i = 1; i != 10; i++)
{
before_number[i] = before_number[i - 1] + bucket[i - 1].size();
}
for (int i = 0; i != 10; i++)
{
for (int j = 0; j != bucket[i].size(); j++)
{
vint[before_number[i] + j] = bucket[i][j]; // store the numbers by sequence
}
}
}
else return;
}
void counting_sort_multi_pro(std::vector<int>& vint, int n)
{
if (n != -1)
{
std::vector<int>bucket[radix_index];
size_t before_number[radix_index]{ 0 };
for (auto c : vint)
{
// equivalent to:
// bucket[c / my_pow(radix_index, n) % radix_index].push_back(c);
// but using the operator >> and & can be more efficient
bucket[c >> (n * radix_binary) & (radix_index - 1)].push_back(c);
}
for (auto& c : bucket)
{
counting_sort_multi_pro(c, n - 1);
}
for (int i = 1; i != radix_index; i++)
{
before_number[i] = before_number[i - 1] + bucket[i - 1].size();
}
for (int i = 0; i != radix_index; i++)
{
for (int j = 0; j != bucket[i].size(); j++)
{
vint[before_number[i] + j] = bucket[i][j]; // store the numbers by sequence
}
}
}
else return;
}
void LSD_sort(std::vector<int>& vint)
{
int max_one = vint[0], min_one = vint[0];
for (auto c : vint)
{
max_one = my_max(max_one, c);
min_one = my_min(min_one, c);
}
max_one -= min_one; // every number is no less than zero now
for (auto& c : vint)
{
c -= min_one;
}
for (int i = 0; i != std::to_string(max_one).length(); i++)
{
counting_sort_one(vint, i);
}
for (auto& c : vint)
{
// return the elements to the original value
c += min_one;
}
}
void LSD_sort_pro(std::vector<int>& vint)
{
int max_one = vint[0], min_one = vint[0];
for (auto c : vint)
{
max_one = my_max(max_one, c);
min_one = my_min(min_one, c);
}
max_one -= min_one; // every number is no less than zero now
for (auto& c : vint)
{
c -= min_one;
}
// If max_one is zero, log(max_one) will be illegal.
// But this time, it is already sorted
// as all the elements are equal.
if (max_one)
{
for (int i = 0; i != static_cast<int>(log(max_one) / log(radix_index)) + 1; i++)
{
counting_sort_one_pro(vint, i);
}
}
for (auto& c : vint)
{
// return the elements to the original value
c += min_one;
}
}
void LSD_sort_pro_heap1(std::vector<int>& vint)
{
int max_one = vint[0], min_one = vint[0];
for (auto c : vint)
{
max_one = my_max(max_one, c);
min_one = my_min(min_one, c);
}
max_one -= min_one;
// make every number no less than zero
for (auto& c : vint)
{
c -= min_one;
}
// If max_one is zero, log(max_one) will be illegal.
// But this time, it is already sorted
// as all the elements are equal.
if (max_one)
{
for (int i = 0; i != static_cast<int>(log(max_one) / log(radix_index)) + 1; i++)
{
counting_sort_one_pro_heap1(vint, i);
}
}
for (auto& c : vint)
{
// return the elements to the original value
c += min_one;
}
}
void LSD_sort_pro_heap2(std::vector<int>& vint)
{
int max_one = vint[0], min_one = vint[0];
for (auto c : vint)
{
max_one = my_max(max_one, c);
min_one = my_min(min_one, c);
}
max_one -= min_one;
// make every number no less than zero
for (auto& c : vint)
{
c -= min_one;
}
// If max_one is zero, log(max_one) will be illegal.
// But this time, it is already sorted
// as all the elements are equal.
if (max_one) // if (max_one != 0)
{
for (int i = 0; i != static_cast<int>(log(max_one) / log(radix_index)) + 1; i++)
{
counting_sort_one_pro_heap2(vint, i);
}
}
for (auto& c : vint)
{
// return the elements to the original value
c += min_one;
}
}
void MSD_sort_pro(std::vector<int>& vint)
{
int max_one = vint[0], min_one = vint[0];
for (auto c : vint)
{
max_one = my_max(max_one, c);
min_one = my_min(min_one, c);
}
max_one -= min_one;
// make every number no less than zero
for (auto& c : vint)
{
c -= min_one;
}
// If max_one is zero, log(max_one) will be illegal.
// But this time, it is already sorted
// as all the elements are equal.
if (max_one) // if (max_one != 0)
{
counting_sort_multi_pro(vint, static_cast<int>(log(max_one) / log(radix_index)));
}
for (auto& c : vint)
{
// return the elements to the original value
c += min_one;
}
}
void MSD_sort(std::vector<int>& vint)
{
int max_one = vint[0], min_one = vint[0];
for (auto c : vint)
{
max_one = my_max(max_one, c);
min_one = my_min(min_one, c);
}
max_one -= min_one;
// make every number no less than zero
for (auto& c : vint)
{
c -= min_one;
}
counting_sort_multi(vint, std::to_string(max_one).length() - 1);
for (auto& c : vint)
{
// return the elements to the original value
c += min_one;
}
}
void insertion_sort(std::vector<int>& vint)
{
insertion_sort(vint.begin(), vint.end());
}
void quick_sort(std::vector<int>& vint)
{
quick_sort(vint, vint.begin(), vint.end());
}
void quick_sort_pro(std::vector<int>& vint)
{
quick_sort_pro(vint.begin(), vint.end());
}
void quick_sort_pro_safe(std::vector<int>& vint)
{
quick_sort_pro_safe(vint.begin(), vint.end());
}
// ALL RIGHTS RESERVED (C) 2020 Teddy van Jerry
HeapVector.h
#pragma once
#define _HEAP_VECTOR_
template<typename ValueType>
class Heap_Vector {
public:
Heap_Vector(size_t n = 32) :capacity(n), content_number(0), vec(new ValueType[n]) {}
~Heap_Vector() // destructor
{
delete[] vec; // free the dynamic array
}
// similar to the function of push_back in vector
void push_back(ValueType value)
{
if (content_number == capacity)
{
expand();
}
vec[content_number++] = value;
}
ValueType operator [](size_t index)
{
return vec[index];
}
size_t size()
{
return content_number;
}
private:
ValueType* vec;
size_t capacity;
size_t content_number;
void expand()
{
// 1. ask for new space for the array
ValueType* new_vec = new ValueType[2 * capacity];
// 2. copy the values over
for (size_t i = 0; i != content_number; i++)
new_vec[i] = vec[i];
// 3. delete the old array
delete[] vec;
// 4. point vec to new array
vec = new_vec;
// 5. update capacity (twice the capacity)
capacity *= 2;
}
};
// ALL RIGHTS RESERVED (C) 2020 Teddy van Jerry
输出示例
(具体运行时间和电脑性能有很大关联,我的运行配置:Intel i7-10750H,16G,2.60GHz)
注意在 Release/X64 状态下运行,否则会很慢,不在 X64 状态下一亿的输入可能会爆掉
- 1亿个整数排序(时间和随机数大小有关),当范围不大时,可以突破一秒大关!
- 10万个整数排序(给插入排序和冒泡排序一个机会)
分析
在不同数据范围内,不同的函数有自己的优势区间。数据较少时快排会比较快,当数值变大时,LSD和MSD更快。
sort 函数
实际上原理就是快速排序。
冒泡排序
详见我的博客 【C++ 程序】 冒泡排序。
插入排序
如名字所言,原理就是往排好的部分中插入元素。
快速排序/快速排序改进版/安全快速排序
详见我的博客 【C++ 程序】 快速排序。
基数排序(LSD & MSD)
详见我的博客 【C++ 程序】 基数排序。
补充
排序的稳定性:相同元素的相对位置不改变称为稳定的,否则称为不稳定的。
运行内存的四个区域:
栈内存
堆内存
代码区
数据区(静态区、全局区、常量区)
ALL RIGHTS RESERVED © 2020 Teddy van Jerry
欢迎转载,转载请注明出处。