用C++类模板写了个排序算法的类,里面包含了几大经典算法的实现,例如插入排序、希尔排序、堆排序、冒泡排序、快速排序等。经测试该类可以很好地实现功能,方便直接引用。
// 几大典型的排序算法介绍以及代码实现
#ifndef _SORT_H_
#define _SORT_H_
#include <vector>
using namespace std;
template <typename T>
class CSort{
public:
typedef vector<T> type;
enum sortType{
INSERTION_SORT, // 直接插入排序
SHELL_SORT, // 希尔排序
SELECTION_SORT, // 选择排序
HEAP_SORT, // 堆排序
BUBBLE_SORT, // 冒泡排序
QUICK_SORT, // 快速排序
MERGE_SORT, // 归并排序
BUCKET_SORT // 桶排序
};
sortType m_sortType;
CSort(type vec) : m_vec(vec) {}
CSort() {}
~CSort() {}
// 设置排序类型
void SetSortType(sortType st) { m_sortType = st; }
// 设置数组内容
void SetVector(type a) { m_vec = a; }
// 返回数组对象
type GetVector() const { return m_vec; }
// 1. 直接插入排序
void InsetionSort ();
// 2. 希尔排序
void ShellSort ();
// 3. 选择排序
void SelectionSort ();
// 4. 堆排序
void HeapSort();
// 5. 冒泡排序
void BubbleSort();
// 6. 快速排序
void QuickSort();
void QuickSort(int left, int right);
// 7. 归并排序
void MergeSort();
void MergeSort(type &tmpArray, int left, int right);
void merge(type &tmpArray, int leftPos, int rightPos, int rightEnd);
// 8. 桶排序
void BucketSort();
private:
type m_vec;
};
///
//
// 打印数组内容
template <typename T>
static void PrintVector(const vector<T> &a)
{
for(int i=0; i<a.size(); i++) {
if( i != a.size()-1)
cout<<a[i]<<" ";
else
cout<<a[i]<<endl;
}
}
/**
* 1. 直接插入排序(InsertionSort):
* 将一个记录插入到已排序好的有序表中,从而得到一个新记录数增1的有序表。
* 即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,
* 直至整个序列有序为止。
*/
template <typename T>
void CSort<T>::InsetionSort ()
{
int j;
for (int i=1; i<m_vec.size(); i++) {
T temp = m_vec[i];
for(j=i; j>0 && temp<m_vec[j-1]; j--)
m_vec[j] = m_vec[j-1];
m_vec[j] = temp;
}
}
/**
* 2. 希尔排序或缩小增量排序(ShellSort):
* 操作方法:
* 1.选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
* 2.按增量序列个数k,对序列进行k 趟排序;
* 3.每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。
* 仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
*/
template <typename T>
void CSort<T>::ShellSort ()
{
for(int gap = m_vec.size()/2; gap > 0; gap /= 2) { // 采用希尔增量
for( int i=gap; i<m_vec.size(); i++ ) {
T temp = m_vec[i];
int j;
for(j = i; j >= gap && temp <m_vec[j-gap]; j -= gap)
m_vec[j] = m_vec[j-gap];
m_vec[j] = temp;
}
}
}
/**
* 3. 选择排序(SelectionSort):
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;
* 然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,
* 直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
*/
template <typename T>
static int GetMinValueIndex(const vector<T> &vec, int pos)
{
int k = pos;
for(int i=pos+1; i<vec.size(); i++) {
if(vec[i] < vec[k]) k = i;
}
return k;
}
template <typename T>
void CSort<T>::SelectionSort()
{
for(int i=0; i<m_vec.size()-1; i++) {
int k = GetMinValueIndex(m_vec, i);
T tmp = m_vec[i];
m_vec[i] = m_vec[k];
m_vec[k] = tmp;
PrintVector(m_vec);
}
}
/**
* 4. 堆排序(HeapSort):
* 初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,
* 使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者
* 最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元
* 素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为
* 堆排序。
* 实现堆排序需要解决2个问题:
* 1.如何将n个待排序的数建成堆;
* 2.输出堆顶元素,如何调整剩下的n-1个数,成为一个新堆。
*/
/**
* 将n个待排序的数建成小顶堆:
* 建堆方法:
* 1. n个节点的完全二叉树,最后一个节点是第[n/2]-1个节点的孩子(标号从0开始);
* 2. 筛选从第[n/2]-1个节点开始,使该子树成为堆;
* 3. 之后依次向前,使节点为根的子树成为堆,直到根节点。
*/
template <typename T>
static void BuildHeap(vector<T> &a)
{
for(int i = a.size()/2-1; i>=0; i--)
HeapAdjust(a, i, a.size());
PrintVector(a); // 输出建堆后数组中内容
}
template <typename T>
static void HeapAdjust(vector<T> &a, int pos, int length)
{
int child = 2*pos+1; // 左节点在数组中的位置
while(child < length) {
if(child +1 < length && a[child +1]<a[child]) // 如果右节点存在并且右节点值小于左节点,将当前位置指向右节点
++child;
if( a[pos]>a[child] ) {
T temp = a[pos];
a[pos] = a[child];
a[child] = temp;
pos = child; // 交换后对孩子节点做同样操作,使之成为堆
child = pos*2+1;
}
else
break; // 不需要调整直接退出
}
}
template <typename T>
void CSort<T>::HeapSort()
{
BuildHeap(m_vec);
type tmpArray(m_vec.size());
// 从最后一个元素开始对序列调整
for(int i=m_vec.size()-1, j=0; i>=0; i--, j++) {
tmpArray[j] = m_vec[0];
m_vec[0] = m_vec[i];
m_vec[i] = tmpArray[j];
HeapAdjust(m_vec, 0, i);
}
m_vec = tmpArray;
PrintVector(m_vec);
}
/**
* 5. 冒泡排序(BubbleSort):
* 在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次
* 进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排
* 序与排序要求相反时,就将它们互换。
*/
template <typename T>
void CSort<T>::BubbleSort()
{
for(int i=0; i<m_vec.size(); i++) {
bool flag = false;
for(int j=m_vec.size()-1; j>i; j--) {
if( m_vec[j] < m_vec[j-1]) {
T tmp = m_vec[j];
m_vec[j] = m_vec[j-1] ;
m_vec[j-1] = tmp;
flag = true;
}
}
if(!flag) break; // 某次循环没有交换冒泡,则序列已经有序,直接退出
else PrintVector(m_vec);
}
}
/**
* 6. 快速排序(QuickSort):
* 1)选择一个基准元素pivot,方便起见通常选择第一个或最后一个元素;
* 2)通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值都比基准元素小,
* 另一部分记录的元素都比基准元素大,此时基准元素在其排好序后的正确位置。
* 3)然后分别对这两部分记录用同样的方法排序,直到整个记录有序。
*/
template <typename T>
static inline void swap(T *a, T *b)
{
T tmp = *a;
*a = *b;
*b = tmp;
}
template <typename T>
void CSort<T>::QuickSort()
{
QuickSort(0, m_vec.size()-1);
}
template <typename T>
void CSort<T>::QuickSort(int left, int right)
{
if(left < right) {
T pivot = m_vec[left]; // 选择最左边的数作为基准,然后将其放到最后的位置
swap(&m_vec[left], &m_vec[right]);
int i = left-1, j = right;
for( ; ; ) {
while( m_vec[++i] < pivot ) {}
while( m_vec[--j] > pivot ) {}
if(i < j) swap(&m_vec[i], &m_vec[j]);
else break;
}
swap(&m_vec[i], &m_vec[right]);
PrintVector(m_vec);
QuickSort(left, i-1);
QuickSort(i+1, right);
}
}
/**
* 7. 归并排序(MergeSort):
* 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为
* 若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
*/
template <typename T>
void CSort<T>::MergeSort()
{
vector<T> tmpArray( m_vec.size() );
MergeSort(tmpArray, 0, m_vec.size()-1 );
}
template <typename T>
void CSort<T>::MergeSort(type &tmpArray, int left, int right)
{
if(left < right) { // 分治策略实现
int center = (left + right) /2;
MergeSort(tmpArray, left, center);
MergeSort(tmpArray, center+1, right);
merge(tmpArray, left, center+1, right);
}
}
template <typename T>
void CSort<T>::merge(type &tmpArray, int leftPos, int rightPos, int rightEnd)
{
int leftEnd = rightPos - 1;
int tmpPos = leftPos;
int numElements = rightEnd - leftPos + 1;
while (leftPos <= leftEnd && rightPos <= rightEnd) {
if(m_vec[leftPos] <= m_vec[rightPos])
tmpArray[tmpPos++] = m_vec[leftPos++];
else
tmpArray[tmpPos++] = m_vec[rightPos++];
}
while (leftPos <= leftEnd) // copy rest of left array
tmpArray[tmpPos++] = m_vec[leftPos++];
while (rightPos <= rightEnd) // copy rest of right array
tmpArray[tmpPos++] = m_vec[rightPos++];
for(int i=0; i<numElements; i++, rightEnd--)
m_vec[rightEnd] = tmpArray[rightEnd];
PrintVector(m_vec);
}
/**
* 8. 桶排序(BucketSort):
* 是将序列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式
* 继续使用桶排序进行排序)。
* 例如要对大小为[1..1000]范围内的n个整数A[1..n]排序。首先,可以把桶设为大小为10的范围,具体
* 而言,设集合B[1]存储[1..10]的整数,集合B[2]存储(10..20]的整数,……集合B[i]存储((i-1)*10,i*10]
* 的整数,i=1,2,..100。总共有100个桶。然后,对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]
* 中。再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择乃至快排,一般来说任何排序法都可以。
* 最后,依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,整个序列就有序了。
*/
template <typename T>
void CSort<T>::BucketSort()
{
cout<<"BucketSort is not completed!"<<endl;
}
#endif
各种排序的时间复杂度,空间复杂度和稳定性如下表所示。
排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最好情况 | 最坏情况 | 辅助存储 | ||
直接插入 | O(N2) | O(N) | O(N2) | O(1) | 稳定 |
希尔排序 | O(N7/6)? | O(N) | O(N2) | O(1) | 不稳定 |
直接选择 | O(N2) | O(N2) | O(N2) | O(1) | 不稳定 |
堆排序 | O(NlogN) | O(NlogN) | O(NlogN) | O(1) | 不稳定 |
冒泡排序 | O(N2) | O(N) | O(N2) | O(1) | 稳定 |
快速排序 | O(NlogN) | O(NlogN) | O(N2) | O(NlogN) | 不稳定 |
归并排序 | O(NlogN) | O(NlogN) | O(NlogN) | O(N) | 稳定 |