1. 插入排序
插入排序是一种简单直观的排序方法,其基本思想在于每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中,直到全部记录插入完成。
由插入排序的思想可以引申出两个重要的排序算法:直接插入排序和希尔排序。
直接插入伪代码:
void InsertSort(ElemType A[], int n)
{
int i ,j;
for(i = 2;i<=n;i++) //依次将A[1]~A[n]插入到前面已排序序列
if(A[i].key<A[i-1].key)
{
A[0].key = A[i].key; //待插入值复制为哨兵
for(j = i-1;A[0].key<A[j].key;j--) //从后往前查找待插入位置
A[j+1] = A[j]; //后移
A[j+1] = A[0]; //将哨兵插入到空位
}
}
直接插入排序的
时间复杂度是
O(n^2),
空间复杂度为
O(1)
在最好的情况下,表中元素已经有序,此时每插入一个元素,都只需比较一次而不用移动元素,因而时间复杂度为O(n)
稳定性:由于每次插入元素总是从后向前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序方法。
2. 交换排序
所谓交换,就是根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。
冒泡排序
冒泡排序算法基本思想是:假设待排序表长为n,从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),则交换它们,直到序列比较完。
冒泡排序伪代码:
void BubbleSort(ElemType A[], int n)
{
//用冒泡排序将序列A中的元素从小到大排列
for (int i = 0; i < n; i++)
{
flag = false; //表示本趟冒泡是否发生交换的标志
for (int j = n-1; j > i ; j--)
{
if(A[j-1].key>A[j].key)
{
swap(&A[j-1], &A[j]); //这里有一个空间复杂度
flag = true;
}
}
if(flag == false)
return ; //本趟遍历后没有发生交换,说明已经有序
}
}
冒泡排序
算法的性能分析:空间复杂度
O(1)
,最坏情况下时间复杂度为
O(n^2)
,最好的情况下(表示元素基本有序)时间复杂度为O(n),其平均复杂度为
O(n^2)
冒泡排序是一个稳定的排序。
3. 快速排序
快速排序是对冒泡排序的一种改进。其基本思想是基于分治法的。想详细了解的朋友请转到http://blog.csdn.net/v_JULY_v/article/details/6116297
完整代码:
#include<stdio.h>
#include<exception>
#include<stdlib.h>
#include<string.h>
void Swap(int *a,int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int Partition(int data[], int length, int start , int end)
{
if(data == NULL || length<0 || start<0 || end>=length)
throw new std::exception("invalid parameters");
int index =(int)(start + end)/2; //产生start到end间的随机数
int small = start - 1;
Swap(&data[index], &data[end]);
for (int i = start; i < end; i++)
{
if(data[i]<data[end])
{
small++;
if(small != i)
Swap(&data[small], &data[i]);
}
}
++small;
Swap(&data[small], &data[end]);
return small;
}
void QuickSort(int data[], int length, int start, int end)
{
if(start == end)
return;
int index = Partition(data, length, start,end);
if(index>start)
QuickSort(data,length,start,index-1);
if(index<end)
QuickSort(data,length,index+1,end);
}
void Test(int dataS[], int dataD[], int length)
{
while(length>=0)
{
if(dataS[length]!=dataD[length])
break;
length--;
}
if(length>=0)
printf("failed");
else
printf("success");
}
int main()
{
int data[] = {2,8,3,7,1,5,6,4};
QuickSort(data,8,0,7);
int expectedData[]={1,2,3,4,5,6,7,8};
Test(data,expectedData,7);
getchar();
return 0;
}
快速排序算法的性能分析:
空间效率:由于快速排序是递归的,需要借助一个递归工作栈来保存每一层递归调用的必要信息,其容量应与队规调用的最大深度一致。最好情况下为取上界的log2(n+1);最坏的情况下,因为要进行n-1次递归调用,所以栈深度为O(n):平均情况为O(log2n)
时间效率:快速排序的运行时间与划分是否对称有关,而后者又与具体使用的划分算法有关。快速排序的最坏情况发生在两个区域分别包含n-1个元素和0个元素时,这种最大程度的不对称性发生在每一层递归上,即对应于初始排序表基本有序或基本逆序时,就得到最坏情况下的时间复杂度O(n^2)
有很多方法可以提高算法的效率。一种方法是当递归过程中划分得到的子序列的规模较小时不要再继续递归调用快速排序,可以采用直接插入排序算法进行后续的排序工作。另一种方法就是尽量选取一个可以将数据中分的枢轴元素。可以用三分法或者随机法,这样使得最坏情况在实际排序中基本不会出现。
时间复杂度O(nlog2n)
快速排序是一种不稳定的排序