本章主要是算法知识的基础讲解,介绍了循环不变式,几个简单的排序算法,递归分治算法等内容。
1、循环不变式
循环不变式主要用来说明算法的正确性,那么什么是循环不变式呢,其实就是在循环过程中,一些元素数据必须保持的一些性质,例如在插入排序中,数组为A,必须保证三个性质:
(1) 初始化:在循环开始之前,循环不变式是成立的,即:A[0]是有序的,A[1...n-1]是无序的。
(2) 保持:在循环的某一次迭代开始之前,循环不变式是成立的,那么在此次迭代结束后依然应该是成立的,即:A[0...i]是有序的,A[i+1...n-1]是无序的。
(3) 终止:当循环结束的时候,从循环不变式就可以得出我们的算法的正确性,即:整个数组A是有序的,排序成功。
/*
* 算法导论 第二章 插入排序
* 使用增量方法,依次遍历数组并逐个插入有序队列
* 插入时从后至前,找到不大于当前元素的第一个位置
* 将此位置后面的元素一次向后移动,将元素插入此位置
* 时间复杂度为O(n^2)
*/
#include <iostream>
using namespace std;
void printArray(int arr[], int len)
{
for (int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
int arr[] = {12, 21, 9, 80, 3, 11, 90, 4, 67};
int len = sizeof(arr) / sizeof(arr[0]);
cout << "原数组:" << endl;
printArray(arr, len);
for (int i=1; i<len; i++)
{
int temp = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > temp)
{
arr[j+1] = arr[j];
j = j - 1;
}
arr[j+1] = temp;
}
cout << "插入排序后的数组:" << endl;
printArray(arr, len);
return 0;
}
2、在算法分析中,我们通常都主要考虑算法的时间复杂度,因为空间资源相对比较廉价,容易满足,而时间是目前主要追求,而且是考虑算法的最坏情况下的时间复杂度。算法的时间复杂度的具体分析通常可以采用递归式求解(列出时间复杂度的表达式,猜想验证或者直接计算),递归树分析等。
3、分治法
很多算法在结构上是递归的,算法一次有一次的调用自身来解决相关的子问题。这种算法通常采用的就是分治策略:将原问题分成若干个较小的子问题,在递归的解决这些子问题,最后将自问题的解合并成原问题的解。主要分为三个步骤:
分解:将原问题分解成为一些列的子问题。
解决:递归的解决各个子问题,若子问题足够小,就直接解决。
合并:将子问题的解合并成原问题的解。
以下是合并排序以及 习题2-4 的逆序对数量计算
/*
* 算法导论 第二章 合并排序
* 利用递归分治法
* 同时计算序列中逆序对数量
* 时间复杂度为O(nlgn)
*/
#include <iostream>
using namespace std;
//定义最大整数
#define MAX_NUMBER 0x7fffffff
void printArray(int arr[], int len)
{
for (int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void mergeSort(int arr[], int p, int r);
void merge(int arr[], int p, int q, int r);
//统计序列中元素的逆序对数量
int count;
int main()
{
int arr[] = {12, 21, 9, 80, 3, 11, 90, 4, 67};
int len = sizeof(arr) / sizeof(arr[0]);
cout << "原数组:" << endl;
printArray(arr, len);
mergeSort(arr, 0, len-1);
cout << "合并排序后的数组:" << endl;
printArray(arr, len);
cout << "逆序对数量:" << count << endl;
return 0;
}
void mergeSort(int arr[], int p, int r)
{
if (p < r)
{
int q = (p + r) / 2;
mergeSort(arr, p, q);
mergeSort(arr, q+1, r);
merge(arr, p, q, r);
}
}
void merge(int arr[], int p, int q, int r)
{
int n1 = q - p + 1;
int n2 = r - q;
int *left = new int[n1+1];
int *right = new int[n2+1];
for (int i=0; i<n1; i++)
{
left[i] = arr[p+i];
}
for (int i=0; i<n2; i++)
{
right[i] = arr[q+1+i];
}
left[n1] = right[n2] = MAX_NUMBER;
int i = 0, j = 0;
for (int k=p; k<=r; k++)
{
if (left[i] <= right[j])
{
arr[k] = left[i];
i++;
} else {
/* 因为left数组元素都是在right的左边
* 所以一旦left[i]>right[j],则left[i]以后的元素都大于right[j]
* 逆序对数量增加 n1-1-i+1=n1-i 对
*/
count += n1 - i;
arr[k] = right[j];
j++;
}
}
delete[] left;
delete[] right;
}/*
* 算法导论 第二章 冒泡排序
* 思想:冒泡排序,顾名思义就是每一次将序列中最小的元素(较轻)移到前面去
* 轻的往上冒,重的往下掉,每次将未排序部分的最轻元素冒到最上面
* 时间复杂度为O(n^2)
*/
#include <iostream>
using namespace std;
void printArray(int arr[], int len)
{
for (int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
int arr[] = {12, 21, 9, 80, 3, 11, 90, 4, 67};
int len = sizeof(arr) / sizeof(arr[0]);
cout << "原数组:" << endl;
printArray(arr, len);
for (int i=0; i<len; i++)
{
for (int j=len-1; j>i; j--)
{
if (arr[j] < arr[j-1])
{
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
cout << "冒泡排序后的数组:" << endl;
printArray(arr, len);
return 0;
}/*
* 算法导论 第二章 习题2.3-7
* 此题利用合并排序+二分查找法
* 利用合并排序法对数组进行排序时间为O(nlgn)
* 对S中的每个元素进行一次二分查找时间为O(n*lgn)
* 所以总的时间复杂度为O(nlgn)
*/
#include <iostream>
using namespace std;
int binarySearch(int arr[], int low, int high, int x);
void mergeSort(int arr[], int p, int r);
void merge(int arr[], int p, int q, int r);
void printArray(int arr[], int len)
{
for (int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
int S[] = {12, 21, 9, 80, 3, 11, 90, 4, 67};
int x = 22;
int len = sizeof(S) / sizeof(S[0]);
cout << "原数组S:" << endl;
printArray(S, len);
//利用合并排序法对S排序
mergeSort(S, 0, len-1);
int i, index = -1;
for (i=0; i<len; i++)
{
//利用二分查找S中是否存在x-S[i]的元素
index = binarySearch(S, 0, len-1, x-S[i]);
if (index != -1 && index != i)
{
break;
}
}
if (index != -1)
{
cout << "S中的元素:" << S[i] << " + " << S[index] << " = " << x << endl;
} else {
cout << "S中不存在和为" << x << "的两个元素" << endl;
}
return 0;
}
int binarySearch(int arr[], int low, int high, int x)
{
while (low <= high)
{
int middle = (low + high) / 2;
if (x > arr[middle])
{
low = middle + 1;
} else if (x < arr[middle]) {
high = middle - 1;
} else {
return middle;
}
}
return -1;
}
void mergeSort(int arr[], int p, int r)
{
if (p < r)
{
int q = (p + r) / 2;
mergeSort(arr, p, q);
mergeSort(arr, q+1, r);
merge(arr, p, q, r);
}
}
void merge(int arr[], int p, int q, int r)
{
int n1 = q - p + 1;
int n2 = r - q;
int *left = new int[n1+1];
int *right = new int[n2+1];
for (int i=0; i<n1; i++)
{
left[i] = arr[p+i];
}
for (int i=0; i<n2; i++)
{
right[i] = arr[q+1+i];
}
left[n1] = right[n2] = 0x7fffffff;
int i = 0, j = 0;
for (int k=p; k<=r; k++)
{
if (left[i] <= right[j])
{
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
}
delete[] left;
delete[] right;
}原文地址:http://blog.csdn.net/lucienduan/article/details/38385231