您的位置:首页 > 其它

各种常规排序算法总结

2013-10-25 23:57 211 查看

1.插入排序

基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子表中的适当位置,直到全部记录插入完成为止。常规插入排序分两种,即直接插入排序和希尔排序。

1.1直接插入排序

假设待排序的记录放在数组R[0...n-1]中,排序过程的某一中间时刻,R被划分成两个子区间R[0..i-1]和R[i..n-1],其中:前一个区间是已经排好序的有序区,后一个子区间是当前未排序的部分,不妨称为无序区。直接插入排序的基本操作是将当前无序区的第一个记录R[i]插入到R[0..i-1]中适当的位置,使R[0..i]变为新的有序区-------算法时间复杂度o(n^2)。

[cpp]
view plaincopy

// 插入排序
void insertSort(int array[], int length)
{
int i, j, key;
for (i = 1; i < length; i++)
{
key = array[i];
// 把i之前大于array[i]的数据向后移动
for (j = i - 1; j >= 0 && array[j] > key; j--)
{
array[j + 1] = array[j];
}
// 在合适位置安放当前元素
array[j + 1] = key;
}
}

1.2shell排序

希尔排序也是一种插入排序方法,实际上是一种分组插入方法。基本思想是:先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量dt=1(dt<dt-1<..d2<d1),及所有记录放在同一组中进行直接插入排序为止。算法最好时间复杂度o(nlogn)。

[cpp]
view plaincopy

// shell排序
void shellSort(int array[], int length)
{
int key;
// 增量从数组长度的一半开始,每次减小一倍
for (int increment = length / 2; increment > 0; increment /= 2)
for (int i = increment; i < length; ++i)
{
key = array[i];
// 对一组增量为increment的元素进行插入排序
for (int j = i - increment; j >= 0 && array[j] > key; j -= increment)
{
array[j+increment] = array[j];
}
// 在合适位置安放当前元素
array[j+increment] = key;
}
}

2.交换排序

基本思想:两两比较待排序记录的关键字,发现两个记录的次序相反时,即进行交换,直到没有反序的记录为止。

2.1冒泡排序

基本思想是,通过无序区中相邻记录关键字间的比较和位置的交换,使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”。整个算法是从最后面的记录开始,对每两个相邻的关键字进行比较,且使关键字较小的记录换至关键字较大的记录之上,使得经过一趟冒泡排序后,关键字最小的记录到大最上端,接着,再在剩下的记录中找到关键字次小的记录,并把它换在第二个位置上,依次类推,一直到所有记录都有序为止。算法时间复杂度o(n^2)。

[cpp]
view plaincopy

//冒泡排序
void bubble_sort(int a[], int length)
{
int i, j, tag;

for(i = length - 1; i > 0; i--)//不断把前面无序区最大元素移到后面有序区
{
tag = 1;//标志位
for(j = 0; j < i; j++)//前面无序区最大元素不断向后移
{
if(a[j] > a[j + 1])
{
a[j] ^= a[j + 1];
a[j + 1] ^= a[j];
a[j] ^= a[j+1];
tag = 0;
}
}
if(tag == 1)
{
break;
}
}
}

2.2快速排序

快速排序是由冒泡排序改进而得的,它的基本思想是:在待排序的n个记录中任取一个记录(通常取第一个记录),把该记录放入适当位置后,数据序列被此记录划分成两部分。所有关键字比该记录关键字小的记录放置在前一部分,所有比它大的记录放置在后一部分,并把该记录排在这两部分的中间(称为该记录归位),这个过程称作一趟快速排序。之后对所有的两部分分别重复上述过程,直至每个部分内只有一个记录或为空为止。简单的说,每趟使表的第一个元素放入适当位置,将表一分为二,对子表按递归方法继续这种划分,直至划分的子表长为1或0.-------算法最好时间复杂度o(nlog2n)

[cpp]
view plaincopy

// 对一个给定范围的子序列选定一个枢纽元素,执行完函数之后返回分割元素所在的位置,
// 在分割元素之前的元素都小于枢纽元素,在它后面的元素都大于这个元素
int partition(int array[], int low, int high)
{
// 采用子序列的第一个元素为枢纽元素
int pivot = array[low];
while (low < high)
{
// 从后往前在后半部分中寻找第一个小于枢纽元素的元素
while (low < high && array[high] >= pivot)
--high;
// 将这个比枢纽元素小的元素交换到前半部分
array[low] = array[high];
// 从前往后在前半部分中寻找第一个大于枢纽元素的元素
while (low < high && array[low] <= pivot)
++low;
// 将这个比枢纽元素大的元素交换到后半部分
array[high] = array[low];
}
array[low] = pivot;
// 返回枢纽元素所在的位置
return low;
}
// 快速排序
void quickSort(int array[], int low, int high)
{
if (low < high)
{
int n = partition(array, low, high);
quickSort(array, low, n);
quickSort(array, n + 1, high);
}
}

3.选择排序

基本思想:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子表的最后,直到全部记录排序完毕。由于选择排序方法每一趟总是从无序区中选出全局最小(或最大)的关键字,所以适合于从大量的记录中选择一部分排序记录,如,从10000个记录中选择出关键字前10位的记录,就适合使用选择排序法。

3.1直接选择排序

直接选择排序法:第i趟排序开始时,当前有序区和无序区分别为R[0..i-1]和R[i..n-1](0≤ i≤n-1),该趟排序则是从当前无序区中选出关键字最小的记录R[k],将它与无序区的第一个记录R[i]交换,使得R[0..i]和R[i+1..n-1]分别为新的有序区和新的无序区

[cpp]
view plaincopy

//直接选择排序
void selectSort(int array[],int nLength)
{
int i,j,k;
for(i = 0; i < nLength - 1; i++)
{
k = i;
for(j = i+1; j < nLength; j++)
{
if(array[j] < array[k])
k = j;
}
if(k != i)
swap(&array[k],&array[i]);
}
}

3.2堆排序

n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):

(1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤)
若将此序列所存储的向量R[1……n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

堆的这个性质使得可以迅速定位在一个序列之中的最小(大)的元素。

堆排序算法的过程如下:1)得到当前序列的最小(大)的元素 2)把这个元素和最后一个元素进行交换,这样当前的最小(大)的元素就放在了序列的最后,而原先的最后一个元素放到了序列的最前面 3)交换可能会破坏堆序列的性质(注意此时的序列是除去已经放在最后面的元素),因此需要对序列进行调整,使之满足于上面堆的性质。重复上面的过程,直到序列调整完毕为止。

[cpp]
view plaincopy

// array是待调整的堆数组,i是待调整的数组元素的位置,length是数组的长度
void heapAdjust(int array[], int i, int nLength)
{
int nChild, nTemp;
for (nTemp = array[i]; 2 * i + 1 < nLength; i = nChild)
{
// 子结点的位置是 父结点位置 * 2 + 1
nChild = 2 * i + 1;
// 得到子结点中较大的结点
if (nChild != nLength - 1 && array[nChild + 1] > array[nChild])
++nChild;
// 如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
if (nTemp < array[nChild])
array[i] = array[nChild];
else// 否则退出循环
break;
}
// 最后把需要调整的元素值放到合适的位置
array[i] = nTemp;
}
// 堆排序算法
void heapSort(int array[], int length)
{
//建立大顶堆
for (int i = length / 2 - 1; i >= 0; --i)
heapAdjust(array, i, length);
// 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
for (int j = length - 1; j > 0; --j)
{
// 把第一个元素和当前的最后一个元素交换,
// 保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
swap(&array[0], &array[j]);
// 不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
heapAdjust(array, 0, j);
}
}

4.归并排序

归并排序是多次将两个或两个以上的有序表合并成一个有序表。最简单的归并是直接将两个有序的子表合并成一个有序的表。
两个有序表直接合并成一个有序表的算法Merge().设两个有序表存放在同一数组中相邻的位置上:R[low..mid],R[mid+1..high],先将他们合并到一个局部的暂存数组R1中,待合并完成后将R1复制到R中。每次从两个段中取出一个记录进行关键字的比较,将较小者放入R1中,最后将各段中余下的部分直接复制到R1中。

[cpp]
view plaincopy

void merge(int array[],int start,int mid,int end)
{
int i,j,k;
i = start;
j = mid + 1;
k = 0;
int * p = (int *)malloc((end - start + 1) * sizeof(int));

while((i <= mid) && (j <= end))
{
if(array[i] < array[j])
p[k++] = array[i++];
else
p[k++] = array[j++];
}
while(i <= mid)
p[k++] = array[i++];
while(j <= end)
p[k++] = array[j++];

k = 0;
while(start <= end)
array[start++] = p[k++];

free(p);
}
//归并排序
void mergeSort(int array[],int start,int end)
{
if(start < end)
{
int i = (start + end) / 2;
mergeSort(array,start,i);
mergeSort(array,i+1,end);
merge(array,start,i,end);
}
}

5.计数排序和基数排序

在之前介绍过的排序方法,都是属于[比较性]的排序法,也就是每次排序时,都是比较整个关键字的大小以进行排序。计数排序是一个非基于比较的线性时间排序算法。

计数排序算法的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。

[cpp]
view plaincopy

//计数排序
//nMaxValue为数组array中最大值
void countingSort(int array[],int nMaxValue,int nLength)
{
int i;
int *pTemp = (int *)malloc((nMaxValue + 1) * sizeof(int));
int *pResult = (int *)malloc((nLength + 1) * sizeof(int));

for(i = 0; i <= nMaxValue; i++)
pTemp[i] = 0;
// 此过程记录每一个元素的个数
for(i = 0; i < nLength; i++)
pTemp[array[i]]++;
// 此过程确定小于元素x的元素的个数
for(i = 1; i <= nMaxValue; i++)
pTemp[i] += pTemp[i-1];
for(i = nLength-1; i >= 0; i--)
{
pResult[pTemp[array[i]]] = array[i];
pTemp[array[i]]--;
}
for(i = 1; i <= nLength; i++)
array[i-1] = pResult[i];
}

基数排序的主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次稳定排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.

比如这样一个数列排序: 342 58 576 356, 以下描述演示了具体的排序过程

第一次排序(个位):

3 4 2

5 7 6

3 5 6

0 5 8

第二次排序(十位):

3 4 2

3 5 6

0 5 8

5 7 6

第三次排序(百位):

0 5 8

3 4 2

3 5 6

5 7 6

结果: 58 342 356 576

[cpp]
view plaincopy

//基数排序
//nMaxBit为数字中的最高位数,如最大数为235,则nMaxBit为3
void radixSort(int *array, int nLength ,int nMaxBit)
{
int i,j;
int temp[10];
int nTmp;
int nRadix = 1;
int *pResult = (int *)malloc(nLength * sizeof(int));

for(j = 1; j <= nMaxBit; j++)
{
// 以下和计数排序一样
for(i = 0; i < 10; i++)
temp[i] = 0;
// 此过程记录每一个元素的个数
for(i = 0; i < nLength; i++)
{
nTmp = (array[i] / nRadix) % 10;
temp[nTmp]++;
}
// 此过程确定小于元素x的元素的个数
for(i = 1; i < 10; i++)
temp[i] += temp[i-1];
for(i = nLength-1; i >= 0; i--)
{
nTmp = (array[i] / nRadix) % 10;
temp[nTmp]--;
pResult[temp[nTmp]] = array[i];
}
for(i = 0; i < nLength; i++)
array[i] = pResult[i];
nRadix *= 10;
}
}
http://blog.csdn.net/qll125596718/article/details/6901539#t1 总结的不错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: