从最简单的排序开始,前人们一直在这不断追寻着这个看似简单的问题的最优解。
笔者也将自己对于这些不同排序算法的认识,见解记录下来。
目前记录了10种排序方法。
目录:
**以上是本篇内容。**
三:交换类
1.直接交换排序
2.快速排序
四:特殊范围类
1.桶排序
2.计数排序
3.基排序
4.归并排序
五:总结
一、插入类
1.直接插入排序
这一类最大的特色是把数据分为两类,
一类是已排序的区间,(以增序为例)
另一类是未排序的区间,取出未排序的区间内第一个数字,实行插入计划。
问题来了,数组如何直接插入?
步骤:
1.保存该数后,先循环已排序区间,找到该数的插入位置,记录下来。
2.将插入位置之后的数(从已排序区间内最后一个数开始)一个一个往后移动一位,此时就空出了插入位置。
3.将保存的数插入。
如此往复,已排序区间越来越大,直至排好。
动画演示
代码如下:
void insertSort(int *ar, int count) {
int index;
int i;
int j;
int current;
for (index = 1; index < count; index++) {
// 从index到count-1,形成“未排序”序列,且,index为其中的首元素。
// 将arr[index]插入到0到index - 1的一个合理位置,即可。
// 第一步,先找到“合理”位置
current = ar[index];
for (i = 0; i < index && current >= ar[i]; i++) {
}
// 第二步,将从i开始到index-1的数,向后平移一个位置。
for (j = index - 1; j >= i; j--) {
ar[j+1] = ar[j];
}
ar[i] = current;
}
}
2.希尔排序
希尔排序是基于直接插入排序上提出的,所以比直接插入快!
那么到底好在哪?
步骤:
1.希尔排序先将数据等间距(步长)分为各小组(一般最后一组的数不全满)
2.然后将每个组的第一个数(形成一组新数据)进行直接插入排列,再将每个组的第二数取出进行直接插入排列,如此遍历每个组。如何取?
(0 + 步长 *组数, 1 + 步长 * 组数。。。)
3.步长缩短,重分小组,执行第2步,直至步长为1。此时排序完成。
注:直接插入排序是步长为1的希尔排序
图解:
动画演示:
代码如下:
void ShellSort(int *ar, int count);
static void stepInsertSort(int *ar, const int count, const int start, const int step);
//第二步操作如下
static void stepInsertSort(int *ar, const int count, const int start, const int step) {
int index;
int i;
int j;
int current;
//代码基本和直接插入排序相同,区别在于步长和开始值
//直接插入排序是步长为1,开始为0的希尔排序
for (index = start + step; index < count; index += step) {
current = ar[index];
for (i = start; i < index && current >= ar[i]; i += step) {
}
for (j = index - step; j >= i; j -= step) {
ar[j + step] = ar[j];
}
ar[i] = current;
}
}
void ShellSort(int *ar, int count) {
int step;
int start;
//第三步操作如下
for (step = count / 2; step > 0; step /= 2) {
for (start = 0; start < step; start++) {
stepInsertSort(ar, count, start, step);
}
}
}
ps:有一个叫做鸡尾酒排序,希尔排序增加版本,每次取出最大值,和最小值。
二,选择类
什么是选择类?
笔者认为就是在排序范围内,选择每次只取出最大数(或者最小数),
同样它也运用了已排序区间,和未排序区间,不过由于知道是 “最大数” ,所以直接插入到已排序的的前面。
1.直接选择排序
步骤:
1.遍历数组(未排序区间),找出最小数,记录下标。
2.把数组第一个数和最小数交换,形成已排序区间。
3.在未排序区间,执行第1步,(即找到第二小的数),再执行第2步,如此继续第3步。直至排序成功。
动画演示:
代码如下:
void selectSort(int *ar, int count) {
int i;
int tmp;
int temp;
int j;
int minIndex;
for (i = 0; i < count - 1; i++) {
tmp = ar[i];
for (minIndex = j = i; j < count; j++) {
if (ar[j] < ar[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) {
temp = ar[i];
ar[i] = ar[minIndex];
ar[minIndex] = temp;
}
}
}
2.堆排序
其实堆就是可以用来找出最大数(最小数)的。
步骤:
1.将数组内数据依次插入完全二叉树中,建立成一个大根堆。
2.将大根堆进行调整,满足如下特征:父节点的值一定大于左右孩子(其叶子节点)的值。
3.将最后一个叶子节点的数与最大数(最上面的根节点)互换位置,并把最大数所在的叶子节点从大根堆中取出。
4.取出后,继续调整大根堆,即执行第2步,让大数上沉,小数下沉。调整结束后,再执行第3步,直至排序完成。
动画演示:
代码如下:
void heapSort(int *ar, int count);
static void adjustHeap(int *ar, const int count, int root);
//调整,让大数上沉,小数下沉
static void adjustHeap(int *ar, const int count, int root) {
int leftIndex;
int rightIndex;
int maxIndex;
int temp;
while (root < count / 2) {
leftIndex = 2*root + 1; // 完全二叉树的左右孩子与父节点的关系。
rightIndex = 2*root + 2;
maxIndex = rightIndex >= count ? leftIndex : (
ar[leftIndex] > ar[rightIndex] ? leftIndex : rightIndex);
maxIndex = ar[root] > ar[maxIndex] ? root : maxIndex;
if (maxIndex == root) {
return;
}
temp = ar[root];
ar[root] = ar[maxIndex];
ar[maxIndex] = temp;
root = maxIndex;
}
}
void heapSort(int *ar, int count) {
int root;
int temp;
for (root = count / 2 - 1 /*最后一个叶子节点的取值*/; root > 0; root--) {
//一一调整每一个父节点,实现大根堆调整
adjustHeap(ar, count, root);
}
//取大数的操作。
while (count > 0) {
adjustHeap(ar, count, 0);
temp = ar[0];
ar[0] = ar[count - 1];
ar[count - 1] = temp;
--count;
}
}
小结:
接下来还有直接交换排序,快速交换排序,计数排序,桶排序,基排序等等。
由于这一篇已经够长,笔者希望在下一篇中继续讲解。
下一篇:排序算法小全《下》