1、冒泡排序。冒泡排序执行的时间取决于比较的趟数,在最好的情况下,待排序的记录是正序,算法只执行一趟,进行了n-1次关键码的比较,不需要移动记录,时间复杂度是O(N);
最坏的情况下,是倒序,每次比较只有一个最大的记录被交换到最终的位置,故算法执行了n-1趟,每次比较的次数是n-i,关键码比较的次数是n(n-1)/2.。
平均情况的时间复杂度与最坏的情况是同一个数量级的。
空间复杂度o(1). 它是稳定的排序算法。
代码如下:
void buffersort1(int array[],int len)
{
bool exchange;//记录是否有交换
for (int i = 0; i < len-1; i++)//外层循环比较的趟数N
{
exchange = false;
for (int j = 0; j < len-i-1;j++)//内层循环每趟需要比较的次数
{
if (array[j]>array[j + 1])
{
int t = array[j];
array[j] = array[j+1];
array[j+1] = t;
exchange = true;
}
}if (exchange == false)
return;
}
2、快速排序
快速排序算法也是基于一种交换的排序算法,是对冒泡排序的一种改进。在冒泡中,记录的比较和移动是在相邻的位置进行的,记录每次交换只能后移一个位置,因而中的比较和移动的次数比较多。在快速排序中,它的思想是记录的比较和移动都是从两端向中间位置进行的,关键码较大的记录一次就能从前面移动到后面,关键小的记录一次就能从后面移动到前面。记录移动的距离较远,从而减少了总的比较次数和交换的次数。
首先选择一个轴值,将待排序的记录划分为独立两部分,左侧的记录都是小于轴值的,右侧的记录都是大于轴值的。
int partion(int arr[], int low, int high)
{
while (low < high)
{
while (low < high&&arr[low] <= arr[high])
high--;
if (low < high)
{
int t = arr[low];
arr[low] = arr[high];
arr[high] = t;
low++;
}
while (low < high&&arr[low] <= arr[high])
low++;
if (low < high)
{
int t = arr[low];
arr[low] = arr[high];
arr[high] = t;
high--;
}
}
return low;
}
void sort(int array[],int low,int high)
{
if (low < high)
{
int pos = partion(array, low, high);
sort(array, low, pos - 1);
sort(array, pos + 1, high);
}
}
快速排序的趟数取决于递归的深度,最好的情况下是每次划分对一个记录定位后,该记录的左侧和右侧序列的长度一样,在具有N个记录的序列中,对一个记录定位需要对整个序列进行扫面一遍,则所需要的时间为O(N).每次划分的时间为o(log2n),因此总的时间复杂度是nlog2n.
最坏的情况是,待排序的序列是正序或者倒序,每次划分只得到有个比上次少一个记录的子序列,另一个为空。此时必须进行N-1次递归才能吧所有记录定位,每次需要比较的次数是n-i,因此时间复杂度是o(N2).平均情况和最坏的情况是一个数量级的。
空间复杂度最好的情况是(log2n),最坏的情况是o(n-1),平均情况是o(log2n).
快速排序是一个不稳定的排序算法。
3、简单选择排序
简单选择排序算法的思想是在第I趟排序中找出最小的元素,并和第I个记录进行交换。因此选择排序算法与序列的初始状态是无关的,无论待排序的序列是什么样子的,每趟排序都是比较N-I次。总的比较次数是O(N2),这是选择排序算法最好。最坏。平均的时间复杂度。它是不稳定的排序算法。
空间复杂度是o(1).
void selectsort(int r[],int n)
{
int j = 0;
for (int i = 0; i < n; i++)
{
int index = i;
for (j = i + 1; j < n; j++)
{
if (r[j]<r[index])index = j;
}
if (index != i)
{
int t = r[i];
r[i] = r[index];
r[index] = t;
}
}
}
4、堆排序算法
堆排序算法是简单选择算法的一种改进,改进的着眼点是:如何减少关键码的比较次数。简单选择排序在一趟排序中仅选出最小的关键码,没有把一趟比较的结果保存下来,因而记录比较的次数就多了。堆排序在选出最小码的同时,也找出较小的关键码,也找出较小的关键码,从而较少了比较的次数。
大根堆的定义:每个节点的值都大于等于其左右孩子的值。堆顶节点是所有节点的最大值。
堆排序思想:首先将待排序的序列建立一个大根堆,然后将堆顶元素与最后一个元素交换,再不断调整堆的过程。堆排序的运行时间主要消耗在初始建堆的过程和不断调整堆的过程。
首先是将待排序的序列建立一个堆,初始堆
void max_heapmdf(int arr[],int k,int n)// 调整堆
{
int i = k;
int j = 2 * k + 1;
while (j < n)
{
if ((j+1 < n) && (arr[j] < arr[j + 1]))
{
j = j + 1;
}
if (arr[i]>arr[j])
break;
else
{
swap(arr[i],arr[j]);
i = j;
j = 2 * i + 1;
}
}
}
void buildheapmax(int arr[],int n)//建立初始堆
{
for (int i = n / 2 - 1; i >= 0; i--)
{
max_heapmdf(arr, i, n);
}
}
void heapsort1(int arr[],int n)
{
buildheapmax(arr, n);
for (int i = 1; i < n; i++)
{
swap(arr[0],arr[n-i]);
max_heapmdf(arr, 0, n-i);
}
}
初始堆的建立需要O(N),第I次取堆顶元素重建堆的时间是需要O(log2i),并且需要去N-1次,因此总的时间复杂度是o(nlog2n).向下调整堆的时间与树高有关。堆排序的平均时间、最坏、最好时间都一样。对,记录的初始状态无关。
空间复杂度O(1).
5、插入算法
思想是给每个元素找位置,将每一个记录插入到已经排好序的序列中。即是将排序序列分成两组,有序组和无序组,每次从无序组中取一个元素,与有序数组中每个元素进行比较,找到合适的位置,并插入到有序数组中。一般将第一个元素视为有序数组。
void insertsort(int arr [],int len)
{
for(int i=1;i<len;i++)
{
int k=i;
int temp=arr[k];
for(int j=i-1;(j>=0)&&arr[j]>temp;j--)
{ arr[j+1]=arr[j];
k=j;
}arr[k]=temp;
}
}
最好的情况是正序,需要比较次数是n-1次,因此时间复杂度是o(n).最坏的情况是逆序,时间复杂度是o(n2);空间复杂度是 o(1).
直接插入排序算法是一种稳定的排序算法,当序列中基本有序或者待排序记录比较少时,它是最佳的排序算法。
6、希尔排序
思想:对直接插入的改进,先将整个待排序记录分割成若干个子序列,在每个子序列中分别进行直接插入排序,待整个序列基本有序时,再对全体记录进行一次直接插入排序。
void shellsort(int array[], int len)
{
int gap = len;
for (gap = gap / 2; gap >0; gap = gap / 2)//注意gap 的条件,gap>0 或者gap>=1
{
for (int i = gap; i<len; i += gap)
{
int k = i;
int temp = array[k];
for (int j = i - gap; j >= 0 && array[j] > temp; j = j - gap)
{
array[j + gap] = array[j];
k = j;
}
array[k] = temp;
}
}
}
时间复杂度是在o(n2)与o(nlog2n)之间,当n在某个特定范围内,希尔排序的时间复杂度约为o(logn1.3).空间复杂度是o(1).是不稳定的排序算法。
7.归并排序
归并排序通常采用2-路归并排序算法。采用分治法。
void Merge(int arr,int low,int mid,int high)
{int i=low;
int j=mid+1;
int k=0;
int *temp=new int [hih-low+1];
if(!temp)
{cout<<"申请失败";
exit(0);
}
while(i<=mid&&j<=high)
{ if(arr[i]<arr[j])
{temp[k++]=arr[i++];
}
else
{temp[k++]=arr[j++];
}
}
while(i<=mid)
{
temp[k++]=arr[i++];
}
while(j<=high)
{
temp[k++]=arr[j++];
}
for(int i=low,k=0;i<=high;i++,k++)
{arr[i]=temp[k];
}
delete []temp;
}
void Msort(int arr[],int low,int high)
{
if(low<high)
{
int mid=(low+high)/2;
Msort(arr,low,mid);
Msort(arr,mid+1,high);
Merge(arr,low,high,mid);
}
}
一趟归并排序需要将待排序序列扫描一遍,其时间性能是o(n),整个归并排序需要进行log2n趟,因此总的时间复杂度是o(nlog2n).这是其平均/最好、最坏的时间复杂度。
空间复杂度是o(n).是一种稳定的排序算法。
8、桶排序
将数组分配到有限数量的桶子里,每个桶子再个别排序。这时可用冒泡、快排、等。
下列中:将数据作为桶(数组)的下标存储,适合于数据值的范围比较小;比如:求在1G大小的字符串中,出现次数最多的那个字符;因为字符char的边界是[0...255],就可以将所有的字符遍历、存储到[0...255]桶中(数组下标).
假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果
对每个桶中的数字采用快速排序,那么整个算法的复杂度是
O(n + m * n/m*log(n/m)) = O(n + nlogn - nlogm)
从上式看出,当m接近n的时候,桶排序复杂度接近O(n)
基数排序:
可以理解为对桶排序的扩充,将数据分别按个位、十位、百位...分别放入buckets[0...9][num]的桶中。
即:先将个位数存储到[0...9]的桶中,个位数将排好序
再按照个位数排好的序,将十位数存储到[0...9]的桶中,十位数排好序(个位数上一轮已经排好序)...
再百位,千位...每次放入桶中后,桶的顺序、桶内数据的顺序是排好的。
例如:
下例中数组:
int array[] = {2, 343, 342, 1, 123, 43, 4343, 433, 687, 654, 3};
按个位依次排序(到桶中)
0|
1|1,
2|2,342
3|343,123,43,4343,433,3
4|654
5|
6|
7|687
8|
9|
按值的顺序存入到array[] = {1,2,342,343,123,43,4343,433,3,654,687};
由此可见,个位数是排好序的;
再将array[] = {1,2,342,343,123,43,4343,433,3,654,687}按十位数依次排序
0|1,2,3
1|
2|123
3|433
4|342,343,43,4343
5|654
6|
7|
8|687
9|
按值的顺序存入到array[] = {1,2,3,123,433,342,343,43,4343,654,687}
由此可见,个位数、十位数是排好序的;
再将array[] = {1,2,3,123,433,342,343,43,4343,654,687}按百位数依次排序
0|1,2,3,43
1|123
2|
3|342,343,4343
4|433
5|
6|654,687
7|
8|
9|
按值的顺序存入到array[] = {1,2,3,43,123,342,343,4343,433,654,687}
由此可见,个位数、十位数、百位数是排好序的;
再将array[] = {1,2,3,43,123,342,343,4343,433,654,687}按千位数依次排序
0|1,2,3,43,123,342,343,433,654,687
1|
2|
3|
4|4343
5|
6|
7|
8|
9|
按值的顺序存入到array[] = {1,2,3,43,123,342,343,433,654,687,4343}
这时整个数组已经按升序排序完成。
基数排序的时间复杂度,假设待排序的关键码由d 个组成,每个关键码的取值范围为m个,则基数排序的时间复杂度是o(d*(n+m))。空间复杂度是 o(m) .它是稳定的排序算法。其中d是位数, n是关键码个数,m是基数。
总结:
(1)在最好的情况下,直接插入排序和冒泡排序是最快的;在平均情况下,快速排序是最快的;在最坏的情况下,堆排序和归并排序是对快的。
(2)当N比较大,关键码分布随机,且对稳定性不做要求,采用快排;
(3)当N比较大,内存空间允许,且要求稳定,采用归并;
(4)当N比较大,关键码分布可能出现正序或者逆序,且对稳定性不做要求,采用堆排序或者归并。
(5)当B比较大,而只要找出最小前几个记录,采用堆排序或者简繁选择排序。
(6)当N比较小,记录基本有序,且要求稳定,采用直接插入。
(7)当待排记录个数比较小,记录所含数据项较多,所占的空间较大,采用简单选择。