【SEU程序设计课笔记】 12 - 2020/11/19 - Write C++ programs - 排序

排序是一个学习程序绕不开的问题,这里我们来探讨一下排序的方式。

排序

这里使用有系统自带的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亿个整数排序(时间和随机数大小有关),当范围不大时,可以突破一秒大关!
    1
    2
    3
  • 10万个整数排序(给插入排序和冒泡排序一个机会)
    4

分析

在不同数据范围内,不同的函数有自己的优势区间。数据较少时快排会比较快,当数值变大时,LSD和MSD更快。

sort 函数

实际上原理就是快速排序。

冒泡排序

详见我的博客 【C++ 程序】 冒泡排序

插入排序

如名字所言,原理就是往排好的部分中插入元素。

快速排序/快速排序改进版/安全快速排序

详见我的博客 【C++ 程序】 快速排序

基数排序(LSD & MSD)

详见我的博客 【C++ 程序】 基数排序

补充

排序的稳定性:相同元素的相对位置不改变称为稳定的,否则称为不稳定的。

运行内存的四个区域:
栈内存
堆内存
代码区
数据区(静态区、全局区、常量区)


ALL RIGHTS RESERVED © 2020 Teddy van Jerry
欢迎转载,转载请注明出处。


See also

Teddy van Jerry 的导航页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值