写于2013年8月29号
自己学会常见的这几种算法,并整理成文。
代码实现
名词解释
时间复杂度:算法占用的时间(一般与数据数量呈正相关)。
空间复杂度:算法占用的空间(一般与数据数量呈正相关)。
算法稳定性:是指是否会交换值相等的两个数据的初始位置。
一.冒泡排序 Bubble Sort
冒泡排序(BubbleSort)是一种入门级别的算法,较好理解。一般作为第一种算法被认识。
冒泡排序是一种稳定的排序。
平均时间按复杂度O(n*n) 【最好时间复杂度O(n),最坏时间复杂度O(n*n)】
空间复杂度O(1)
算法的运作如下:
1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2、对每一对相邻元素重复步骤1,从开始第一对到结尾的最后一对。每趟过后,排在最后的元素最大。
3、重复步骤2,直到剩下一个元素。
代码:
代码:
1 for(int i=0;i<n-1;i++) //n-1趟排序
2 {
3 for(int j=n-1;j>i;j--) //j>i因为i前面的元素已经排好了
4 {
5 int temp=0;
6 if(arrayA[j]<arrayA[j-1]) //比较相邻的两个元素,见步骤1
7 {
8 temp=arrayA[j];
9 arrayA[j]=arrayA[j-1];
10 arrayA[j-1]=temp;
11 }
二、选择排序Selection Sort
选择排序其实是冒泡的升级,每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
冒泡排序是一种稳定的排序。
平均时间按复杂度O(n*n) 【最好最坏均是O(n*n)】
空间复杂度O(1)
算法运作如下:
1、比较所有待排序元素。找到最小(最大)的元素。
2、将这个元素放入已排序数列的最后。
3、持续每次对待排序元素重复上面的步骤,直到没有待排序元素。
代码:
1 for(int i=0;i<n-1;i++) //n-1趟排序,因为最后只剩一个元素的时候不用再比较
2 {
3 int index=i; //索引值,记录当前已排序数列排列到的位置
4 for(int j=i+1;j<n;j++) //j从i+1开始,因为前面的元素已经排好了
5 {
6 if(arrayA[j]<arrayA[index])
7 index=j; //每趟将待排序数据中的最小值的位置存放到index
8 }
9
10 //若该趟index指向的的最小值不在i处(最前面),则交换array[index]和array[i]的值
11 if (index!=i)
12 {
13 //交换数据
14 int temp = arrayA[i];
15 arrayA[i] = arrayA[index];
16 arrayA[index] = temp;
17 }
18 }
三、快速排序QuickSort
快速排序是对冒泡排序的一种改进。
快速排序是一种不稳定的排序。
平均时间按复杂度O(nlogn) 【最好时间复杂度O(nlogn),最坏时间复杂度O(n*n)】
空间复杂度O(nlogn)
算法运作如下:
1、以第一个元素作为key
2、对数组进行分区,使key左边元素的值都不大于key,右边的元素值都不小于key,最后将作为key的元素调整到排序后的正确位置。
3、将步骤2结束后的数列二分为key的左右两部分,递归快速排序,最终二分到只有一个元素,排序完成。
步骤2简图
全过程简图:
初始状态: 【49,38,65,97,76,13,27,49′】
步骤二: 【27,38,13】49【76,97,65,49′】
步骤三: 【13】27【38】【49′,65】76【97】
有序序列: 【13,27,38,49,49′,65,76,97】
#include<stdio.h>
#include<stdlib.h>
#define len 8
int Partition( int d[],int low,int high)
{
int t;
t = d[low];
while(low<high)
{
while(low<high && d[high]>=t ) --high;
d[low] = d[high];
while( low<high && d[low]<=t ) ++low;
d[high] = d[low];
}
d[low] = t;
return low;
// for(int k=0;k<len;k++)
// printf("%d\n",d[k]);
}
void QSort(int *d,int low,int high)
{ //对顺序表中的子序列作快速排序
int pivotloc;
if( low <high )
{
pivotloc = Partition(d,low,high);
QSort(d,low,pivotloc-1);
QSort(d,pivotloc+1,high);
}
}
void QuickSort(int *d)
{
QSort(d,0,len-1);
}
int main()
{
int d[len] = {49,38,65,97,76,13,27,49};
QuickSort(d);
for(int k=0;k<len;k++)
printf("%d\n",d[k]);
return 0;
}
参考:http://blog.youkuaiyun.com/lvyuan30276/article/details/9813705
四、归并排序Merge Sort
“归并”的含义是将两个或两个以上的有序表组合成一个新的有序表。
速度仅次于快速排序(数量级相等,系数不同)。
一般用于对总体无序,但是各子项相对有序的数列。
归并排序是一种稳定的排序。
平均时间按复杂度O(nlogn) 【最好时间复杂度O(nlogn),最坏时间复杂度O(nlogn)】
空间复杂度O(n)
算法原理:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,指向两个有序表的头
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
第四步:重复步骤三直到某一指针达到序列尾,将另一序列剩下的所有元素直接复制到合并序列尾。
第五步:将第四步重复,直到完成整个数列。
原理简图
代码:
1. #include <iostream>
2. using namespace std;
3.
4. const int maxSize = 100;
5.
6. /*
7. 函数名: Merge
8. 函数功能: 将有序的SR[begin, middle]和SR[middle+1, end]排序后存储到TR中。
9. 函数参数: int SR 原数组
10. int TR 目标数组
11. */
12. void Merge(int SR[], int TR[], int begin, int middle, int end)
13. {
14. int i, j, k;
15. i = begin;
16. j = middle + 1;
17. k = begin;
18. while (i <= middle && j <= end)
19. {
20. if (SR[i] <= SR[j])
21. {
22. TR[k] = SR[i];
23. k++;
24. i++;
25. }
26. else
27. {
28. TR[k] = SR[j];
29. k++;
30. j++;
31. }
32. }
33.
34. while (i <= middle)
35. {
36. TR[k] = SR[i];
37. k++;
38. i++;
39. }
40.
41. while (j <= end)
42. {
43. TR[k] = SR[j];
44. k++;
45. j++;
46. }
47. }
48.
49. void MergeSort(int SR[], int TR1[], int begin, int end)
50. {
51. if (begin == end)
52. {
53. TR1[begin] = SR[begin];
54. }
55. else
56. {
57. int TR2[maxSize + 1];
58. int middle = begin + (end - begin) / 2; /* 将SR[begin,end]平均分成SR[begin, middle]和SR[middle+1, t] */
59. MergeSort(SR, TR2, begin, middle); /* 递归地将SR[begin, middle]归并为有序的TR2[begin, middle] */
60. MergeSort(SR, TR2, middle+1, end); /* 递归地将SR[middle+1, end]归并为有序的TR2[middle+1, end] */
61. Merge(TR2, TR1, begin, middle, end); /* 将TR2[begin, middle]和TR2[middle+1, end]归并到TR2[begin,end] */
62. }
63. }
64.
65. int main()
66. {
67. int array[] = {50, 10, 90, 30, 70, 40, 80, 60, 20};
68. MergeSort(array, array, 0, 8);
69. for (int i = 0; i < 9; i++)
70. {
71. cout << array[i] << " " ;
72. }
73. cout << endl;
74. }
参考:
http://blog.youkuaiyun.com/listener51/article/details/9839723 图解
http://blog.youkuaiyun.com/begginghard/article/details/9878433 算法
五、堆排序Heap Sort
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
背景知识:1991年计算机先驱奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德Robert W.Floyd和威廉姆斯J.Williams在1964年共同发明了著名的堆排序算法Heap Sort
时间复杂度为O(n*logn)【最好、最坏、平均三者,在数量级上相等】
空间复杂度O(1)【一个temp单元,用来辅助存储】
堆排序不稳定
算法描述:
1、 将原数据(N1,Nn)整理为大根堆
2、 将堆顶元素与排在线性最后的元素交换位置
3、 将数据(N1,Nn-1)重置为大根堆
4、 重复步骤2和步骤3,直到堆中仅剩一个元素
代码:
1. //用大根堆,排序后是从小到大排序(每次将最后一个元素与堆顶元素交换)
2. //假设数组data的下标从1开始
3. void HeapSort(int data[], int length)
4. {
5. int i;
6. for(i=length/2; i>=1; i--) //建立一个大根堆
7. HeapAdjust(data, i, length);
8. for(i=length; i>1; i--)
9. {
10. Swap(data, 1, i); //将根节点(此堆中最大元素)与最后一个元素交换位置
11. HeapAdjust(data, 1, i-1); //抛出堆中最后一个位置,重新整理为最大堆
12. }
13. }
14.
15. //data[s+1]¬¬—data[m]已满足堆的性质,现在调整data[s],使得data[s]—data[m]也成为一个大根堆
16. void HeapAdjust(int data[], int s, int m)
17. {
18. int temp, j;
19. temp = data[s];
20. for(j=2*s; j<=m; j=j*2)
21. {//在data[i]的两个孩子data[2i]和data[2i+1] 中的较大者,跟data[i]进行比较,若孩子的值较大,则交换父节点和此孩子节点,保证其满足大根堆的性质。
22. if(j<m && data[j]<data[j+1]) //选择兄弟节点中的较大者
23. j++;
24. if(temp >= data[j]) //如果该节点小于父节点,则符合条件,跳出
25. break;
26. data[s] = data[j]; //如果该节点的值大于父节点,进行值的交换
27. s = j;
28. }
29. data[s] = temp; //此时辅助空间的值重新为父节点
30. }
六、桶排序Bin Sort
桶排序特别适合数据随机分布在域上的情况。
其思想就是把区间[min,max]划分成n个相同大小的桶,然后将所有数据放到对应的桶中去(sum放到num/n+1号桶中)。然后对各个桶中的数进行排序(此处算法不做限制),最后按次序把各桶中的元素列出来即可。
数据随机分配在域上的情况下,
平均时间复杂度为线性的O(N+C),其中C=N*(logN-logM)【最好时间复杂度为O(N),但极费空间;最坏时间复杂度为子排序算法的复杂度】
空间复杂度为O(B*N),其中B是桶的个数
代码
1.分组
2.使用其他通用排序算法对各个桶内元素排序
代码略