您的位置:首页 > 其它

各排序算法总结(简单选择,冒泡,快速,希尔排序,堆排序)

2013-08-31 21:40 369 查看
1)冒泡排序bubble_sort
1.原理
假设对a[N]进行排序
依次比较相邻两个数,小数放前,大数放前。
*1 从头开始进行第一轮比较,则得到最后一个位置是所有数中的最大的一个数;
需要比较的次数是N-1,为什么是N-1?因为,总共是N个数,数组下标是从0开始,
如果比较最后两个数据,判断条件:if(a[N-1-1] > a[N-1]),a[N-1]就是数组的最后一个数了,
如果比较次数是N,则执行该论最后一对数据比较时就是if(a[N-1] > a
),
众所都知,a
中是没有a
,所以次数会得到一个意想不到的排序,里面会多一个垃圾值,垃圾值的产生就是a

所以此时必须用N-1。
*2 从头开始进行第二轮比较,则得到倒数第二个位置是所有数中的次最大数;
需要比较的次数是(N-1)-1,为什么是(N-1)-1?因为,解释同上。
*3 从头依次进行第三轮比较,则得到倒数第三个位置是所有数中的次次最大数;
需要比较的次数是N-1-1-1,为什么是(N-1)-1-1?因为,解释同上。
*N-1 依次从头开始比较,直至比较完。
2.代码实现过程
void bubble_sort(int a[], int N) //N是数组a的长度
{
int i, j, tmp;

for(i = 0; i < N-1; i++) //进行比比较的趟数,此时的N-1如果换成N,对程序影响不大,只不过多一次循环而已
{
for(j = 0; j < N-i-1; j++) //开始进行比较,此时的N-i-1不能换成N-i,如果写成N-i,会产生一个垃圾值,解释在*1中
{
if(a[j] > a[j+1])
{
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}

return;
}

2)简单选择排序select_sort
1.原理
假设对a
进行排序
假定选一个最小的,依次把后面的数与它进行比较,如果后面的比假定选的最小的还小,则进行交换这两个数。每进行一轮就要找出一个最小的数。
*1 第一轮 假定第一个数是“最小的”,然后依次把后面的数与这个“最小的”的进行比较,如果比这个“最小的”小,首先标记该数为“最小的”,
然后把这个“最小的”与前面的“最小的”交换值,目的是保证第一个数是后面所有数中最小的一个。
*2 第二轮 因为已经知道第一个是最小的,这时从第二个开始寻找最小的,假定第二个是最小的,然后依次把后面的数与这个“最小的”的进行比较,
如果比这个“最小的”小,首先标记该数为“最小的”,然后把这个“最小的”与前面的“最小的”交换值,目的是保证第二个数是后面所有数中最小的一个。

*N 第N轮 步骤同上。

说明 是进行N轮比较还是N-1轮比较,影响不大,原因是:该数组中共有N个数,如果对N-1个数进行了排序处理,即第N-1个数是后面中最小的一个数,
剩余的唯一的第N个数肯定是所有数中最大的,显然它应该放在最后一个位置上,没必要再进行一次循环。如果是进行N轮比较,无非是程序多执行一次循环,
其实实际上这次循环也没有执行,因为在子循环中无法满足循环条件,不能执行循环体。

2.代码实现过程
void select_sort(int a[], int N) //N是数组a的长度
{
int i, j, min, tmp;

for(i = 0; i < N; i++) //从头开始比较
{
min = i; //把第i个数当作最小的,把下标标记为min
for(j = i+1; j < N; j++) //把a[min]依次和它后面的数进行比较
{
if(a[j] < a[min]) //如果后面的某个值比a[min]小
{
min = j; //重新标记最小值下标
}
tmp = a[min]; //交换
a[min] = a[i]; //原来min = i的,如果没有找到比a[min]小的,就不进行交换(如果没找到,i还是和min一样,执行该三条语句没作用)
a[i] = tmp; //只有a[j] < a[min]成立,执行了min = j,这时才能交换(这时min的已经改为j,不再是i了,所以执行才有作用)
} //自己表达欠缺,上面三句表达不够明确,只要自己研读程序,因该能明白其中奥妙
}

return;
}

3)快速排序quick_sort
1.原理
假定对a
排序
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。
实现快速排序需要两个函数,分别是:partition()和quick_sort()

函数: int partition(int a[], int i, int j)
函数功能: 把数组分为两段,左段都是比基准小的数,但是左段的数是无序的;右段都是比基准大的数,但是右段的数是无序的。
函数大概实现过程:
*1 选取一个基准(pivot),基准一般选取第一个数a[0];

*2 定义两个变量i、j;
令j从数组尾部向左扫描,直到遇到一个数比基准小的,进行交换。目的是:使比基准小的都放在基准左边。
令i从数组开头向右扫描,直到遇到一个数比基准大的,进行交换。目的是:使比基准大的都放在基准右边。
进行多次循环后,最终i的值就是基准应该放的位置。
原因是:我们假定的基准是数组的第一个数a[0],a[0]也许既不是数组中最大的数也不是数组中最小的数,
也就是基准的位置应该在数组中的某个位置,经过多次循环后,i不断往后递增,当不满足循环条件时,i就停止了,此时i就是基准的位置
(表达欠缺,还需要自己揣摩)

函数: void quick_sort(int a[], int left, int right)
函数功能: 实现排序
函数大概实现过程:
递归的对每一段进行排序

2.代码实现过程
int partition(int a[], int i, int j)
{
int pivot; //基准

pivot = a[i];
while(i < j)
{
while(i < j && a[j] > pivot);
{
j--; //如果右边的比基准大,左移j
}
a[i] = a[j]; //如果遇到了右边的比基准小,和基准交换

while(i < j && pivot <= a[i]);
{
i++; //如果左边的基准小,右移i
}
a[j] = a[i]; //如果遇到了左边的比基准大,和基准交换
}
a[i] = pivot;

return i;
}

void quick_sort(int a[], int left, int right)
{
int pivottag;

if(left < right)
{
pivottag = partition(a, left, right);
quick_sort(a, 0, pivottag-1);
quick_sort(a, pivottag+1, right);
}

return;
}

4)希尔排序
在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点, 并且对插入下一个数没有提供任何帮助。

如果比较相隔较远距离(称为 增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除 多个元素交换。

算法先将要排序的一组数按某个增量d分成若干组,每组中 记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量 对它进行,在每组中再进行排序。

当增量减到1时,整个要排序的数被分成 一组,排序完成。
下面的函数是一个希尔排序算法的一个实现,初次取序列的一半为增量, 以后每次减半,直到增量为1。

void shell_sort(int *x, int n)

{

int h, j, k, t;

for (h=n/2; h>0; h=h/2) /*控制增量*/

{

for (j=h; j<n; j++) /*这个实际上就是上面的直接插入排序*/

{

t = *(x+j);

for (k=j-h; (k>=0 && t<*(x+k)); k-=h)

{

*(x+k+h) = *(x+k);

}

*(x+k+h) = t;

}

}

}

5)堆排序

堆排序是一种树形选择排序,是对直接选择排序的有效改进。 堆的定义如下:具有n个元素的序列(h1,h2,...,hn),当且仅当 满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2) 时称之为堆。在这里只讨论满足前者条件的堆。

由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项。完全二叉树可以 很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。 初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储顺序, 使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点 交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点 的堆,并对它们作交换,最后得到有n个节点的有序序列。

从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素 交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数 实现排序的函数。

堆排序是不稳定的。算法时间复杂度O(nlog2n)。

功能:渗透建堆 输入:数组名称(也就是数组首地址)、参与建堆元素的个数、从第几个元素开始

void sift(int *x, int n, int s)

{

int t, k, j;

t = *(x+s); /*暂存开始元素*/

k = s; /*开始元素下标*/

j = 2*k + 1; /*右子树元素下标*/

while (j<n) {

if (j<n-1 && *(x+j) < *(x+j+1)) /*判断是否满足堆的条件:满足就继续下一轮比较,否则调整。*/

{ j++; }

if (t<*(x+j)) /*调整*/

{

*(x+k) = *(x+j); k = j; /*调整后,开始元素也随之调整*/

j = 2*k + 1;

}

else /*没有需要调整了,已经是个堆了,退出循环。*/

{

break;

}

}
*(x+k) = t; /*开始元素放到它正确位置*/

}

/* 功能:堆排序 输入:数组名称(也就是数组首地址)、数组中元素个数 */

void heap_sort(int *x, int n) {

int i, k, t; int *p;

for (i=n/2-1; i>=0; i--) {

sift(x,n,i); /*初始建堆*/

}
for (k=n-1; k>=1; k--) {

t = *(x+0); /*堆顶放到最后*/

*(x+0) = *(x+k);

*(x+k) = t;

sift(x,k,0); /*剩下的数再建堆*/

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐