内部排序算法总结(一)
内部排序算法总结(一)
注:以下所有排序代码中都是通过简单的仿函数实现通用。该部分的代码实现是以下所有排序算法的基础
//filename: stdafx.h #pragma once #include<iostream> using namespace std; #include<assert.h> template<class T> struct Great { bool operator()(const T&x, const T&y) { return x > y; } }; template<class T> void PrintArr(T *arr, size_t n) { for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; }
一.插入排序
①简单插入排序
原理:对给定数组,遍历之并将之插入到已有的空组中(其实就是遍历点之前的已存有序组)
//filename: InsertSort.h
#pragma once
#include"stdafx.h"
template<class T,template <class>class Cmp = Great > void InsertSort(T *arr, size_t n) { assert(arr); ///1532685 for (int beg = 1; beg < n; ++beg) { int end = beg - 1; while (end >= 0 && Cmp<T>()(arr[end], arr[end+1])) { swap(arr[end], arr[end + 1]); end--; } } } void TestInsertSort() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; //ShellSort<int>(arr, 10); InsertSort<int>(arr, 10); PrintArr<int>(arr, 10); }
②希尔排序(缩小增量排序)
原理:希尔排序是对插入排序的进一步优化提升。
先将整个待排序序列分割成若干个子序列,分别进行直接插入排序,待整个序列中的数据“整体有序“时,再对整个序列进行一次 直接插入排序。
//filename:InsertSort.h //希尔排序 template<class T, template <class>class Cmp = Great > void ShellSort(T*arr, size_t n) { assert(arr); int gap = n; while (gap > 1) { gap = gap / 3 + 1; for (int beg = gap; beg < n; ++beg) { int end = beg-gap; while (end >= 0 && Cmp<T>()(arr[end], arr[end + gap])) { swap(arr[end], arr[end + gap]); end-=gap; } } } } void TestShellSort() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; ShellSort<int>(arr, 10); //InsertSort<int>(arr, 10); PrintArr<int>(arr, 10); }
二.选择排序
①直接选择排序
原理:通过两层循环控制,外层确定次数为查找次数,内层找到当前最值放在最右或者左。
//filename:SelectSort.h
#include"stdafx.h" template<class T, template<class> class Cmp = Great> void SelectSort(T*arr, size_t n) { assert(arr); for (int beg = 0; beg < n; ++beg) { int flag = beg; for (int j = beg + 1; j < n; ++j) { if (Cmp<T>()(arr[flag], arr[j])) flag = j; } swap(arr[beg], arr[flag]); } } /////////////////////Test void TestSelectSort() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; //ShellSort<int>(arr, 10); SelectSort<int>(arr, 10); PrintArr<int>(arr, 10); }
②直接选择排序的优化
改进:对于直接插入排序中的每次知道到最大/小值放在合适的位置这个点,进行加强:每次找到最大,最小值,放在两侧。从而减少交换次数
//filename:SelectSort.h template<class T, template<class> class Cmp = Great> void BetSelectSort(T*arr, size_t n) { assert(arr); int beg = 0; int end = n - 1; while(beg<end) { int leftF = beg; int rightF = end; for (int j = beg; j <=end; ++j) { if (Cmp<T>()(arr[leftF], arr[j])) swap(arr[leftF], arr[j]); if (!Cmp<T>()(arr[rightF], arr[j])) swap(arr[rightF], arr[j]); } ++beg; --end; } } //Test void TestBetSelectSort() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; //ShellSort<int>(arr, 10); BetSelectSort<int>(arr, 10); PrintArr<int>(arr, 10); }
③堆排序
原理:根据最大堆最小堆可以快速求得最值的策略。利用堆进行排序。取堆顶元素放在适当位置
//filename:SelectSort.h
int Arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; template<class T, template<class> class Cmp = Great> void AdjustDown(T*arr, int n, int root) { if (n <=1) return; while (root <= (n - 2) / 2) { int child= root * 2 + 1; child = child + 1 < n ? Cmp<T>()(arr[child + 1], arr[child]) ? child + 1 : child : child; if (Cmp<T>()(arr[child], arr[root])) { swap(arr[child], arr[root]); } root = child; } } template<class T, template<class> class Cmp = Great> void heapSort(T*arr, size_t n) { //建堆 for (int i = (n - 2) / 2; i >= 0;--i) { AdjustDown(arr, n, i); } //排序 int end = n - 1; while (end > 0) { swap(arr[0], arr[end]); AdjustDown<T>(arr, end, 0); end--; } } void TestheapSort() { heapSort<int>(Arr, 10); PrintArr<int>(Arr, 10); }
三.快速排序
①冒泡排序
原理:通过N次遍历,每一次数据从头往后遍历,和相邻位置比较,如果顺序不正确就交换。直到全部有序
//filename: FastSort.h #pragma once #include"stdafx.h" template<class T, template <class>class Cmp = Great > void BubbSort(T *arr, size_t n) { for (int beg = 0; beg < n; ++beg) { for (int j = 0; j < n - beg - 1; ++j) { if (Cmp<T>()(arr[j], arr[j + 1])) { swap(arr[j], arr[j + 1]); } } } } void TestBubbSort() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; BubbSort<int>(arr, 10); PrintArr<int>(arr, 10); }
②冒泡排序优化
改进点:对冒泡排序,当某次冒泡的遍历过程中,发现遍历过程数据都是有序的,那么剩余的遍历冒泡就是非必要的。
//filename:FastSort.h
void BubbSort_B(T *arr, size_t n) { for (int beg = 0; beg < n; ++beg) { int flag = 0; for (int j = 0; j < n - beg - 1; ++j) { if (Cmp<T>()(arr[j], arr[j + 1])) { swap(arr[j], arr[j + 1]); //交换了那么设置调整开关为1 flag = 1; } } //如果遍历过程中不曾修改,那么说明序列有序,不在遍历 if (flag == 0) break; } }
③快速排序
思想原理:通过一趟排序将待排序记录分割成独立的两部分,其一部分比另一部分关键字小,则可以对这两部分数据继续递归进行排序,直到有序
template<class T, template <class>class Cmp = Great > void FastSort(T *arr, size_t n, int first, int last) { if (first < 0 || last >= n||first>last) return; int beg = first; int end = last; while (beg < end) { while (beg < end&&Cmp<T>()(arr[end], arr[beg])) { --end; } swap(arr[beg], arr[end]); while (beg < end && (Cmp<T>()(arr[end], arr[beg]))) { ++beg; } swap(arr[beg], arr[end]); } //beg == end == 中间元素 FastSort(arr, n, first, beg - 1); FastSort(arr, n, beg + 1, last); } void TestFastSort() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; FastSort<int>(Arr, 10,0,9); PrintArr<int>(Arr, 10); }
四.归并排序
归并:将两个或者两个以上的有序表组合成一个新的有序表
2-路归并排序的核心操作:将一维数组中的相邻两个有序序列归并为一个有序序列
// 归并排序中的合并算法 void Merge(int array[], int start, int mid, int end) { int temp1[10], temp2[10]; int n1, n2; n1 = mid - start + 1; n2 = end - mid; // 拷贝前半部分数组 for (int i = 0; i < n1; i++) { temp1[i] = array[start + i]; } // 拷贝后半部分数组 for (int i = 0; i < n2; i++) { temp2[i] = array[mid + i + 1]; } // 把后面的元素设置的很大 temp1[n1] = temp2[n2] = 1000; // 逐个扫描两部分数组然后放到相应的位置去 for (int k = start, i = 0, j = 0; k <= end; k++) { if (temp1[i] <= temp2[j]) { array[k] = temp1[i]; i++; } else { array[k] = temp2[j]; j++; } } } // 归并排序 void MergeSort(int array[], int start, int end) { if (start < end) { int i; i = (end + start) / 2; // 对前半部分进行排序 MergeSort(array, start, i); // 对后半部分进行排序 MergeSort(array, i + 1, end); // 合并前后两部分 Merge(array, start, i, end); } } void TestMSort2() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; MergeSort(arr, 0, 9); PrintArr<int>(arr, 10); }
②归并中的改进优化,对函数Merge进行改进,上边的方案中,只是静态的在栈上定义临时数组。大小固定。我们应当是用动态new来申请保存的
而且,对于数组的临时保存,我们只需要保存头后者尾中的一半,然后反向归并即可。
#include"stdafx.h" //[)[) template<class T,template<class>class Cmp = Great> void MerGe(int * arr, int start, int cir, int end) { assert(arr); int newLen = end - cir; int * newArr = new int[newLen]; //保存后半部分值 for (int i = cir; i < end; ++i) { newArr[i-cir] = arr[i]; } //将后半部分并入元数组 while (newLen&&cir) { //if (newArr[newLen - 1]>arr[cir - 1]) if (Cmp<T>()(newArr[newLen - 1], arr[cir - 1])) { arr[--end] = newArr[newLen - 1]; newLen--; } else { arr[--end] = arr[cir - 1]; cir--; } } //如果后半部分有剩余,那么直接加入前半部分 //否则就不处理,因为本身就是在原数组中处理的 if (newLen) { for (int i = 0; i < newLen; ++i) { arr[i] = newArr[i]; } } delete[]newArr; } // 归并排序 template<class T,template<class> class Cmp = Great> void MergeSort(int array[], int start, int end) { if (start < end) { int i; i = (end + start) / 2; // 对前半部分进行排序 MergeSort<T>(array, start, i); // 对后半部分进行排序 MergeSort<T>(array, i + 1, end); // 合并前后两部分 MerGe<T>(array, start, i, end); } } void TestMerge() { int arr[10] = { 1, 4, 7, 5, 2, 3, 6, 8, 9, 0 }; MergeSort<int>(arr, 0, 10); PrintArr<int>(arr, 10); }