给大家推荐一个《算法分析设计》的视频,我觉得老师讲的很清晰:算法设计与分析MOOC-青岛大学-张公敬教授
算法概述
算法的概念:算法是指解决问题的一种方法或过程,是由若干条指令组成的有穷序列。
算法的特征:
- 有限性:算法必须在有限时间内终止;
- 正确性:算法必须正确描述问题的求解过程;
- 可行性:算法必须是可实施的;
- 算法可以有0个或0个以上的输入;
- 算法必须有1个或1个以上的输出。
算法与程序的关系:
区别:
- 程序可以不一定满足可终止性。但算法必须在有限时间内结束;
- 程序可以没有输出,而算法则必须有输出;
- 算法是面向问题求解的过程描述,程序则是算法的实现。
联系:
- 程序是算法用某种程序设计语言的具体实现;
- 程序可以不满足算法的有限性性质。
算法复杂性度量:
期望反映算法本身性能,与环境无关。
理论上不能用算法在机器上真正的运行开销作为标准(硬件性能、代码质量影响)。
一般是针对问题选择基本运算和基本存储单位,用算法针对基本运算与基本存储单位的开销作为标准。
算法复杂性C依赖于问题规模N、算法输入I和算法本身A。即C=F(N, I, A)。
分治法
为什么使用分治法:求解问题算法的复杂性一般都与问题规模相关,问题规模越小越容易处理。
分治法基本思想:
将一个难以直接解决的大问题,分解为规模较小的相同子问题,直至这些子问题容易直接求解,并且可以利用这些子问题的解求出原问题的解。各个击破,分而治之。
分治法产生的子问题一般是原问题的较小模式,这就为使用递归技术提供了方便。递归是分治法中最常用的技术。
使子问题规模大致相等的做法是出自一种平衡子问题的思想,它几乎总是比子问题规模不等的做法要好
分治法所能解决的问题一般具有以下几个特征:
- 该问题的规模缩小到一定的程度就可以容易地解决;
- 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;
- 利用该问题分解出的子问题的解可以合并为该问题的解;
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。(这条特征涉及到分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但一般用动态规划较好。)
递归式求时间复杂度(看不懂证明 直接背公式结论)
T ( n ) = a T ( n b ) + O ( n k ) T(n)=aT(\dfrac{n}{b})+O(n^k) T(n)=aT(bn)+O(nk),其中a>0,b>1。
T ( n ) = { O ( n k ) a < b k O ( n k l o g b n ) a = b k O ( n l o g b a ) a > b k T(n)=\begin{cases} O(n^k) & a<b^k \\ O(n^klog_bn) & a=b^k \\ O(n^{log_ba}) & a>b^k \\ \end{cases} T(n)=⎩
⎨
⎧O(nk)O(nklogbn)O(nlogba)a<bka=bka>bk
算法实例
归并排序
private static void mSort(int[] arr, int left, int right) {
if (left == right){
return;
}
// 递归算法 主要负责切片 即把大的数组切成一个小数组
int mid = left + ((right-left) >> 1);
mSort(arr, left, mid);
mSort(arr, mid+1, right);
merger(arr, left, mid, right);
}
private static void merger(int[] arr, int left, int mid, int right){
int[] temp = new int[right-left+1];
int i = 0;
int p1 = left; // 指针1从左数组开始遍历
int p2 = mid + 1; // 指针2从右数组开始遍历
while (p1 <= mid && p2 <= right){
temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
// 左边还没遍历完
while (p1 <= mid)