第8章 高效算法设计读书笔记

算法分析初步

8.1.1 渐进时间复杂度 ——最大连续和,给出一个序列,找到i,j使连续和尽量大

               使用枚举思想:枚举每一个可能的序列首和序列尾

for( i = 1; i <= n; i++){
	for( j = i; j <= n; j++){
		int sum = 0;
		for( k = i; k <= j; k++){           // 求和
			sum += a[k];
			tot++;                      // 计算基本操作的数量,T(n)和n的3次方同阶
		}
		if( sum > best) best = sum;
	}
}
8.1.2 上界分析 ——三重循环,最坏情况下内层循环需要n次,故T(n) = O(n的3次方)(上界)

              对这个程序进行简化:连续子序列之和等于两个前缀和之差

for( i = 1; i <= n; i++){
	for( j = i; j <= n; j++){
		best = max( s[j] - s[i-1], best);  // 遍历数组,更新best值,T(n)和n的2次方同阶
	}
}
8.1.3 分治法 ——分治法一般分为3个步骤

              1、划分问题:划分成子问题 ;     把序列分成个数相等的两半

              2、递归问题:递归解决子问题;  分别求出完全位于左半或者右半的最佳序列

              3、合并问题:合并子问题的解      求出起点位于左半终点位于右半的序列,并和子问题最优解比较

                     递归方程 T(n) = 2T(n/2) + O(n); T(1) = 1;  的解是T(n) = O(n log n)

再谈排序和搜索

8.2.1 归并排序 ——也可以用分治法分成三个步骤

              1、划分问题:划分成子问题 ;     把序列分成个数相等的两半

              2、递归问题:递归解决子问题;  两半分别排序

              3、合并问题:合并子问题的解      把两个有序表合并成一个

#include<stdio.h>
void sort( int* a, int x, int y, int* t);
int a[] = { 20, 12, 13 };
int t[100];
int main(void){
	int i;
	sort( a, 0, 3, t);							// 第三个参数是3确保指向空位置 
	for( i = 0; i < 3; i++) printf("%d ", a[i]);
	return 0;
}
void sort( int* a, int x, int y, int* t){
	int m, p, q, i;
	if( y-x > 1){								// xy之间序列不为空 
		m = x + (y-x) / 2;						// 划分 
		p = x, q = m, i = x;					// 三个指针 
		// 事实上p和q都好理解,i = x 可以理解为这是将由x为起点的数组重新排序 
		sort( a, x, m, t);						// 左递归 
		sort( a, m, y, t);						// 右递归 
		while( p < m || q < y){					// 序列非空、继续合并 
			if( q >= y || ( p < m && a[p] <= a[q])){
		/* 
		   q >= y : 右序列为空(此时左序列不为空)
			或 
		   p < m && a[p] <= a[q])
		   p < m  : 左序列不为空 且 
		   a[p] <= a[q] :  左小于右
		   (此时认为p为左序列最小值,而q为右序列最小值) 
		*/ 
				t[i++] = a[p++];				// 把P赋值给t[i],并比较下一个元素 
			}
			else{
				
		/*
			右序列不为空 或 左大于右 
		*/ 
				t[i++] = a[q++];				// 把P赋值给t[i],并比较下一个元素 
			}
		}
		for( i = x; i < y; i++){				// 赋值回函数 
			a[i] = t[i];
		}
	} 
}
                    逆序对数(尚没有仔细看懂)

8.2.2 快速排序 ——暂时略

8.2.3 二分查找 ——折半查找的思想,不过只针对有序序列

#include<stdio.h>
void sort( int* a, int x, int y, int* t);                              // 利用之前的分治方法进行排序
int main(){
	int a[9] = { 20, 12, 13, 18, 19, 4, 7, 16, 5};
	int t[100];
	int x, y, m, v, i, num;
	scanf("%d", &m);
	sort( a, 0, 9, t);
	for( i = 0; i < 9; i++) printf("%d ", a[i]);
	printf("\n");
	x = 0; y = 8;
	num = 0;
	while( x < y){                                                 // 一般二分查找写成非递归形式
		v = x + (y-x) / 2;
		if( a[v] == m){
			printf("序号是%d\n", v+1);
			break; 
		}
		else if( a[v] <= m) x = v;		
		else y = v;
	}
	return 0;
}
void sort( int* a, int x, int y, int* t){
	int m, v, i, p, q;
	if( y - x > 1){
		m = x + (y-x) / 2;
		sort( a, x, m, t);
		sort( a, m, y, t);
		p = x; q = m; i = x;
		while( p < m || q < y){
			if( q >= y || ( p < m && a[p] < a[q])) t[i++] = a[p++];
			else t[i++] = a[q++];
		}
		for( i = x; i < y; i++) a[i] = t[i];
	}
}
                 如果序列中有重复,且重复的为所求数字,那么用原来的二分法就只能求得最中间的位置,需要进行一些小的修改

	while( x < y){                                  // 二分查找求上界
		v = x + (y-x) / 2;
		if( a[v] <= m){
			 x = v+1;			// 此时这里很重要,否则会发生死循环 
		}		
		else y = v;				// 若求下界则是当相等时改变y 
	}
                 范围统计:给出一个序列,以及m个询问,对于每个询问(a, b),求闭区间 [a,b] 内个数

                        即求 a 所在的下界 L 和 b 所在的上界 R ,而后求区间 [ L, R]长度

递归与分治 ——【TBC】

贪心法 ——排序、取当前最优解

8.4.1 最优装载问题 ——装轻的比装重的划算,故将所有物体按重量从小到大排列

8.4.2 部分背包问题 ——部分选取,一定能让总重量恰好为C

8.4.3 乘船问题 ——两个下表i,j来表示当前最重和最轻的人

              If(最轻的人+最重的人 标准重) { num++; i++; j--;} //浪费最少

              Else { num++; j--;} //此时最重的只能自己乘一条船,往下寻找能和最轻的人同乘的 最重的人

8.4.4 选择不相交区间 —— 有n个区间(Ai, Bi)。如果区间a能够完全覆盖b则选取a就是 不划算的,依据Bi将所有区间排序,那么第一个元素一定是在最优解中

             排序后B1 < B2

                     ①A1 >= A2时,区间1被区间2完全包含,所以选取区间2是不划算的

                     ②A1 < A2时,区间1中位于A2左边的部分对结果并没有影响,真正有影响 的是区间A2B1部分,这一部分被区间2完全包含,所以选取区间2是不划算的

              所以应该选取第一个区间,然后需寻找此后与此区间不相交的第一个区间

8.4.5 区间选点问题 ——小区间被满足时大区间也会被满足,所以在区间包含的情况下大区 间不用考虑。依据Bi将所有区间排序,然后取最后一个点

8.4.6 区间覆盖问题 ——进行一个预处理,所有区间在线段[s,t]外的部分都切掉,因为存在 无意义。依据Ai将所有区间排序,如果第一个区间起点不是s,则无解。否则选取最长区间,随后设置此区间尾Bi为新的起点。

8.4.7 Huffman编码 ——证明用Huffman树可以得出最优解需要以下两个结论:

                    ①如果编码a是编码b的前缀,那么a对应的结点一定为b所对应结点的祖先。 而两个叶结点不存在祖先后代的关系

                    ②最有前缀码一定可以写成二叉树

              构造一颗最优编码树(Huffman算法):每棵子树权值等于相应字符的频率,每次 选择权值最小的树组成一颗权值为两树之和的新树,放回集合中。满足以下两个性质

                    ①设x,y是使用频率最小的两个字符,存在前缀码使二者具有相同码长,仅有 最后一位不同(保留了最优解)

                    ②设zx,y的父结点,把z看成频率具有x,y频率之和的字符,那么此树是 包含z而无x,y字符的集合的最优解(最优子结构性质)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值