前言:这一篇文章中我们将讨论数组排序的问题,对于数据量比较大的,不能在内存中完成排序的,
必须在磁盘上完成排序类型叫作外部排序,本篇将不讨论。
对于内部排序的一些相关知识:
存在几种容易的算法以排序,如插入排序。
有一种算法叫做谢尔排序(ShellSort),它编程非常简单,以运行,并在实践中很有效。
还有一些稍微复杂的的排序算法。
任何通用的排序算法均需要次比较。
1.插入排序
最简单的排序算法之一是插入排序(insertion sort)。这是一个对少量元素进行排序的有效算法,
插入排序基于这样一种假设:位置0到位置p-1上的元素已经是经过排序的,下图显示了一个简单的数组在每一趟排序后的情况。
或者可以这样理解,插入排序的机理与打扑克牌时,整理手中牌时的做法一样,在开始摸牌时手中牌是空的,
牌面朝下放在桌子上。接着依次从桌上取一张牌插入到左手牌中正确的位置上,当牌摸完时,左手中的牌就成了有序的状态。
下面给出算法具体代码:
//插入排序
void InsertSort(int Nums[], int Length)
{
for (int i = 0; i < Length; i++)
{
int j, temp = Nums[i];//将第i个数保存起来
for (j = i - 1; Nums[j] > temp && j >= 0; j--)//依次将第i个数与前面的数比较
{
Nums[j + 1] = Nums[j];//如果前面的书比第i个数大的,则向后移动
}
Nums[j + 1] = temp; //最后将空出的位置装入上面保存的第i个数
}
}
2.谢尔排序
谢尔排序(Shellsort)的名称源于它的发明者Donald Shell,他通过比较相距一定间隔的元素来工作,
各趟比较所用的距离随着算法的进行二减小,知道只比较相邻元素的最后一趟排序位置。由于这个原因,
谢尔排序有时也叫作缩减增量排序。
上图可以看到增量分别为5,3,1的排列状态,5排序之后,从第一个元素开始相隔为5的元素变为有序,
同理3排序和1排序之后元素全部为有序了。
下面给出算法的具体代码
//谢尔排序
void ShellSort(int Nums[], int Length)
{
for (int gap = Length / 2; gap > 0; gap /= 2)
{
//根据增量gap进行插入排序
for (int i = gap; i < Length; i += gap)
{
int j, temp = Nums[i];
for (j = i; j - gap >= 0; j -= gap)
{
if (temp < Nums[j - gap])
Nums[j] = Nums[j - gap];
else
break;
}
Nums[j] = temp;
}
}
}
我们接着调整根节点在整棵数种的位置,发现根节点比左右子节点都大,不用调整,
这时我们继续将根节点14和最后一个节点1互换,那么1就变成了根节点,再调整1在根节点中的位置:
调整后如上图,根节点为10了,再与最后一个节点互换,然后调整根节点位置:
全部分离后,排列的内容为,可以看到内容变为有序了。
下面给出算法代码:
#include <stdio.h>
//这个函数调整对数组中第n个元素的位置
void HeapAdjust(int array[], int n, int length)
{
int Child;
for (int i = n; i * 2 <= length; i = Child)
{
Child = i * 2;
if (Child + 1 <= length && array[Child] < array[Child + 1])
Child++;
//如果较大的子节点大于父节点则交换位置
if (array[i] < array[Child])
{
int Temp = array[i];
array[i] = array[Child];
array[Child] = Temp;
}
else
{
break;
}
}
}
void HeapSort(int array[], int length)
{
//调整前半部分,保证了最大的值都在前半部分
for (int i = length / 2; i > 0; i--)
{
HeapAdjust(array, i, length);
}
for (int i = length-1; i > 0; i--)
{
//将最大的数移动到尾部
int Temp = array[1];
array[1] = array[i+1];
array[i+1] = Temp;
//除去尾部后,调整第一个元素位置
HeapAdjust(array, 1, i);
}
}
void HeapAdjustLittle(int array[], int num, int length)
{
//如果输入的数小于这些数,直接返回
if (num < array[1])
{
return;
}
//如果输入的数大于数组中最小的数,则赋值,然后调整堆数组
array[1] = num;
int Child;
for (int i = 1; i * 2 <= length; i = Child)
{
Child = i * 2;
if (Child + 1 <= length && array[Child] > array[Child + 1])
Child++;
//如果较小的子节点大于父节点则交换位置
if (array[i] > array[Child])
{
int Temp = array[i];
array[i] = array[Child];
array[Child] = Temp;
}
else
{
break;
}
}
}
//打印出数组内容
void PrintArray(int array[], int size)
{
printf("最大的前%d个数:\n", size);
for (int i = 0; i < size; i++)
{
printf("%3d", array[i]);
}
printf("\n");
}
int myarray[] = { 0, 1, 9, 2, 8, 3, 7, 4, 6, 5 , 10};
int main()
{
//将前十个数进行一次堆排序,并输出结果
HeapSort(myarray, sizeof(myarray) / 4 - 1);
PrintArray(myarray + 1, sizeof(myarray) / 4 - 1);
//输入数字,打印出前十个最大的数
while (1)
{
int num = 0;
scanf("%d", &num);
HeapAdjustLittle(myarray, num, sizeof(myarray) / 4 - 1);
PrintArray(myarray + 1, sizeof(myarray) / 4 - 1);
}
return 0;
}
其他几种排序算法我们在下篇将会讲解,敬请期待.....