算法导论——lec 01

一、 指导思想


1. 核心是分析算法复杂性的方法:基础算法+分析;

2. 解决问题的思路是关键:方法 + 分析 + 应用;

3. 算法无止境: 提升,数学很重要;

4. 算法的应用:系统结构、内存访问、读写操作;

【实例1】:判断n个元素的数组A中是否有某个元素出现超过floor(n/2)次(超过即等于一半或者一半以上), 写出伪代码并分析算法复杂度。

 算法一: 比较每个元素,为每个元素计数,复杂度O(n*n)

 算法二: 先排序,然后一次扫描计数,复杂度O(n*logn)

 算法三: 分治,分两段分别找,合并时扫描计数,算法复杂度O(n*logn)

将数组分为两段,递归地检查每段是否存在某个元素的出现次数大于(n/2):如果两段都存在这样的数,当两者相等时,那么这个数就是所求的数。如果两段所求的结果不相等,那么就分别检查这两个数是否就是我们所求的元素。

代码:

#include <iostream>
#include <fstream>
#include <string>
#include <map>

using namespace std;

int algo(int *A, int low, int high)
{
	if(low == high)
		return A[low];
	int mid = (low + high)/2;
	int a1 = algo(A, low, mid);
	int a2 = algo(A, mid+1, high);
	if(a1 == a2 && a1 != -1)
		return a1;

	int cnt1 = 0, cnt2 = 0;
	for(int i = low; i <= high; i++)
	{
		if(A[i] == a1)
			cnt1++;
		if(A[i] == a2)
			cnt2++;
	}
	if(cnt1 >= (high-low+2)/2)
		return a1;
	if(cnt2 >= (high-low+2)/2)
		return a2;
	//cout<<"[logic error]"<<endl;
	return -1;
}

int main(int argc, char* argv[])
{
	int n, A[100];
	cin>>n;
	for(int i = 1; i <= n; i++)
		cin>>A[i];

	cout<<"using algo:"<<algo(A, 1, n)<<endl;

	return 0;
}



 算法四:分治,分两段,只在其中之一找。算法复杂度为O(n)。查找的时候只从一段找,对于值相同的数字尽量都放在同一个段里面。如果存在某个数出现次数超过一半,那么经过part算法之后,必然是存在于较长的一段里面。我们只需要选择较长的一段判断是否存在这样的数。无合并的过程。

int part(int *A, int low, int high)
{
	int tmp = A[low];
	int i = low, j = high;
	//if(A[i] == A[j])
		//return 0;
	while(i <= j)
	{
		while(A[j] > tmp)--j;
		A[i] = A[j];
		
		while(A[i] <= tmp)++i;
		A[j] = A[i];
	}
	A[j] = tmp;
	return j;//返回位置信息
}
int find(int *A, int n)
{
	int low = 1, high = n;
	while(low < high && high - low > (n+1)/2)
	{
		int x = part(A, low, high);
		if(high - x >= (n+1)/2)
			low = x+1;
		else if(high - x < (n+1)/2)
			high = x;
	}
	int cnt = 0;
	int tmp = A[low];
	for(int i = low+1; i <= high; i++)
		if(tmp == A[i])
			cnt++;
	if(cnt >= (n+1)/2)
		return tmp;
	else
		return -1;
}



 算法五:利用中位数算法,先找到中位数,再扫描判别中位数的出现次数是否超过一半。如果有超过一半的元素,那么这个元素也一定是中位数。算法复杂度为O(n);

int findK(int *A, int low, int high, int k)
{
	int tmp = A[low];
	int i = high, j = low;
	while(i <= j)
	{
		while(i <= j && A[i] >= tmp)--i;
		A[j] = A[i];
		
		while(i <= j && A[j] <= tmp)++j;
		A[i] = A[j];
	}
	A[i] = tmp;
	if(high - i + 1 == k)
		return A[i];
	else if(high - i + 1 > k)
		return findK(A, i+1, high, k);
	else if(high - i + 1 < k)
		return findK(A, low, i-1, k-high+i-1);
}


int find(int *A, int n)
{
	int tmp = findK(A, 1, n, (n+1)/2);
	int cnt = 0;
	for(int i = 1; i <= n; i++)
		if(A[i] == tmp)
			 cnt++;
	if(cnt >= (n+1)/2)
		return tmp;
	else 
		return -1;
}


 算法六:moore and boyer

第一遍扫描的时候,如果存在总数超过一半的元素,那么最后剩下的这个元素必定为这个元素;

第二遍扫描的时候就统计这个元素出现的次数,如果超过一半,那么就是这个元素,如果这个元素次数不超过一半,那么就不存在这样的元素。

code:

int find(int *A, int n)
{
	int p = 0, cnt = 0;
	for(i = 1; i <= n; i++)
		if(p == A[i])
			cnt++;
		else if(cnt > 0)
			cnt--;
		else if(cnt == 0){
			cnt++;
			p = A[i];
		}
	cnt = 0;
	if(cnt > 0)
	{
		for(i = 1; i <= n; i++)
			if(p == A[i])
				cnt++;
		if(cnt > n/2)
			return p;
	}
	return -1;
}


二、 算法的应用:

1. 算法是求解某个问题的长度有限的指令序列,每条指令都是确定的、简单的、机械的和可知性的。

2. 对于不同的解决方案,分析算法的复杂度。

3. 排序算法考虑的因素:待排数据项数,已经排好的程度,可能的取值范围,采用的存储设备类型。

    多项式计算:变元个数、次幂、系数范围等。

4. 评价算法:正确性、简单性、时间复杂性、空间复杂性、是否最优。

5. 时间复杂性归结为操作次数,具体操作次数与问题规模有关

6. 算法效率的比较:

【实例二】多项式乘法的效率

方法一:

复杂度分析:f(n) = f(n-1) + 2, f(0) = 0, f(1) = 2 

     f(n) = 2n;

code:

	p = a0;
	tmp = 1;
	for i = 1 to n
		tmp = tmp*x;
		p = p + ai*x;
	end
	print p;


方法二:

复杂度分析:f(n) = f(n-1) + 1; f(1) = 1;  

     f(n) = n;

code:

	p = an;
	for i = 1 to n;
		p = p*x + a(n-i)
	end
	print p;

三、 基本概念

1. 正确性:如果一个算对每一个输入实例都能输出正确的结果并停止,则称算法是正确的。

不正确的算法并不都是没用的:近似算法、模拟算法。

2. 可计算性:从理论上判断什么样的问题可以给出算法并利用计算机求解,什么样的问题不可以,属于可计算性理论的研究范围。

3. 算法分析:对一个算法所需要的资源进行预测,如内存、带宽、硬件、时间。

4. 计算模型:哥德尔的递归函数、丘奇的伽马演算、波斯特的波斯特机、图灵的图灵机、RAM模型。

在可解层次上,这些模型都是等效的。

5. 单处理器随机存取模型(RAM模型)

1)指令顺序执行;

2)算数指令、数据移动指令、控制指令;

3)数据类型:整数类型、浮点实数类型;

4)不考虑存储层次建模:高速缓存和虚拟内存;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值