一.插入排序(O(N2))
1.1算法分析
插入排序由N-1趟排序组成,对于p=1趟到p=N-1趟,插入排序保证从位置0到位置p上的元素为已排序状态。插入排序利用了这样的事实:位置0到
位置p-1上的元素是已经排序过的。
关于插入排序的定理:N个互异数的数组的平均逆序数式N(N-1)/4,这也是排序过程中需要交换的总次数。
1.2代码示例
实例代码如下所示:
//以下为插入排序算法示例
#include<iostream>
using namespace std;
#define M 6
void InsertionSort(int Int[], int);
int main()
{
int i;
int Integer[M] = { 34,8,64,51,32,21 };
cout << "排序前:";
for (int i = 0; i < M; i++)
cout<<Integer[i]<<" ";
cout<<endl;
InsertionSort(Integer,M);
cout << "排序后:";
for (int i = 0; i < M; i++)
cout<<Integer[i] << " " ;
getchar();
return 0;
}
void InsertionSort(int Int[], int N)//排序算法函数主体
{
int j, p;
int tmp;
for (p = 1; p < M; p++)
{
tmp = Int[p];
for (j = p; j > 0 && Int[j - 1] > tmp; j--)
Int[j] = Int[j - 1];
Int[j] = tmp;
}
}
1.3插入排序运行结果截图
二.希尔排序(O(N2))
2.1算法分析
希尔排序(ShellSort)的名称源于它的发明者Donald Shell,该算法通过比较相距一定的间隔的元素来工作;各趟比较所用的距离随着算法的
进行而减小,直到只比较相邻元素的最后一趟排序位置。因此,有时也叫缩小增量排序。
希尔排序使用一个增量序列,h1,h2……..hk ,且经过增量hk排序一趟的文件,称为是hk-排序的。希尔排序的一个重要性质是,一个hk-排序的文件(此后将是hk-1-排序的)保持它的hk-排序性能。通俗的说就是,后面的排序不会打乱前面具有的排序性。
增量序列的一种流行(但是不太好)的选择是使用Shell建议的序列:ht=[ N/2 ]和hk = [ hk+1/2 ]。
关于希尔排序的定理:—————–>
*.使用希尔增量(hk +1= [ hk+1/2 ])时希尔排序的最坏情形运行时间为Θ(N^2).
*.使用Hibbard增量(hk+2=2hk+1+1)的希尔排序的最坏情形运行时间为Θ(N^3/2).
2.2代码示例
//以下为希尔排序算法示例
#include<iostream>
using namespace std;
#define M 13
void ShellSort(int Int[], int);
int main()
{ int i;
int Integer[M] = { 81,94,11,96,12,35,17,95,28,58,41,75,15};
cout << "排序前:";
for (int i = 0; i < M; i++)
cout<<Integer[i]<<" ";
cout<<endl;
ShellSort(Integer,M);
cout << "排序后:";
for (int i = 0; i < M; i++)
cout<<Integer[i] << " " ;
getchar();
return 0;
}
void ShellSort(int Int[], int N)
{
int i, j, Increment;
int tmp;
for (Increment = N/2; Increment > 0; Increment /= 2)//流行的希尔增量为N/2
for(i = Increment; i < N; i++)
{
tmp = Int[i];
for (j = i; j >= Increment; j -= Increment)
if (tmp < Int[j - Increment])
Int[j] = Int[j - Increment];
else
break;
Int[j] = tmp;
}
}
2.3希尔排序运行结果截图
三.堆排序(2NlogN-O(N))
3.1算法分析
关于堆:
*.堆(heap)是一颗被完全填满的二叉树,有可能的例外是在底层,底层的元素从左到右填入,这样的树也称为完全二叉树。
*.堆的结构:完全二叉树由于其规律性,可以用数组来实现而不用指针。对于数组中任一位置i上的元素,其左儿子在位置2i上,右
儿子在左儿子后的单元(2i+1)中,它的父亲则在位置[i/2]上。
*.堆具有堆序性:根上的元素应该是最小/大元,且我们考虑任意子树也应该是一个堆,那么任意节点就应该小于/大于它的所有后裔。
在介绍堆的章节,提到过堆(即优先队列)可以用于花费O(NlogN)时间的排序(该方法需要再开辟一个数组),是目前为止的最佳的大O运行时间。
在实例实现过程中,将使用一个(Max)堆,但由于速度的原因避免了实际的ADT。按照通常习惯,每一件事都是在数组中完成的。第一步以线性
时间建立一个堆。然后通过将堆中的最后元素与第一个元素交换,缩减堆的大小并进行下滤,来执行N-1次DeleteMax操作。当算法终止时,数组
则以所排的顺序包含这些元素。
3.2代码示例
#include<iostream>
using namespace std;
#define LeftChild(i) (2*(i)+1)
#define M 7
void PercDown(int Int[], int i, int N);
void HeapSort(int Int[], int N);
int Swap(int *p, int *q);
int main()
{
int Integer[M] = { 31,41,59,26,53,97,58 };
cout << "排序前:";
for (int i = 0; i < M; i++)
cout<<Integer[i]<<" ";
cout << endl;
HeapSort(Integer, M);
cout << "排序后:";
for (int i = 0; i < M; i++)
cout << Integer[i] << " ";
cout << endl;
getchar();
}
void PercDown(int Int[], int i, int N)//下滤操作,堆的空穴向下移动
{
int Child;
int tmp;
for (tmp = Int[i]; LeftChild(i) < N; i = Child)
{
Child = LeftChild(i);
if (Child != N - 1 && Int[Child + 1] > Int[Child])
Child++;
if (tmp < Int[Child])
Int[i] = Int[Child];
else
break;
}
Int[i] = tmp;
}
void HeapSort(int Int[], int N)
{
int i;
for (i = N/2; i >= 0; i--) //Build Heap操作
PercDown(Int, i, N);
for (i = N - 1; i > 0; i--)
{
Swap(&Int[0], &Int[i]);//Delete Max操作
PercDown(Int, 0, i);
}
}
int Swap(int *p, int *q)//swap函数注意形参传入不要出错
{
int temp;
temp = *p;
*p = *q;
*q = temp;
return 0;
}
3.3堆排序运行结果截图
四.归并排序(O(NlogN))
4.1算法分析
*.归并排序以O(NlogN)最坏情形运行时间运行,是递归算法的一个很好的实例。
*.这个算法的基本操作是合并两个已排序的表。因为这两个表示已经排序好的,所以若将输出放到第三个表中时则该算法可以通过对输入数据一趟
排序来完成。
*.该算法是经典的分治策略,它将问题分成一些小问题然后递归求解,然后将各个阶段解得的答案修补到一起。
*.虽然归并排序的运行时间是O(NlogN),但是它很难用于主存排序,主要问题在于合并两个排序的表需要线性附加内存,在整个算法中还要花
费将数据拷贝到临时组再拷贝会来这样这样一些附加的工作,其结果严重放慢了排序的速度。
4.2代码示例
#include<stdio.h>
#include<stdlib.h>
#include<time.h> //突然想看看运行时间如何,就加了time测试,多组数据实验表明归
// 并排序较快而且非常稳定(10ms以内),插入排序相对比较慢而且逆序数不一样还会更慢
#define M 13
void Msort(int Integer[], int TmpArray[], int left, int right);
void MergeSort(int Integer[], int N);
void Merge(int Integer[], int TmpArray[], int lpos, int rpos, int rightend);
int main()
{
int i;
time_t time_count_begin;
time_t time_count;
time_count_begin = clock();
int Integer[M] = { 81,94,11,96,12,35,17,95,28,58,41,75,15 };
printf("排序前:");
for (int i = 0; i < M; i++)
printf("%d ", Integer[i]);
printf("\n");
MergeSort(Integer, M);
printf("排序后:");
for (int i = 0; i < M; i++)
printf("%d ", Integer[i]);
time_count = (clock()-time_count_begin)*1000/CLOCKS_PER_SEC;
printf("\n所需时间为%d毫秒 ", time_count);
getchar();
return 0;
}
void Msort(int Integer[], int TmpArray[], int left, int right)
{
int center;
if (left < right)
{
center = (left + right) / 2;
Msort(Integer, TmpArray, left, center);
Msort(Integer, TmpArray, center+1, right);
Merge(Integer, TmpArray, left, center+1,right);
}
}
void MergeSort(int Integer[], int N)
{
int *TmpArray;
TmpArray = malloc(N * sizeof(int));
if (TmpArray != NULL)
{
Msort(Integer, TmpArray, 0, N - 1);
free(TmpArray);
}
else
printf("No space for tmp array!!");
}
//lpos= 左半边的起始点 rpos=右半边的起始点
void Merge(int Integer[], int TmpArray[], int lpos, int rpos, int rightend)
{
int i, leftend, NumElements, tmpos;
leftend = rpos - 1;
tmpos = lpos;
NumElements = rightend - lpos + 1;
//主循环
while (lpos <= leftend && rpos <= rightend)
if (Integer[lpos] <= Integer[rpos])
TmpArray[tmpos++] = Integer[lpos++];
else
TmpArray[tmpos++] = Integer[rpos++];
while (lpos <= leftend) //拷贝剩余的第一部分
TmpArray[tmpos++] = Integer[lpos++];
while (rpos <= rightend) //拷贝剩余第二部分
{
TmpArray[tmpos++] = Integer[rpos++];
}
//将Tmparray拷贝回去
for (i = 0; i < NumElements; i++, rightend--)
Integer[rightend] = TmpArray[rightend];
}
4.3归并排序运行结果截图
五、快速排序(O(N2))
5.1算法分析
快速排序的平均复杂度为O(NlogN),稍加努力(选取合适的枢纽元)就可以做到,并且避免最坏情形。将数组S快速排序分为以下四步:
*.如果S中元素个数是0或者1,则返回;
*.取S中任一元素v,称之为枢纽元(或基准数)pivot;
*.将S-{v}(S中其余元素)分成两个不相交的集合:S1={x∈S-{v}|x≤v}和S2={x∈S-{v}|x≥v};
*.返回QucikSort(S1)后,继而v,继而QucikSort(S2)。
5.2代码示例
#include <stdio.h>
#define M 12
void quicksort(int Int[],int left, int right)
{
int i, j, t, temp;
if (left>right)
return;
temp = Int[left]; //temp中存的就是基准数
i = left;
j = right;
while (i != j)
{
//顺序很重要,要先从右边开始找
while (Int[j] >= temp && i<j)
j--;
//再找左边的
while (Int[i] <= temp && i<j)
i++;
//交换两个数在数组中的位置
if (i<j)
{
t = Int[i];
Int[i] = Int[j];
Int[j] = t;
}
}
//最终将基准数归位
Int[left] = Int[i];
Int[i] = temp;
quicksort(Int,left, i - 1);//继续处理左边的,这里是一个递归的过程
quicksort(Int,i + 1, right);//继续处理右边的 ,这里是一个递归的过程
}
int main()
{
int i, j, t;
int Integer[M] = { 81,94,11,96,12,28,58,15,32,100,-22.5,12 };
printf("快速排序前:"); //输出排序后的结果
for (i = 0; i < M; i++)
printf("%4d ", Integer[i]);
quicksort(Integer, 0, M - 1); //快速排序调用
printf("\n快速排序后:"); //输出排序后的结果
for (i = 0; i < M; i++)
printf("%4d ", Integer[i]);
getchar(); getchar();
return 0;
}