插入排序
- 思想:从数组第二个元素开始,每次选取一个作为待排元素,与已排好的元素依次比较。
- 如果待排元素比已排元素小,则已排元素依次后移。最后把待排元素插入到合适的位置。
- 插入排序的平均复杂度为 。
插入排序函数模板
template<typename T>
void InsertSort(T* list, int n) // 选择排序函数的实现
{
T temp; // 定义一个临时T类型变量
for (int i = 1;i < n;i++)
{
temp = list[i]; // 临时变量赋值为下一个要排序的新元素
int j = i-1; // 令下标j为i-1,前i-1个元素已经排好序
while (temp < list[j]) // 比较新元素与前i-1个元素大小
{
list[j + 1] = list[j]; // 若新元素值小于前面排好序的数,前面的数依次后移
j--; // j前移
}
list[j + 1] = temp;
}
}
冒泡排序
- 思想:每一趟从头到尾,一次比较相邻两个元素,上面(前面)比下面(后面)大,则交换两个元素的位置。
- 每一趟将较大的值“下沉”,较小的值“冒泡上升”,最终固定较大值的位置。总共有n-1趟冒泡排序。
- 冒泡排序的平均复杂度为 。
冒泡排序函数模板
template<typename T>
void BubbleSort(T* list, int n) // 冒泡排序函数的实现
{
for (int i = 0;i < n - 1;i++) // 总共有n-1趟
{
for (int j = 1;j < n - i;j++) // 遍历数组
{
if (list[j - 1] > list[j]) // 依次比较相邻两个数的值,较大数下沉,较小的数上冒
mySwap(list[j - 1], list[j]);
}
}
}
选择排序
- 思想:每次从未排序的元素中挑选一个最小值与前面待排位置的元素交换。
- 选择排序的平均时间复杂度为 。
选择排序函数模板
template<typename T>
void SelectSort(T *a, int n) // 选择排序函数模板
{
for (int i = 0;i < n;i++) // 总共有size趟
{
int minIndex = i; // 每趟从剩下的元素中寻找最小的元素,初始化第i趟最小元素下标为i
for (int j = i + 1;j < n;j++)
{
if (a[j] < a[minIndex])
minIndex = j; // 更新最小元素下标
}
mySwap(a[i], a[minIndex]); // 交换第i个元素与第i趟最小元素的位置
}
}
希尔排序(增量排序)
- 每次将一定间隔(增量)位置上的元素(看作子数组)在相应位置上进行插入排序。增量依次减小。
- 当增量为1时,数组基本有序,再进行一次直接插入排序完成整个排序过程。
- 希尔排序的最坏时间复杂度为 ,使用Hibbard增量的希尔排序最坏时间为 。
- Sedgewick提出的几种增量序列,最坏时间为 ,平均时间猜测为 。
- 希尔排序适用于大量输入数据的排序,在实际运行时比堆排序快。
希尔排序函数模板
template<typename T>
void ShellSort(T* list, int n)
{
int increment = n / 2; // 定义初始增量为size/2
T temp; // 临时T类型变量
while (increment > 0) // 增量大于0时执行循环
{
for (int i = increment;i < n;i++)
{
temp = list[i];
int j = i - increment; // j为前面已排好的一定间隔的数组
while (temp < list[j] && j >= 0) // 临时变量小于前面排好的元素,则已排好元素依次后移
{
list[j + increment] = list[j];
j -= increment; // j按增量前移
}
list[j + increment] = temp;
}
increment /= 2; // 增量变为原来的一半
}
}
堆排序
- 首先从待排数组中构建一个大堆,交换堆顶元素与数组(堆)末尾元素(即最大的数移到数组最末尾)。
- 然后,将新的堆顶元素执行下滤操作,构建一个包含前n-1项元素的新大堆,再交换堆顶与堆末尾元素。
- 如此反复进行n-1次,最终实现从小到大的排序。
- 堆排序理论平均和最坏时间复杂度都为 。但在实践中比希尔排序慢。
堆排序函数模板
template<typename T>
void HeapDown(T* list, int i, int N) // 构建(大顶)堆的辅助函数,"下滤"地调整堆
{
T temp = list[i]; // 临时变量保存节点i的值,将节点i看作为"空穴"
int BigChild; // 这里创建一个"大孩子"表示数值较大的孩子
while (2*i + 1 < N) // 当i("空穴")左孩子的下标小于数组(堆)大小时执行"下滤"操作
{
if (2*i + 2 >= N) // 当i("空穴")右孩子(2*i + 2 >= N),右孩子不存在,"大孩子"直接为其左孩子(根据堆为完全二叉树这里只可能是左孩子)
BigChild = 2 * i + 1;
else // 否则i("空穴")有左(2 * i + 1)右(2 * i + 2)两个孩子
BigChild = list[2 * i + 1] > list[2 * i + 2] ? 2 * i + 1 : 2 * i + 2; // "大孩子"为值比较大的孩子
if (list[BigChild] > temp) // 如果"大孩子"的值大于临时变量
{
list[i] = list[BigChild]; // 填充i("空穴")为其大孩子的值
i = BigChild; // 更新i的值("空穴"下移),赋值为其大孩子
}
else
break; // 否则,i("空穴")的孩子没有比它大的,则"空穴"停止移动
}
list[i] = temp; // 在相应的"空穴"位置,填充最初的节点i的值
}
template<class T>
void HeapSort(T* list, int n) // 堆排序函数
{
// 从数组中构建堆
for (int i = n / 2 - 1; i >= 0;i--)
{
HeapDown(list, i, n); // 从最末尾的非树叶节点(size/2)开始执行"下滤"调整操作,直至调整至根节点,创建了一个大堆
}
// 从构建好的队里,执行"DeleteMax"操作
for (int i = n - 1;i > 0;i--)
{
mySwap(list[0], list[i]); // 将堆顶元素(数组最大值)与堆尾元素交换位置
HeapDown(list, 0, i); // "下滤"调整除去最大值的堆结构(只有根节点改变,故只需调整元素下标为0的"非页节点"),以便后续操作
}
}
归并排序
- 典型利用分治递归策略的例子。首先,考虑两个有序的子数组,很容易将它们合并成一个有序的子数组。
- 合并操作:每次选取两个子数组中的较小的数到合并数组中,被选取的子数组下标后移,直到一个子数组被选完,另一个子数组剩下的元素依次移动到合并数组的末尾。整个合并数组有序排列。
- 分治递归:将整个数组划分为两路,两路划分为四路……直到每路都只包含一个元素(一个元素本身就有序)。然后依次从低向上进行合并,每次合并之后的子数组也有序,最终合并到一个有序的数组。
- 归并排序的最坏时间复杂度为 。
合并操作函数模板
template<typename T>
void Merge(T* A, int p, int q, int r) // 归并排序辅助函数,用于对左右有序的数组进行合并
{
T *templist = new T[12];
int i = p, j = p, k = q + 1; // i为临时数组的下标,j为左部分起始下标,k为右半部分起始下标
while (j <= q && k <= r) // 左右两部分下标分别再其范围内时
{
if (A[j] < A[k]) // 比较左右两部分的元素,当左部分元素小于右部分元素时
{
templist[i++] = A[j]; // 赋值给临时数组
j++; // 下标后移
}
else
{
templist[i++] = A[k];
k++;
}
}
while (j <= q) // 左半部分剩余元素依次复制到临时数组中
{
templist[i++] = A[j];
j++;
}
while (k <= r) // 若右边有剩余元素
{
templist[i++] = A[k]; // 依次把剩余元素填到templist后面
k++;
}
for (i = p;i <= r;i++)
{
A[i] = templist[i]; // 临时数组中的元素依次赋值到A中
}
}
合并排序驱动函数模板
template<typename T>
void MergeSort(T* list, int p, int r) // 合并排序函数模板实现
{
if (p < r)
{
int mid = (p + r) / 2; // 中点q
MergeSort(list, p, mid); // 递归地归并排序左半部分
MergeSort(list, mid + 1, r); // 递归地归并排序右半部分
Merge(list, p, mid, r); // 递归地左右两部分合并
}
}
快速排序
- 同样采用分治递归的策略。首先从数组中选取一个数作为“枢纽元”。
- 分别从后往前、从前往后遍历数组,依次将从后往前的比“枢纽元”大的元素与从前往后的比“枢纽元”小的元素调换位置。
- 当前后遍历的下标有交叉时停止遍历,在相应位置填充“枢纽元”。此时,数组中在“枢纽元”之前的都比它小,之后的元素都比它大。完成了一次快速排序。
- 以“枢纽元”为界划分前后两个子数组,递归地在子数组中进行快速排序。递归终止的条件为某子数组“开始”位置不小于“终止”位置。
- 快速排序的平均时间复杂度为 ,最坏为 ,但在实践中,快速排序时目前已知的最快的排序算法。
快速排序函数模板
template<typename T>
void QuickSort(T* A, int start, int end) // 快速排序函数模板
{
int i = start + 1, j = end; // 以开头为"枢纽元"
while (i < j)
{
while (A[j] > A[start]) // 从后往前,寻找一个比"枢纽元"小的数
j--;
while (A[i] < A[start]) // 从前往后,寻找一个比"枢纽元"大的数
i++;
if(i < j)
mySwap(A[i], A[j]); // 交换两个数的位置
}
mySwap(A[start], A[j]); // 交换"枢纽元"与最后一个比它小的值的位置
if (--j > start)
QuickSort(A, start, j); // 递归地对前面都比"枢纽元"小的元素进行快速排序
if (i < end)
QuickSort(A, i, end); // 递归地对前面都比"枢纽元"大的元素进行快速排序
}
附排序综合实例(基于数组类模板)C/C++
#include<iostream>
#include<cassert>
using namespace std;
template<typename T> // 数组类模板定义
class Array { // 创建一个静态数组类
public:
Array(int sz); // 构造函数
Array(const Array<T> &a); // 复制构造函数
~Array(); // 析构函数
int getSize(); // 返回数组大小
Array<T>& operator = (const Array<T> &rhs); // 重载"="运算符
T & operator [] (int i); // 重载"[]"下标操作符
const T & operator [] (int i) const; // 重载"[]"常函数
operator T*(); // 重载到T*类型的转换
void insertSort(); // 插入排序
void bubbleSort(); // 冒泡排序
void selectSort(); // 选择排序
void shellSort(); // 希尔排序
void heapSort(); // 堆排序
void mergeSort(); // 归并排序
void quickSort(); // 快速排序
template<typename T> // 友元函数的声明,前面要加上类模板声明
friend void print(Array<T> a); // 打印数组
private:
T* list; // 数组元素的头指针
int size; // 数组大小
};
// 实现类
template<typename T>
Array<T>::Array(int sz) // 构造函数的实现
{
assert(sz >= 0);
size = sz;
list = new T[size]; // 动态分配size个T类型的元素空间
}
template<typename T>
Array<T>::Array(const Array<T> &a) // 复制构造函数的实现
{
this->size = a.size; // 数组大小不变
this->list = new T [size]; // 动态分配size个T类型的新空间
for (int i = 0;i < size;i++) // 深层复制构造
{
this->list[i] = a.list[i];
}
}
template<typename T>
Array<T>::~Array() // 析构函数
{
delete[] list;
}
template<typename T>
int Array<T>::getSize()
{
return size;
}
template<typename T>
T & Array<T>::operator [] (int i) // 重载"[]"下标操作符
{
assert(i >= 0 && i < size);
return list[i];
}
template<typename T>
const T & Array<T>:: operator [] (int i) const // 重载"[]"常函数
{
assert(i >= 0 && i < size);
return list[i];
}
template<typename T>
Array<T>& Array<T>::operator = (const Array<T> &rhs) // 重载"="运算符
{
if (&rhs != this)
{
if (size != rhs.size)
{
delete[] list; // 删除数组原有内存
size = rhs.size; // 动态分配新内存
list = new T[size];
}
}
for (int i = 0;i < size;i++)
{
list[i] = rhs.list[i];
}
}
template<typename T>
Array<T>::operator T*() // 重载到T*类型的转换
{
return list;
}
template<typename T>
void InsertSort(T* list, int n) // 选择排序函数的实现
{
T temp; // 定义一个临时T类型变量
for (int i = 1;i < n;i++)
{
temp = list[i]; // 临时变量赋值为下一个要排序的新元素
int j = i-1; // 令下标j为i-1,前i-1个元素已经排好序
while (temp < list[j]) // 比较新元素与前i-1个元素大小
{
list[j + 1] = list[j]; // 若新元素值小于前面排好序的数,前面的数依次后移
j--; // j前移
}
list[j + 1] = temp;
}
}
template<typename T>
void Array<T>::insertSort() // 类成员选择排序函数的实现
{
InsertSort(list, size);
}
template<typename T> // 创建两个数的交换函数模板
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void BubbleSort(T* list, int n) // 冒泡排序函数的实现
{
for (int i = 0;i < n - 1;i++) // 总共有n-1趟
{
for (int j = 1;j < n - i;j++) // 遍历数组
{
if (list[j - 1] > list[j]) // 依次比较相邻两个数的值,较大数下沉,较小的数上冒
mySwap(list[j - 1], list[j]);
}
}
}
template<typename T>
void Array<T>::bubbleSort() // 冒泡排序函数的实现
{
BubbleSort(list, size);
}
template<typename T>
void SelectSort(T *a, int n) // 选择排序函数模板
{
for (int i = 0;i < n;i++) // 总共有size趟
{
int minIndex = i; // 每趟从剩下的元素中寻找最小的元素,初始化第i趟最小元素下标为i
for (int j = i + 1;j < n;j++)
{
if (a[j] < a[minIndex])
minIndex = j; // 更新最小元素下标
}
mySwap(a[i], a[minIndex]); // 交换第i个元素与第i趟最小元素的位置
}
}
template<typename T>
void Array<T>::selectSort() // 选择排序函数的类成员实现
{
SelectSort(list, size);
}
template<typename T>
void ShellSort(T* list, int n)
{
int increment = n / 2; // 定义初始增量为size/2
T temp; // 临时T类型变量
while (increment > 0) // 增量大于0时执行循环
{
for (int i = increment;i < n;i++)
{
temp = list[i];
int j = i - increment; // j为前面已排好的一定间隔的数组
while (temp < list[j] && j >= 0) // 临时变量小于前面排好的元素,则已排好元素依次后移
{
list[j + increment] = list[j];
j -= increment; // j按增量前移
}
list[j + increment] = temp;
}
increment /= 2; // 增量变为原来的一半
}
}
template<typename T>
void Array<T>::shellSort()
{
ShellSort(list, size);
}
template<typename T>
void HeapDown(T* list, int i, int N) // 构建(大顶)堆的辅助函数,"下滤"地调整堆
{
T temp = list[i]; // 临时变量保存节点i的值,将节点i看作为"空穴"
int BigChild; // 这里创建一个"大孩子"表示数值较大的孩子
while (2*i + 1 < N) // 当i("空穴")左孩子的下标小于数组(堆)大小时执行"下滤"操作
{
if (2*i + 2 >= N) // 当i("空穴")右孩子(2*i + 2 >= N),右孩子不存在,"大孩子"直接为其左孩子(根据堆为完全二叉树这里只可能是左孩子)
BigChild = 2 * i + 1;
else // 否则i("空穴")有左(2 * i + 1)右(2 * i + 2)两个孩子
BigChild = list[2 * i + 1] > list[2 * i + 2] ? 2 * i + 1 : 2 * i + 2; // "大孩子"为值比较大的孩子
if (list[BigChild] > temp) // 如果"大孩子"的值大于临时变量
{
list[i] = list[BigChild]; // 填充i("空穴")为其大孩子的值
i = BigChild; // 更新i的值("空穴"下移),赋值为其大孩子
}
else
break; // 否则,i("空穴")的孩子没有比它大的,则"空穴"停止移动
}
list[i] = temp; // 在相应的"空穴"位置,填充最初的节点i的值
}
template<class T>
void HeapSort(T* list, int n) // 堆排序函数
{
// 从数组中构建堆
for (int i = n / 2 - 1; i >= 0;i--)
{
HeapDown(list, i, n); // 从最末尾的非树叶节点(size/2)开始执行"下滤"调整操作,直至调整至根节点,创建了一个大堆
}
// 从构建好的队里,执行"DeleteMax"操作
for (int i = n - 1;i > 0;i--)
{
mySwap(list[0], list[i]); // 将堆顶元素(数组最大值)与堆尾元素交换位置
HeapDown(list, 0, i); // "下滤"调整除去最大值的堆结构(只有根节点改变,故只需调整元素下标为0的"非页节点"),以便后续操作
}
}
template<class T>
void Array<T>::heapSort() // 堆排序函数
{
HeapSort(list, size);
}
template<typename T>
void Merge(T* A, int p, int q, int r) // 归并排序辅助函数,用于对左右有序的数组进行合并
{
T *templist = new T[12];
int i = p, j = p, k = q + 1; // i为临时数组的下标,j为左部分起始下标,k为右半部分起始下标
while (j <= q && k <= r) // 左右两部分下标分别再其范围内时
{
if (A[j] < A[k]) // 比较左右两部分的元素,当左部分元素小于右部分元素时
{
templist[i++] = A[j]; // 赋值给临时数组
j++; // 下标后移
}
else
{
templist[i++] = A[k];
k++;
}
}
while (j <= q) // 左半部分剩余元素依次复制到临时数组中
{
templist[i++] = A[j];
j++;
}
while (k <= r) // 若右边有剩余元素
{
templist[i++] = A[k]; // 依次把剩余元素填到templist后面
k++;
}
for (i = p;i <= r;i++)
{
A[i] = templist[i]; // 临时数组中的元素依次赋值到A中
}
}
template<typename T>
void MergeSort(T* list, int p, int r) // 合并排序函数模板实现
{
if (p < r)
{
int mid = (p + r) / 2; // 中点q
MergeSort(list, p, mid); // 递归地归并排序左半部分
MergeSort(list, mid + 1, r); // 递归地归并排序右半部分
Merge(list, p, mid, r); // 递归地左右两部分合并
}
}
template<typename T>
void Array<T>::mergeSort() // 类合并函数的实现
{
MergeSort(list, 0, size-1); // 调用MergeSort函数模板
}
template<typename T>
void QuickSort(T* A, int start, int end) // 快速排序函数模板
{
int i = start + 1, j = end; // 以开头为"枢纽元"
while (i < j)
{
while (A[j] > A[start]) // 从后往前,寻找一个比"枢纽元"小的数
j--;
while (A[i] < A[start]) // 从前往后,寻找一个比"枢纽元"大的数
i++;
if(i < j)
mySwap(A[i], A[j]); // 交换两个数的位置
}
mySwap(A[start], A[j]); // 交换"枢纽元"与最后一个比它小的值的位置
if (--j > start)
QuickSort(A, start, j); // 递归地对前面都比"枢纽元"小的元素进行快速排序
if (i < end)
QuickSort(A, i, end); // 递归地对前面都比"枢纽元"大的元素进行快速排序
}
template<typename T>
void Array<T>::quickSort() // 类成员快速排序函数实现
{
QuickSort(list, 0, size - 1); // 调用快速排序模板
}
template<typename T>
void print(Array<T> a)
{
cout << "The array contents:" << endl;
for (int i = 0;i < a.size;i++)
{
cout << a[i] << " ";
}
cout << endl;
}
int main()
{
const int rawdata[] = { 19, 13, 9, 8, 23, 39, 4, 2, 75, 100, 43, 27};
Array<int> myArray(sizeof(rawdata)/sizeof(int));
for (int i = 0;i < sizeof(rawdata) / sizeof(int);i++)
{
myArray[i] = rawdata[i]; // 将原始数据填充到Array类模板的对象myArray中
}
print(myArray); // 打印原始数组
Array<int> copyMyArray1(myArray); // 调用复制构造函数创建一个myArray副本1
cout << "After insert sort, ";
copyMyArray1.insertSort(); // 使用插入排序对副本进行选择排序
print(copyMyArray1);
Array<int> copyMyArray2 = myArray; // 使用重载运算符"="创建myArray副本2
cout << "After bubble sort, ";
copyMyArray2.bubbleSort(); // 使用冒泡排序对副本2进行排序
print(copyMyArray2);
Array<int> copyMyArray3 = myArray; // 创建副本
cout << "After selection sort, The array contents: \n";
int* a = (int*)copyMyArray3; // 将副本转换为int类型的指针(数组首元素指针)
SelectSort(a, copyMyArray3.getSize()); // 利用重载之后的选择排序函数进行排序
for (int i = 0;i < copyMyArray3.getSize();i++)
{
cout << *(a+i) << " "; // 打印排序后数组
}
cout << endl;
cout << "After selection sort, "; // 使用类的成员函数SelectSort对副本对象copyMyArray3进行选择排序
copyMyArray3.selectSort();
print(copyMyArray3);
Array<int> copyMyArray4 = myArray;
cout << "After shell's sort, "; // 使用希尔排序对副本4进行排序
copyMyArray4.shellSort();
print(copyMyArray4);
Array<int> copyMyArray5 = myArray;
cout << "After heap sort, "; // 使用堆排序对副本5进行排序
copyMyArray5.heapSort();
print(copyMyArray5);
Array<int> copyMyArray6 = myArray;
cout << "After merge sort, "; // 使用堆排序对副本6进行排序
copyMyArray6.mergeSort();
print(copyMyArray6);
Array<int> copyMyArray7 = myArray;
cout << "After quick sort, "; // 使用快速排序对副本7进行排序
copyMyArray7.quickSort();
print(copyMyArray7);
system("pause");
return 0;
}
- 操作运行结果
The array contents:
19 13 9 8 23 39 4 2 75 100 43 27
After insert sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After bubble sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After selection sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After selection sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After shell's sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After heap sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After merge sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
After quick sort, The array contents:
2 4 8 9 13 19 23 27 39 43 75 100
请按任意键继续. . .