假设输入为一组随机整数如下形式,且最终结果均为升序排列。
vector<int> vec{1,9,22,10,8,33,54,32,7};
选择排序(insert sort)
原理:分为两步,令i = 0,首先,在vec[i,end)之间搜索得到最小值,然后将该值与vec[i]交换,++i并跳转到第一步,直到i == end,完成排序。
复杂度分析:平均时间复杂度为O(n^2),空间复杂度为常数。
代码:
void selection_sort(vector<int> &vec)
{
for(auto i = vec.begin(); i != vec.end(); ++i)
{
auto min_idex = i;
for(auto j = i+1; j != vec.end(); ++j)
{
if(*min_idex > *j)
min_idex = j;
}
if(min_idex != i){
swap(*i, *min_idex);
}
}
}
插入排序(insert sort)
原理:i=0, vec[i]依次与其左侧的数作比较,如果小于则进行互换,直到该数大于左侧的数或它位于最左侧,++i并重复上一步,直到i > end。简而言之先对左侧子序列排序,而后子序列长度慢慢增加直到整个序列有序。
复杂度分析:平均时间复杂度为O(1/4 n^2),空间复杂度为常数,stability
代码:
void insert_sort(vector<int> &vec)
{
for(auto i = vec.begin(); i != vec.end(); ++i)
{
for(auto j = i; j != vec.begin(); --j)
{
if(*(j-1) > *j)
swap(*(j-1), *j);
}
}
}
希尔排序(shell sort)
原理:希尔排序其实是一种改进的插入排序算法,如果待排序列局部有序的话会大大加快插入排序算法的速度,正是如此,通过改变插入排序的步长,多次调用插入排序就可以先局部排序而后整体排序。常见的步长递增公式为step = 3*n + 1。
复杂度分析:平均算法复杂度未知,空间复杂度为常数。
代码:
void shell_sort(vector<int> &vec)
{
int N = vec.size();
int h = 1;
// Find the suitable h
while(h < N/3) h = 3 * h + 1 ;
while(h >= 1)
{
// Avoid compare the first element
for(auto i = vec.begin() + h; i != vec.end() ; ++i)
{ // The descend sequence to compare
for(auto j = i ; (j - vec.begin()) >= h ; j = j - h)
if(*(j-h) > *j)
swap(*(j-h), *j);
}
h = h/3;
}
}
归并排序(merge sort)
原理:首先将待排序列分为两个子序列,递归的对两个子序列进行归并排序,最后合并两个子序列。
复杂度分析:平均时间复杂度 O(N log N), 空间复杂度 O(N) 需要分配额外的空间,stablity.
代码:
bool is_sort(vector<int> &vec, const int &lo, const int &hi)
{
for(int i = lo; i <= hi -1; ++i)
if(vec[i] > vec[i+1]){
return false;
}
return true;
}
void merge(vector<int> &vec, vector<int> &temp_vec, const int &lo, const int &mid, const int &hi)
{
if(!is_sort(vec, lo, mid) || !is_sort(vec, mid+1, hi)){
cerr << "the merge subsequence is't sorted." << endl;
exit ;
}
// acceleration the sort
if(vec[mid] < vec[mid+1])
return;
// copy
for(int i = lo; i <= hi; ++i)
temp_vec[i] = vec[i];
int i = lo, j = mid + 1;
for(int k = lo; k <=hi; ++k)
{
if(i > mid)
vec[k] = temp_vec[j++];
else if(j > hi)
vec[k] = temp_vec[i++];
else if(temp_vec[i] < temp_vec[j])
vec[k] = temp_vec[i++];
else
vec[k] = temp_vec[j++];
}
}
void sorting(vector<int> &vec, vector<int> &temp_vec, const int &lo, const int &hi)
{
if(lo >= hi)
return;
sorting(vec, temp_vec, lo, (hi+lo)/2);
sorting(vec, temp_vec, (hi+lo)/2 + 1, hi);
merge(vec, temp_vec, lo, (hi+lo)/2, hi);
show(vec);
}
void merge_sort(vector<int> &vec)
{
vector<int> temp_vec(vec.begin(), vec.end());
sorting(vec, temp_vec, 0, vec.size()-1);
}
自底而上(button to up)的非递归方法
void button_up_merge_sort(vector<int> &vec)
{
vector<int> temp_vec(vec.begin(), vec.end());
for(int sz = 1; sz < vec.size(); sz = sz+sz)
for(int lo = 0; lo < vec.size() - sz; lo = lo + sz + sz)
{
int hi = (lo + sz + sz -1 < vec.size() - 1) ? (lo + sz + sz - 1) : (vec.size() - 1);
merge(vec, temp_vec, lo, lo + sz -1, hi);
}
}
快速排序(quick sort)
原理及步骤:先对序列进行随机化(为了避免遇到较差的情况),接着采用递归的手段,依次进行划分,排序。划分时,首先选择一个值比如v,使得对于给定序列 v 左侧的值都小于v,右侧的值都大于v。
算法复杂度:平均时间复杂度O(1.39N log N), 空间复杂度O(1)(原地算法)unstability
代码:
void shuffle(vector<int> &vec, const int &lo, const int &hi)
{
srand((unsigned)time(NULL));
for(int i = lo + 1; i <= hi; ++i)
{
int j = rand() % i;
if( i != j)
swap(*(vec.begin() + i), *(vec.begin() + j));
}
}
int partition(vector<int> &vec, const int &lo, const int &hi)
{
int i = lo;
int j = hi + 1;
while(1)
{
while(vec[lo] < vec[--j])
if(j == lo)
break;
while(vec[lo] > vec[++i])
if(i == hi)
break;
if(i >= j)
break;
swap(*(vec.begin() + i), *(vec.begin() + j));
}
swap(*(vec.begin() + lo), *(vec.begin() + j));
return j;
}
void sorting_quick(vector<int> &vec, const int &lo, const int &hi)
{
if(hi <= lo )
return;
int j = partition(vec, lo, hi);
sorting_quick(vec, lo, j - 1);
sorting_quick(vec, j + 1, hi);
}
void quick_sort(vector<int> &vec)
{
shuffle(vec, 0, vec.size() - 1);
sorting_quick(vec, 0, vec.size() - 1);
}
一种改进的利用快排查找第k个小的数的算法
int find_kth_lowest(vector<int> &vec, const int &k)
{
shuffle(vec, 0, vec.size() - 1);
int lo = 0;
int hi = vec.size() - 1;
while(hi >= lo)
{
int j = partition(vec, lo, hi);
if( j > k)
hi = j - 1;
else if(j < k)
lo = j + 1;
else
return vec[k];
}
}
Dijkstra 3-way quick sort,对于排序存在多个相同数值的改进快排算法。
void dijkstra_quick_sort(vector<int> &vec, const int &lo, const int &hi)
{
if(hi <= lo)
return;
int lt = lo;
int gt = hi;
int i = lo;
int v = vec[lo];
while( i <= gt)
{
if(vec[i] < v){
swap(*(vec.begin() + i), *(vec.begin() + lt));
lt++;
i++;
}else if(vec[i] > v){
swap(*(vec.begin() + i), *(vec.begin() + gt));
gt--;
}else{
++i;
}
}
dijkstra_quick_sort(vec, lo, lt-1);
dijkstra_quick_sort(vec, gt + 1, hi);
}
堆排序(heap sort)
原理及步骤:二叉堆是一个特殊的二叉树,要求每个节点的值都大于其两个儿子节点的值,因此树的根节点就是当前堆最大值。堆排序就是先建堆,接着每次取根节点并删除,直到堆大小为1,结束。删除根节点的策略:首先将根节点与尾后节点的值互换,接着删除尾后节点,并对根节点处理(满足儿子节点小于父节点的约束)。
算法复杂度:平均时间复杂度为O(2N lg N), 空间复杂度O(1),原地算法,unstability.
代码:
void sink(vector<int> &vec, int k, const int &max_length)
{
while(2 * k <= max_length)
{
int j = 2 * k;
if(j < max_length && vec[j] < vec[j+1])
j++;
if(vec[k] < vec[j])
swap(*(vec.begin() + k), *(vec.begin() + j));
k = j;
}
}
void heap_sort(vector<int> &vec)
{
// Insert an unused number to make the index begin at 1.
vec.insert(vec.begin(), -1);
int N = vec.size() - 1;
for(int i = N / 2; i >= 1; --i)
{
sink(vec, i, N);
}
while(N > 1)
{
swap(*(vec.begin() + 1), *(vec.begin() + N--));
sink(vec, 1, N);
}
// Erase the unused number.
vec.erase(vec.begin());
}