这个学期课程很少,空闲时间很多,故重新复习了一下《算法导论》中的常用算法和数据结构,并且将实现代码保存到博客,以便大三暑假找实习时方便复习。。。。
(1)插入排序
直接插入排序的思想非常简单,将序列中第一个元素作为一个有序序列,然后将剩下的n-1个元素按关键字大小依此插入该有序序列,每插入一个元素后依然保持该序列有序,经过n-1趟排序后即成为有序序列。该算法的时间复杂度是O(n^2)。
template<typename T>
void insertionSort(T *A, int length)
{
for(int i = 1; i < length; i++){
int j = i-1;
T temp = A[i];
// 按升序排列
while(j >= 0 && A[j] > temp){
A[j+1] = A[j];
j--;
}
A[j+1] = temp;
}
}
(2)冒泡排序
冒泡排序也是一个较简单的排序算法。冒泡排序通过不断交换两个元素实现,它的思想是:第一趟在序列(A[0]~A[n-1])中从前往后进行两个相邻元素的比较,若后者小,则交换,比较n-1次;第一趟排序结束,最大元素被交换到A[n-1]中(大的泡泡不断沉底),下一趟排序则只需在子序列(A[0]~A[n-2])中进行;如果在某一趟排序中未交换元素,说明子序列已经有序,则不再进行下一趟排序。冒泡排序算法的时间复杂度同样是O(n^2)。
template<typename T>
void bubbleSort(T *A, int length)
{
int i, j, last;
i = length-1; //最多进行length-1次
while(i > 0){
last = 0;
for(j = 0; j < i; j++){
if(A[j] > A[j+1]){
int tmp = A[j+1];
A[j+1] = A[j];
A[j] = tmp;
last = j; // 注意last的设置
}
}
i = last;
}
}
如上代码是通过last的设置来判断是否需要继续排序的。
(3)归并排序
归并排序是分治思想的应用,即不断将原问题划分为较小并较易解决的子问题,然后再合并子问题的结果得到原问题的结果。直观的操作如下:
A.分解:将n个元素分成各含n/2个元素的子序列;
B.解决:用合并排序法对两个子序列递归地排序;
C.合并:合并两个已排序的子序列以得到排序结果。
故归并排序的关键步骤在于合并两个已排序的子序列。为做排序,引入一个辅助过程MERGE(A,p,q,r),其中A是数组,p,q,r是下标,满足p<=q<r。该过程假设子数组A[p...q]和A[q+1...r]都已排好序,并将它们合并成一个已排好序的子数组代替当前子数组A[p...r]。
const int MaxInt = (1<<31)-1;
template<typename T>
void merge(T* A, int p, int q, int r)
{
int length1 = q-p+1;
int length2 = r-q;
// 多出的一个空间用来放置哨兵
int *L = new int[length1+1];
int *R = new int[length2+1];
// 把原先数组的值分别复制到左边和右边
for(int i = 0; i < length1; i++)
L[i] = A[p+i];
for(int i = 0; i < length2; i++)
R[i] = A[q+1+i];
L[length1] = R[length2] = MaxInt;
// 开始合并
int i = 0, j = 0;
for(int k = p; k <= r; k++){
if(L[i] < R[j])
A[k] = L[i++];
else
A[k] = R[j++];
}
// 记得回收内存
delete []L;
delete []R;
}
template<class T>
void mergeSort(T* A, int p, int r)
{
if(p < r){
// 说明数组至少还有2个元素,要继续排序
int q = (p+r)/2;
mergeSort(A, p, q);
mergeSort(A, q+1, r);
merge(A, p, q, r);
}
}
这个排序算法实现起来虽然比前两个算法复杂,但效率也高很多,归并排序的时间复杂度是O(nlgn)。