几大典型排序算法的c++代码实现及总结
2015-09-23 16:38
741 查看
用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) | 稳定 |
相关文章推荐
- 不允许创建临时变量交换两个变量的内容
- More Effective C++ ——02_尽量使用C++风格的类型转换
- c++primer plus第十三章-虚函数virtual
- C++序列化方法 参考google protobuf
- 使用C++的类模板实现Stack类
- C++ 类访问控制
- C++ 多线程
- 设置一个临时变量交换两个变量的值
- C++ 预处理器
- 简要解读C++的动态和静态关联以及虚析构函数
- c/c++编程习惯
- Java和c++类实现单向链表
- C++ 动态内存
- More Effective C++ ——01_指针与引用的区别
- C语言高速入口系列(七)
- C++ 实现可变参数
- C语言register关键字—最快的关键字
- c,c++,java格式总结
- C++ 异常处理
- 深入解析C++编程中的纯虚函数和抽象类