在看了morewindows的白话经典算法的排序教程后,我逐个从简单到难用C++ 来实现了一遍,增加自己的理解和记忆,以下是源代码和测试结果,代码关键部分有注释:
#include <iostream>
using namespace std;
// todo:所有排序写成一个继承模板算法类,这样就不仅仅是支持int的排序了
// 1.1原始冒泡算法,排序出一个逐渐增长的数组
class BubblingSort1
{
public:
BubblingSort1(int arr[], int num): //num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
void sort()
{
for (int i=0;i<_num;i++)
{
for (int j=1;j<_num-i;j++)// 因为外循环经过一次,那么无序数组末尾就冒泡出一个数字来占用了一个位置,成为有序的一个
{
if (_arr[j-1]>_arr[j]) // 数组里相邻的数值进行比较
{
swap(_arr[j-1],_arr[j]);// C++系统文件自带的常用函数
}
}
}
}
~BubblingSort1();
private:
int* _arr;
int _num;
};
// 1.2优化后的冒泡算法
class BubblingSort2
{
public:
BubblingSort2(int arr[], int num): //num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
void sort()
{
bool isSorted = false; // 假如一次外部循环无序集合相邻的一次都没交换,那说明这个无序集合已经有序了,可以不用再继续循环了。
for (int i=0; i < _num && isSorted==false; i++)
{
isSorted = true;
for (int j=1;j<_num-i;j++)// 因为外循环经过一次,那么无序数组末尾就冒泡出一个无序中最大的数字来占用了一个位置,成为有序的一个
{
if (_arr[j-1]>_arr[j]) // 数组里相邻的数值进行比较
{
swap(_arr[j-1], _arr[j]);// C++系统文件自带的常用函数
isSorted = false;
}
}
}
}
~BubblingSort2();
private:
int* _arr;
int _num;
};
// 1.3进一步优化后的冒泡算法
class BubblingSort3
{
public:
BubblingSort3(int arr[], int num): //num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
void sort()
{
int swapPos = _num;
for (int i=0; i < _num && swapPos !=0 ; i++)
{
int tmp = swapPos;
swapPos = 0;
for (int j=1; j < tmp; j++)
{
if (_arr[j-1] > _arr[j])// 数组里相邻的数值进行比较
{
swap(_arr[j-1], _arr[j]);// C++系统文件自带的常用函数
swapPos = j;
}
}
}
}
~BubblingSort3();
private:
int* _arr;
int _num;
};
// 2.插入排序使数组增序排列
class InsertionSort
{
public:
InsertionSort(int arr[], int num): //num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
~InsertionSort();
void sort()
{
int i,j;
for (i=1;i<_num;i++)
{
int tmp = _arr[i]; // 待排序的值
if (tmp >= _arr[i-1]) // 既然比有序区域的最大值都还大,那么就别循环了,什么也不要动,待排序的值就暂时放在原位置,所以说直接插入排序效率受到数组原有排序的影响大
{
continue;
}
for (j=i-1;j>=0 && tmp < _arr[j];j--) // 有时可以在for里用&&符号可以避免在循环内部用if,使之代码更加简洁
{
_arr[j+1] = _arr[j]; // 待排序的数字更小则往后推动
}
_arr[j+1] = tmp;
}
}
private:
int* _arr;
int _num;
};
// 3.希尔排序(分组的直接插入排序,即跳着插)使之增序排列
class ShellSort
{
public:
ShellSort(int arr[], int num): //num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
~ShellSort();
void sort() // 不能误认为有3重for,复杂度就是o(n的三次方)
{
int gap;
int i,j;
for (gap = _num/2; gap >0; gap/=2 ) // 初始增量设置为数组长度的一半,直到为1就是插入排序了
{
for (i=gap;i<_num;i+=gap) // 在gap间断的集合里,从第二个数据开始插入,第一个数据不管间断多少都是_arr[0]
{
int tmp = _arr[i]; // 待排序的值
if (tmp >= _arr[i-gap]) // 既然比有序区域的最大值都还大,那么就别循环下去了,什么也不要动,待排序的值就暂时放在原位置,所以说希尔排序效率受到数组原有排序的影响大,如果已经有序,则复杂度最好,就是0(n)的一个外部循环
{
continue;
}
for (j=i-gap; j>=0 && tmp<_arr[j]; j-=gap) // 在for里用&&符号可以避免在循环内部用if,使之代码更加简洁
{
_arr[j+gap]=_arr[j]; // 往后移动
}
_arr[j+gap] = tmp; // 插入正确位置
}
}
}
private:
int* _arr;
int _num;
};
// 4.选择排序 (使之递增)直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是直接播放排序是将无序区的第一个元素直接插入
// 到有序区以形成一个更大的有序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
class SelectSort
{
public:
SelectSort(int arr[], int num): //num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
~SelectSort();
void sort()
{
int i,j;
for (i=0; i<_num; i++)
{
int minIndex = i;
for (j=i+1;j<_num;j++)
{
if (_arr[minIndex] > _arr[j])
{
minIndex = j;
}
}
swap(_arr[i], _arr[minIndex]); // 很有可能i = minIndex 的,swap 是模板函数,不止支持整数的交换
}
}
private:
int* _arr;
int _num;
};
// 5.归并排序(分治法)
class MergeSort
{
public:
MergeSort(int arr[], int num): // num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
~MergeSort();
void sort()
{
// int tmp[_num]; // 临时数组不能通过编译,因为数组在编译时要确定大小后才能分配空间,所以用指针代替
int* tmp = new int[_num]; // 一个外部递归处的临时大数组空间,避免在内部一直分配和释放小数组空间
recursiveSort(0, _num-1, tmp);
delete []tmp;
}
private:
void recursiveSort(int first, int last , int tmp[])
{
if (first < last)
{
int mid = (first + last) / 2;
recursiveSort(first, mid, tmp); // 左边有序
recursiveSort(mid+1, last, tmp);// 右边有序,注意下mid要加1.
mergeArray(first,mid,last,tmp); // 左右合并
}
}
void mergeArray(int first, int mid, int last, int tmp[])
{
int i = first,j = mid + 1;
int k=0;
while (i<=mid && j<=last)
{
if (_arr[i]<=_arr[j])
{
tmp[k++]=_arr[i++];
}
else
{
tmp[k++]=_arr[j++];
}
}
// 退出while循环了,说明要么i到达了mid,要么是j到达了last,或者同时,此时只需要将剩下的直接补到tmp数组的末尾
while(i<=mid)
{
tmp[k++]=_arr[i++];
}
while(j<=last)
{
tmp[k++]=_arr[j++];
}
// 很容易忽略的一步
for (i=0; i<k; i++)
{
_arr[first+i] = tmp[i];
}
}
private:
int* _arr;
int _num;
};
// 6.快速排序 挖坑填数+分治法(分治策略一般用上递归,即sort要调用sort自己)
class QuickSort
{
public:
QuickSort(int arr[], int num): // num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
}
~QuickSort();
private:
int addJustArray(int left, int right) // 挖坑填数的调准
{
int i = left, j = right;
int hole = _arr[left]; // 最先时候的设置的一个坑,举选首位元素作为原始坑了。
while (i < j)
{
// 从右向左找j,第一个小于坑里的数值的元素值
while(i<j && _arr[j] >= hole)
{
j--;
}
if (i<j) // 找到了符合小于坑里的数值hole的要求的j
{
_arr[i] = _arr[j]; //填数
i++;
}
// 现在轮到j是一个坑了,那么下次将填充j,从i开始向右找找大于hole值的i
while (i<j && _arr[i]<= hole)
{
i++;
}
if (i<j)
{
_arr[j] = _arr[i];
j--;
}
}
// 退出循环的时候 i==j
_arr[i] = hole; // 让i这个位置有序
return i;
}
public:
void sort(int left, int right)
{
// 常规版本
/* if (left < right)
{
int i = addJustArray(left, right);
sort(left, i-1);
sort(i+1, right);
}*/
// 调准代码后的简洁版本
if (left < right)
{
int i = left, j = right, hole = _arr[left]; // 最先时候的设置的一个坑,举选首位元素作为原始坑了。;
while (i < j)
{
// 从右向左找j,第一个小于坑里的数值的元素值
while(i<j && _arr[j] >= hole)
{
j--;
}
if (i<j) // 找到了符合小于坑里的数值hole的要求的j
{
_arr[i++] = _arr[j]; //填数
}
// 现在轮到j是一个坑了,那么下次将填充j,从i开始向右找找大于hole值的i
while (i<j && _arr[i]<= hole)
{
i++;
}
if (i<j)
{
_arr[j--] = _arr[i];
}
}
// 退出循环的时候 i==j
_arr[i] = hole; // 让i这个位置有序
sort(left, i-1);// 递归调用
sort(i+1, right);
}
}
private:
int* _arr;
int _num;
};
// 7.堆排序
class HeapSort
{
public:
HeapSort(int arr[], int num): // num 是待排序的数组的元素个数
_arr(arr),
_num(num)
{
makeMinHeap();
}
~HeapSort();
// 最小堆结果就是降序,因为最小的换到当前堆的最后去了,所以逐渐变小
void sort()
{
for (int i=_num -1; i>=1; i--)
{
swap(_arr[i], _arr[0]); //
minHeapFixDown(0,i); // 经过交换后就不符合堆的规则了,所以从根节点开始开始向下进行调准,把较为小的往上移动
}
}
private:
void minHeapFixDown(int i, int n) // 把较小的往上移动,注意第二个参数是指这个(剩下的)堆的最大索引
{
int tmp, j=2*i+1;
tmp = _arr[i];
while (j<n)
{
if ( j+1 < n && _arr[j+1] < _arr[j]) // 这里选出孩子节点中更小的j数组索引,注意需要判断j+1是否小于n,不然数组可能越界
{
j++;
}
if( tmp <= _arr[j] ) // 和i相比,看是否需要移动,若i节点的数据是最小了,则对这个i节点的处理停止。
{
break;
}
_arr[i] = _arr[j];
i=j;
j= 2*i+1;
}
_arr[i] = tmp; // 最后一步不能忘记,把tmp值放到这个临界位置,这样整个树又是一个合法的堆了
}
void makeMinHeap() // 堆化处理,建立最小堆
{
for (int i=_num/2-1; i >=0; i-- )
{
minHeapFixDown(i,_num-1);
}
}
private:
int* _arr;
int _num;
};
int main()
{
// *******************1.冒泡排序1,而2和3忽略测试了*******************
cout<<"冒泡排序测试"<<endl;
int arr1[] = {10,20,33,1,15,1,99,33};
BubblingSort1* bubblingSort1 = new BubblingSort1(arr1, sizeof(arr1)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
bubblingSort1->sort();
int i;
for (i=0; i< sizeof(arr1)/sizeof(int); i++)
{
cout<<arr1[i]<<" ";
}
cout<<endl;
cout<<endl;
// *******************2.插入排序测试*******************
cout<<"插入排序测试"<<endl;
int arr2[] = {10,20,33,1,15,1,99,33};
InsertionSort* insertSort = new InsertionSort(arr2, sizeof(arr2)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
insertSort->sort();
for (i=0; i< sizeof(arr2)/sizeof(int); i++)
{
cout<<arr2[i]<<" ";
}
cout<<endl;
cout<<endl;
// *******************3.希尔排序测试*******************
cout<<"希尔排序测试"<<endl;
int arr3[] = {10,20,33,1,15,1,99,33};
ShellSort* shellSort = new ShellSort(arr3, sizeof(arr3)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
shellSort->sort();
for (i=0; i< sizeof(arr3)/sizeof(int); i++)
{
cout<<arr3[i]<<" ";
}
cout<<endl;
cout<<endl;
// *******************4.选择排序测试*******************
cout<<"选择排序测试"<<endl;
int arr4[] = {10,20,33,1,15,1,99,33};
SelectSort* selectSort = new SelectSort(arr4, sizeof(arr4)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
selectSort->sort();
for (i=0; i< sizeof(arr4)/sizeof(int); i++)
{
cout<<arr4[i]<<" ";
}
cout<<endl;
cout<<endl;
// *******************5.归并排序测试*******************
cout<<"归并排序测试"<<endl;
int arr5[] = {10,20,33,1,15,1,99,33};
MergeSort* mergeSort = new MergeSort(arr5, sizeof(arr5)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
mergeSort->sort();
for (i=0; i< sizeof(arr5)/sizeof(int); i++)
{
cout<<arr5[i]<<" ";
}
cout<<endl;
cout<<endl;
// *******************6.快速排序测试*******************
cout<<"快速排序测试"<<endl;
int arr6[] = {10,20,33,1,15,1,99,33};
QuickSort* quickSort = new QuickSort(arr6, sizeof(arr6)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
quickSort->sort(0, sizeof(arr6)/sizeof(int) - 1 );
for (i=0; i< sizeof(arr6)/sizeof(int); i++)
{
cout<<arr6[i]<<" ";
}
cout<<endl;
cout<<endl;
// *******************7.最小堆排序*******************
cout<<"最小堆排序测试"<<endl;
int arr7[] = {10,20,33,1,15,1,99,33};
HeapSort* heapSort = new HeapSort(arr7, sizeof(arr7)/sizeof(int));// 传进去的实际是个指针,所以函数内部可以对这个指针进行操作
heapSort->sort(); // 外部调用递归
for (i=0; i< sizeof(arr7)/sizeof(int); i++)
{
cout<<arr7[i]<<" ";
}
cout<<endl;
cout<<endl;
return 0;
}
<span style="font-family:Arial;BACKGROUND-COLOR: #ffffff"></span>