【算法设计与分析(王晓东第五版)】期末复习笔记

chap 2 递归与分治

复杂度递推表达式如何求解
在这里插入图片描述

2.1归并排序

2.1.1要点

稳定排序。
先分解,再合并(处理)
可改为自下而上(迭代版本)

缺点:

  • ①合并时也要比较、挪动位置——只是渐进意义上的O(n)
  • ②空间开销O(n)(本次考试不考察空间复杂度)

以上两个缺点使之不如快排。

2.1.2代码

分:
在这里插入图片描述
合:
在这里插入图片描述
非递归-分:
在这里插入图片描述
在这里插入图片描述

2.1.3时间复杂度分析

T ( n ) = 2 T ( n 2 ) + O ( n ) T(n) = 2T(\frac{n}{2})+O(n) T(n)=2T(2n)+O(n),即 O ( n l o g n ) O(nlogn) O(nlogn)

2.1.5扩展问题:逆序对

问题:给定一个长度为n的排列,求其逆序对数

逆序对: 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 称为 A 的一个逆序对.


使用归并排序后时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),而朴素算法的复杂度为 O ( n 2 ) O(n^2) O(n2)

2.2快速排序

2.2.1要点
不稳定排序

2.2.2代码
外壳:
在这里插入图片描述
分区(下标范围:p~r):
在这里插入图片描述

2.2.3时间复杂度分析

最好: T ( n ) = 2 T ( n 2 ) + O ( n ) T(n) = 2T(\frac{n}{2})+O(n) T(n)=2T(2n)+O(n),即 O ( n l o g n ) O(nlogn) O(nlogn)

不好不坏,每次按比例划分,如1比9: T ( n ) = T ( n 10 ) + T ( 9 n 10 ) + O ( n ) T(n) = T(\frac{n}{10})+T(\frac{9n}{10})+O(n) T(n)=T(10n)+T(109n)+O(n),即 O ( n l o g n ) O(nlogn) O(nlogn)

一下好一下坏: T ( n ) = 2 U ( n 2 ) + O ( n ) , U ( n ) = T ( n − 1 ) + O ( n ) T(n) = 2U(\frac{n}{2})+O(n),U(n) = T(n-1)+O(n) T(n)=2U(2n)+O(n)U(n)=T(n1)+O(n),即 O ( n l o g n ) O(nlogn) O(nlogn)

最坏: T ( n ) = T ( n − 1 ) + O ( n ) T(n) = T(n-1)+O(n) T(n)=T(n1)+O(n),即 O ( n 2 ) O(n^2) O(n2)

2.2.4走例子

2.2.5扩展:稳定快排写法

在一次partition中,创建一个临时数组,在要partition的数组里找到小于pivot的元素添加到临时数组中,然后将 pivot添加到临时数组中, 然后再扫描一遍数组将大于等于pivot到元素添加到临时数组中,最后再将临时数组拷贝回原数组,这样partition之后 到快速排序就是稳定的。

分区三趟遍历
在这里插入图片描述
代码:

public static int partitionStable(int[] array, int left, int right) {
        int[] temp = new int[right - left + 1];
        //制定最左元素为pivot
        int pivot = array[left];
        int idxForTemp = 0; // 初始化指向临时数组temp 的第一个位置
        //先在 left 到right 这个范围内找小于 pivot 到元素
        for (int i = left + 1; i <= right; i++) {
            if (array[i] < pivot) {
                temp[idxForTemp++] = array[i];
            }
        }
        temp[idxForTemp++] = pivot; // 把 pivot放到它该有到位置上面
        int awsFromTemp = idxForTemp - 1; // 保存放pivot到位置 后面要用
        //再遍历一遍数组找到所有大于等于pivot到元素 依次放到临时数组temp中
        for (int i = left + 1; i <= right; i++) {
            if (array[i] >= pivot) {
                temp[idxForTemp++] = array[i];
            }
        }
        idxForTemp = 0;
        int aws = 0;
        //把排好序到部分拷贝回原数组
        for (int i = left; i <= right; i++) {
            if(idxForTemp == awsFromTemp){
                aws = i;
            }
            array[i] = temp[idxForTemp++];
        }
        return aws;
    }

2.3线性时间选择

(没有扩展,难在分组代码)

代码
随机选择:
在这里插入图片描述
分组选择:
在这里插入图片描述
四个参数的partition函数代码:(已测试)

int Partition(int nums[], int lo, int hi, int X) {
	for (int i = lo; i < hi; i++) {
		if (nums[i] == X) {
			swap(nums[i], nums[lo]);
			break;
		}
	}

	int i = lo, j = hi - 1;
	while (i < j) {
		while (i < j && nums[j] >= X) j--;
		if (i < j) nums[i++] = nums[j];

		while (i < j && nums[i] <= X) i++;
		if (i < j) nums[j--] = nums[i];
	}
	nums[i] = X;
	return i;
}

书上代码的数组下标貌似都是左右闭区间,这不规范。

在这里插入图片描述
75和5这两个常数是怎么确定的:
在这里插入图片描述

复杂度

随机选择:平均 O ( n ) O(n) O(n) ,最差 O ( n 2 ) O(n^2) O(n2)

分组选择:最差 O ( n ) O(n) O(n)

2.4 二分查找

(扩展:快速幂)

chap 3 动态规划

3.1矩阵连乘

3.1.1最优子结构证明
3.1.2状态转移方程

状态定义: m [ i ] [ j ] m[i][j] m[i][j] 表示计算从矩阵 A i A_i Ai A j A_j Aj的连乘积的最小标量乘法次数。

状态转移方程:
m [ i ] [ j ] = min ⁡ i ≤ k < j ( m [ i ] [ k ] + m [ k + 1 ] [ j ] + p i − 1 ⋅ p k ⋅ p j ) m[i][j] = \min_{i \leq k < j} (m[i][k] + m[k+1][j] + p_{i-1} \cdot p_k \cdot p_j) m[i][j]=ik<jmin(m[i][k]+m[k+1][j]+pi1pkpj)
其中 p i p_i pi 是矩阵 A i A_i Ai的行数。

3.1.3代码

3.1.4复杂度

O ( n 3 ) O(n^3) O(n3)

3.1.5走例子
3.1.6扩展

输出具体方案

3.2最长公共子序列

状态定义: c [ i ] [ j ] c[i][j] c[i][j]表示序列 X X X的前 i i i 个元素和序列 Y Y Y 的前 j j j个元素的最长公共子序列的长度。

状态转移方程:
c [ i ] [ j ] = { c [ i − 1 ] [ j − 1 ] + 1 if  x i = y j max ⁡ ( c [ i − 1 ] [ j ] , c [ i ] [ j − 1 ] ) otherwise c[i][j] = \begin{cases} c[i-1][j-1] + 1 & \text{if } x_i = y_j \\ \max(c[i-1][j], c[i][j-1]) & \text{otherwise} \end{cases} c[i][j]={c[i1][j1]+1max(c[i1][j],c[i][j1])if xi=yjotherwise

(拓展:具体方案,[所有方案?],最长上升子序列,回文子序列)

在这里插入图片描述

具体方案
在这里插入图片描述

3.3最大子段和

复杂度O(n)

3.4 01背包(不考优化)

状态定义: m [ i ] [ j ] m[i][j] m[i][j] 表示考虑第 i i i n n n 个物品,在背包容量为 j j j 时的最大价值。

状态转移方程:
m [ i ] [ j ] = max ⁡ ( m [ i + 1 ] [ j ] , m [ i + 1 ] [ j − w [ i ] ] + v [ i ] ) m[i][j] = \max(m[i+1][j], m[i+1][j-w[i]] + v[i]) m[i][j]=max(m[i+1][j],m[i+1][jw[i]]+v[i])

chap 4 贪心

贪心策略的正确性证明:只有Dij算法与其他三个不同。

4.1活动安排

贪心策略

按结束时间非递减排序。每次选择与已选活动集相容且结束时间最早的活动。

正确性证明

在这里插入图片描述

代码

在这里插入图片描述

复杂度

O(nlogn)

4.2最优装载问题

贪心策略:重量最轻者先装。

证明:

在这里插入图片描述
代码:


算法主要计算量在排序。复杂度nlogn

4.3单源最短路径

贪心策略:

选V-S中特殊路径长度最短的顶点加入到S中。

证明:
在这里插入图片描述
代码:
在这里插入图片描述
在这里插入图片描述

4.4背包问题

策略:
在这里插入图片描述
证明:参考最优装载。形式化证明。

代码:
在这里插入图片描述
在这里插入图片描述

chap 5 回溯法

5.1 TSP

5.1.1解向量设计

用n元组x[1:n]表示旅行商问题的解。其中,x[i]表示旅行商经过的第i个城市编号为x[i]。

5.1.2剪枝策略
  1. 边权=infinity,此路不通,剪
  2. 记录之前已经到达了叶子节点的最短路径best。如果当前路径长度已经大于等于best,剪。
5.1.3时间复杂度分析

1. 若只需最短路径长度,时间复杂度为
2. 若还需具体路径,时间复杂度为(更新bestpath,需要从当前path逐个复制到bestpath)

5.1.4画树

在这里插入图片描述
note:

  1. 最开始的best=101是随便设的,实际上应为infinity
  2. 假设从1出发,就把根节点的其他孩子都剪了
  3. 回路应该再回到起始点,比如树上的2-3-4,完整应该是1-2-3-4-1,4-1的路径更新合并到了一步
    在这里插入图片描述
5.1.5 输出结果

(1,3,2,4),最短路程=25

5.2 n皇后

5.2.1 解向量设计
用n元组x[1:n]表示n后问题的解。其中,x[i]表示皇后i放在棋盘的第i行的第x[i]列。

5.2.2 剪枝策略
设两个皇后摆放的位置分别是(i, xi)和(j, xj)

  1. 由于不允许将2个皇后放在同一列上,所以解向量中的x[i]互不相同。 即 x i ≠ x j x_i \ne x_j xi=xj
  2. 由于不允许将2个皇后放在同一斜线上,所以 ∣ i − x i ∣ ≠ ∣ j − x j ∣ | i-x_i | \ne | j-x_j | ixi=jxj

5.2.3 时间复杂度分析
O ( n ! ) O(n!) O(n!)

5.2.4 画树

当n=4时,树如下:

note:在下图中找到一个可行解就停止了

在这里插入图片描述

5.2.5 输出结果

(2,4,1,3)

5.3 0-1背包

5.3.1 解向量设计
用n元组x[1:n]表示01背包的解。其中,x[i]取值为1或0,表示第i件物品放入或不放入背包。

5.2.2 剪枝策略

左子树:放不下,剪
在这里插入图片描述

右子树:上界小于已得到的最优值,剪
在这里插入图片描述

5.2.3 时间复杂度分析
在这里插入图片描述
5.2.4 画树

在这里插入图片描述

5.4 图的m着色

在这里插入图片描述

书名:算法设计分析 作者:王晓东 图书目录 第1章 算法引论 1.1 算法程序 1.2 表达算法的抽象机制 1.3 描述算法 1.4 算法复杂性分析 小结 习题 第2章 递归分治策略 2.1 速归的概念 2.2 分治法的基本思想 2.3 二分搜索技术 2.4 大整数的乘法 2.5 Strassen矩阵乘法 2.6 棋盘覆盖 2.7 合并排序 2.8 快速排序 2.9 线性时间选择 2.10 最接近点对问题 2.11 循环赛日程表 小结 习题 第3章 动态规划 3.1 矩阵连乘问题 3.2 动态规划算法的基本要素 3.3 最长公共子序列 3.4 凸多边形最优三角剖分 3.5 多边形游戏 3.6 图像压缩 3.7 电路布线 3.8 流水作业调度 3.9 0-1背包问题 3.10 最优二叉搜索树 小结 习题 第4章 贪心算法 4.1 活动安排问题 4.2 贪心算法的基本要素 4.2.1 贪心选择性质 4.2.2 最优子结构性质 4.2.3 贪心算法动态规划算法的差异 4.3 最优装载 4.4 哈夫曼编码 4.4.1 前缀码 4.4.2 构造哈夫曼编码 4.4.3 哈夫曼算法的正确性 4.5 单源最短路径 4.5.1 算法基本思想 4.5.2 算法的正确性和计算复杂性 4.6 最小生成树 4.6.1 最小生成树性质 4 6.2 Prim算法 4.6.3 Kruskal算法 4.7 多机调度问题 4.8 贪心算法的理论基础 4.8.1 拟阵 4.8.2 带权拟阵的贪心算法 4.8.3 任务时间表问题 小结 习题 第5章 回溯法 5.1 回溯法的算法框架 5.1.1 问题的解空间 5.1.2 回溯法的基本思想 5.1.3 递归回溯 5.1.4 迭代回溯 5.1.5 子集树排列树 5.2 装载问题 5.3 批处理作业调度 5.4 符号三角形问题 5.5 n后问题 5.6 0-1背包问题 5.7 最大团问题 5.8 图的m着色问题 5.9 旅行售货员问题 5.10 圆排列问题 5.11 电路板排列问题 5.12 连续邮资问题 5.13 回溯法的效率分析 小结 习题 第6章 分支限界法 6.1 分支限界法的基本思想 6.2 单源最短路径问题 6.3 装载问题 6.4 布线问题 6.5 0-1背包问题 6.6 最大团问题 6.7 旅行售货员问题 6.8 电路板排列问题 6.9 批处理作业调度 小结 习题 第7章 概率算法 7.1 随机数 .2 数值概率算法 7.2.1 用随机投点法计算л值 7.2.2 计算定积分 7.2.3 解非线性方程组 7.3 舍伍德算法 7.3.1 线性时间选择算法 7.3.2 跳跃表 7.4 拉斯维加斯算法 7.4.1 n后问题 7.4.2 整数因子分解 7.5 蒙特卡罗算法 7.5.1 蒙特卡罗算法的基本思想 7.5.2 主元素问题 7.5.3 素数测试 小结 习题 第8章 NP完全性理论 8.1 计算模型 8.1.1 随机存取机RAM 8.1.2 随机存取存储程序机RASP 8.1.3 RAM模型的变形简化 8.1.4 图灵机 8.1.5 图灵机模型RAM模型的关系 8.1.6 问题变换计算复杂性归约 8.2 P类NP类问题 8.2.1 非确定性图灵机 8.2.2 P类NP类语言 8.2.3 多项式时间验证 8.3 NP完全问题 8.3.1 多项式时间变换 8.3.2 Cook定理 8.4 一些典型的NP完全问题 8.4.1 合取范式的可满足性问题 8.4.2 3元合取范式的可满足性问题 8.4.3 团问题 8.4.4 顶点覆盖问题 8.4.5 子集和问题 8.4.6 哈密顿回路问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值