1、冒泡排序
每一次比较相邻元素的大小,所以一趟比较n-1对相邻位置的数,并在每次发现前面的数大于相邻的后面的数时,交换位置,直到某一趟跑完后发现这一趟没有进行任何交换操作,结束。
最好的情况:正序排列,比较n-1次,移动0次,时间复杂度:O(n)
最坏的情况:逆序排列,比较1+2+3+...+(n-1) = n(n-1)/2,移动n(n-1)/2次,时间复杂度:O(n^2)
//a:待排序的数组;n:数组元素个数
void BubbleSort(int a[], int n)
{
bool tag = true;
int tmp = 0;
for(int i=1; i<n&&tag; i++)
{
tag = false;
for(int j=0; j<n-i; j++)
{
if(a[j] > a[j+1])
{
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
tag = true;
}
}
}
}
void BubbleSort(int A[], int n){
bool tag = true;
int tmp = 0;
for(int i=n; i>0&&tag; i--){
tag = false;
for(int j=0; j<i-1; j++){
if(A[j] > A[j+1]){
int tmp = A[j];
A[j] = A[j+1];
A[j+1] = tmp;
tag = true;
}
}
}
}
2、插入排序
2.1直接插入排序
将一个记录插入到已排好序的有序表中.
最好的情况:正序排列,比较n-1次,时间复杂度:O(n)
最坏的情况:逆序排列,比较n(n-1)/2,时间复杂度:O(n^2)
//直接插入排序
void InsertSort(int a[], int n)
{
int tmp = 0;
int i, j;
for(i=1; i<n; i++)
{
tmp = a[i];
for(j=i; j>0&&tmp<a[j-1]; j--)
a[j] = a[j-1];
a[j] = tmp;
}
}
2.2 折半插入
折半插入减少了比较的次数,但是移动次数不变,所以时间复杂度仍为:O(n^2)
//折半查找
int HalfFind(int a[], int low, int high, int value)
{
int index = 0;
while(low <= high)
{
index = (low + high)/2;
if(value == a[index])
break;
else if(value < a[index])
high = index - 1;
else low = index + 1;
}
return index;
}
//折半插入
void HalfInsertSort(int a[], int n)
{
int tmp = 0;
int i, j;
for(i=1; i<n; i++)
{
if(a[i] < a[i-1])
{
tmp = a[i];
int index = HalfFind(a, 0, i-1, tmp);
for(j=i; j>index; j--)
a[j] = a[j-1];
a[index] = tmp;
}
}
}
3、希尔排序
不断把待排序的对象分成若干个小组,对同一小组内的对象采用直接插入法排序,当完成了所有对象都分在一个组内的排序后,排序过程结束。
基本操作:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
希尔排序的一个特点是:子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量” 的记录组成一个子序列。希尔排序的分析是一个复杂的问题,因为它的时间是所取“增量”序列的函数,这涉及一些数学上尚未解决的难题。因此,到目前为止尚未有人求得最好的增量序列。增量序列可以有各种方法,但需要注意:应使增量序列中的值没有除1之外的公因子;并且最后一个增量值必须等于1。
时间复杂度:O(nlogn)
//希尔排序
void ShellSort(int a[], int n)
{
int d = n;
while(d > 1)
{
d = (d + 1)/2;
for(int i=0; i<n-d; i++)
{
if(a[i] > a[d + i])
{
int tmp = a[i];
a[i] = a[d + i];
a[d + i] = tmp;
}
}
}
}
4、选择排序
每次从数列中找出一个最小的数放到最前面来,再从剩下的n-1个数中选择一个最小的,不断做下去。
最坏或者平均,均比较n(n-1)/2 所以最坏和平均的时间复杂度为O(n^2),他没有什么好的情况。
//选择排序
void SelectSort(int a[], int n)
{
int tmp = 0;
int min = 0;
for(int i=0; i<n; i++)
{
min = i;
for(int j=i+1; j<n; j++)
{
if(a[j] < a[min])
min = j;
}
tmp = a[i];
a[i] = a[min];
a[min] = tmp;
}
}
5、归并排序
时间复杂度:最差、最好、平均都是 O(nlogn)
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void Merge(int a[], int low, int mid, int high, int tmp[])
{
int i = low, j = mid + 1;
int m = mid, n = high;
int k = 0;
while(i<=m && j<=n)
{
if(a[i] <= a[j])
tmp[k++] = a[i++];
else tmp[k++] = a[j++];
}
while(i<=m)
tmp[k++] = a[i++];
while(j<=n)
tmp[k++] = a[j++];
for(i=0; i<k; i++)
a[low + i] = tmp[i];
}
void mergeSort(int a[], int low, int high, int tmp[])
{
if(low < high)
{
int mid = (low + high)/2;
mergeSort(a, low, mid, tmp); //左边有序
mergeSort(a, mid + 1, high, tmp);//右边有序
Merge(a, low, mid, high, tmp); //两个有序序列合并
}
}
//归并排序
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if(p == NULL)
return false;
mergeSort(a, 0, n-1, p);
delete []p;
return true;
}
6、快速排序
最好:O(nlogn)
最坏:O(n^2) 在元素有序的情况下,最坏。
设置两个指针low和high,初值为low和high,枢轴的关键字为key。首先从high所指的位置起向前搜索找到第一个关键字小于key的记录,和枢轴记录相互交换,然后从low所指位置开始向后搜索,找到第一个关键字大于key的记录,和枢轴记录交换,重复这两步,直至low=high为止。
int Partition(int a[], int low, int high)
{
int tmp = a[low];
int key = a[low]; //枢轴
while(low < high)
{
while(low<high && a[high]>=key) high --;
a[low] = a[high];
while(low<high && a[low]<=key) low ++;
a[high] = a[low];
}
a[low] = tmp;
return low;
}
//快速排序
void QSort(int a[], int low, int high)
{
int loc = 0;
if(low < high)
{
loc = Partition(a, low, high);
QSort(a, low, loc - 1);
QSort(a, loc + 1, high);
}
}
快速排序的最坏情况和最快情况。(下面的内容来自精通八大排序算法系列:一、快速排序算法)
最坏情况发生在划分过程产生的俩个区域分别包含n-1个元素和一个0元素的时候,
即假设算法每一次递归调用过程中都出现了,这种划分不对称。那么划分的代价为O(n),
因为对一个大小为0的数组递归调用后,返回T(0)=O(1)。
估算法的运行时间可以递归的表示为:
T(n)=T(n-1)+T(0)+O(n)=T(n-1)+O(n).
可以证明为T(n)=O(n^2)。
因此,如果在算法的每一层递归上,划分都是最大程度不对称的,那么算法的运行时间就是O(n^2)。
亦即,快速排序算法的最坏情况并不比插入排序的更好。
此外,当数组完全排好序之后,快速排序的运行时间为O(n^2)。
而在同样情况下,插入排序的运行时间为O(n)。
//注,请注意理解这句话。我们说一个排序的时间复杂度,是仅仅针对一个元素的。
//意思是,把一个元素进行插入排序,即把它插入到有序的序列里,花的时间为n。
再来证明,最快情况下,即PARTITION可能做的最平衡的划分中,得到的每个子问题都不能大于n/2.
因为其中一个子问题的大小为|_n/2_|。另一个子问题的大小为|-n/2-|-1.
在这种情况下,快速排序的速度要快得多。为,
T(n)<=2T(n/2)+O(n).可以证得,T(n)=O(nlgn)。
直观上,看,快速排序就是一棵递归树,其中,PARTITION总是产生9:1的划分,
总的运行时间为O(nlgn)。各结点中示出了子问题的规模。每一层的代价在右边显示。
每一层包含一个常数c。
有关快速排序的文章,参见:
http://blog.chinaunix.net/uid-26432915-id-3023291.html
http://blog.csdn.net/v_JULY_v/article/details/6116297
参考文章:
http://blog.chinaunix.net/uid-26432915-id-3023292.html
http://blog.csdn.net/lixiang040330624/article/details/4647512
http://www.matrix67.com/blog/archives/166
http://blog.csdn.net/morewindows/article/details/6678165