算法设计与分析复习一轮

算法的基本性质

算法的定义:算法是解某一特定问题的一组有穷规则的集合

算法的特性

  • 有限性
  • 确定性(指的是每个动作都是确定的,并非执行结果确定)
  • 输入
  • 输出
  • 能行性

用算法的复杂性衡量一个算法的效率

程序可以不满足算法的有限性

算法研究的核心问题是时间(速度)问题

算法的描述方法:自然语言、流程图、程序设计语言、伪码

算法设计的过程:问题分析、建立数学模型、算法设计与选择(结合数据结构,解决问题的核心)、算法分析、算法实现、程序调试、编制整理文档

初等操作

运行时间的上界: O O O
运行时间的下界
运行时间的准确界

分治法

分治法的求解过程:划分子问题、求解子问题、合并子问题

适用条件

  • 问题可以分解为若干规模较小、相同性质子问题(最优子结构)
  • 子问题易于求解
  • 子问题的解可以合并为原问题的解
  • 各个子问题相互独立,即子问题之间不包含公共的子问题

独立子问题:各子问题之间相互独立
平衡子问题:使子问题的规模大致相同

贪心

最优子结构、自顶向下、局部最优解

基本要素:贪心选择性质和最优子结构性质

dp

基本要素:子问题重叠性质和最优子结构性质

dp 的基本思想

  • 重叠子问题
  • 多阶段决策

dp 的前提条件

  • 最优化原理/最佳原则/最优子结构
  • 无后向性/无后效性

记忆化搜索 = 递归 + 备忘录

dp 基本步骤

  • 找出最优解的性质,并刻画其结构特征
  • 递归地定义最优值
  • 以自底向上地方式计算出最优值
  • 根据计算最优值时得到的信息,构造最优解

简化步骤

  • 分析最优解的性质,并刻画其结构特征
    • 寻找更小的问题
  • 递归地定义最优值
  • 以自底向上的方式或自顶向下地记忆化方法(备忘录法)计算出最优值
  • 根据计算最优值时得到的信息,构造问题的最优解

与分治法的相同点:都具有最优子结构性质
与分治法的区别:dp 适用于分解得到的子问题往往不是相互独立的(重叠子问题)

与贪心的相同点:都具有最优子结构性质
与贪心的区别:dp 往往依赖相关子问题的解,而贪心选择局部最优,再去解决相关子问题;dp 往往自底向上,而贪心往往自顶向下

数塔问题求解的特点

  • 多阶段决策问题:将全过程求解分为若干阶段求解
  • 递推性:在全过程最优路径中,将会出现阶段的最优路径
  • 无后效性:前面确定的路径不会受后面计算的影响,后面的结果依据前面的最优结果来确定
  • 动态规划:逐段求解最优路径,势必会找到一个全过程最优路径

快速排序

  1. 确定分界点 x x x
    1.1 q [ l ] q[l] q[l]
    1.2 q [ r ] q[r] q[r]
    1.3 q [ l + r + 1 > > 1 ] q[l+r+1>>1] q[l+r+1>>1]
    1.4 随机
  2. 调整区间:将所有小于等于 x x x 的数放在左边,大于等于 x x x 的数放在右边
  3. 递归处理左右两段

代码

void quick_sort(int q[],int l,int r)
{
	if(l>=r)return ;
	int i=l-1,j=r+1;
	//int x=q[l+r+1>>1];
	int x=q[l+rand()%(r-l+1)];
	while(i<j)
	{
		do i++;while(q[i]<x);
		do j--;while(q[j]>x);
		if(i<j)swap(q[i],q[j]);
	}
	quick_sort(q,l,j);
	quick_sort(q,j+1,r);
}

稳定性:不稳定

基数排序

是一种非比较性整数排序算法

算法思想:将整数按位切割为不同数字,按数字排序

排序方式:MSD(从高位开始排序,可采用计数排序)和 LSD(从低位开始排序,可采用桶排序)

复杂性分析
设最大位数为 d d d,基数为 r r r(十进制为 10 10 10),则时间复杂度为 O ( d × ( n + r ) ) O(d\times (n+r)) O(d×(n+r)),空间复杂度为 O ( r + n ) O(r+n) O(r+n)

代码

int a[100005],tmp[100005],cnt[10],n;
int max_bit(int a[],int n)
{
    int d=1,p=10;
    for(int i=1;i<=n;i++)
        while(a[i]>=p)
        {
            d++;
            p*=10;
        }
    return d;
}
void radix_sort(int a[],int n)
{
    int d=max_bit(a,n);
    int radix=1;
    for(int i=1;i<=d;i++)
    {
        memset(cnt,0,sizeof cnt);
        for(int j=1;j<=n;j++)cnt[(a[j]/radix)%10]++;
        for(int j=1;j<10;j++)cnt[j]=cnt[j]+cnt[j-1];
        for(int j=n;j;j--)
        {
            int k=(a[j]/radix)%10;
            tmp[cnt[k]]=a[j];
            cnt[k]--;
        }
        memcpy(a,tmp,sizeof tmp);
        radix*=10;
    }
}

稳定性:稳定

时间复杂度递推方程的求解

Hanoi

算法:

Hanoi(A,C,n)将 A 柱上的 n 个盘子移动到 C 柱上
{
	if(n==1)
		move(A,C);//将 A 柱上的 1 个盘子移到 C 柱上
	else
	{
		Hanoi(A,B,n-1);
		move(A,C);
		Hanoi(B,C,n-1);
	}	
}

设移动 n n n 个盘子的移动次数为 T ( n ) T(n) T(n),则可得到 Hanoi 时间复杂度的递推方程:
T ( n ) = 2 × T ( n − 1 ) + 1 T(n)=2\times T(n-1) +1 T(n)=2×T(n1)+1

迭代计算,即 T ( n ) = 2 × ( 2 × T ( n − 2 ) + 1 ) + 1 = ⋯ = 2 n − 1 × T ( 1 ) + 1 + 2 + ⋯ + 2 n − 2 = 2 n − 1 T(n)=2\times (2\times T(n-2)+1)+1=\dots =2^{n-1} \times T(1)+ 1+2+\dots +2^{n-2}=2^n-1 T(n)=2×(2×T(n2)+1)+1==2n1×T(1)+1+2++2n2=2n1
最后用数学归纳法代入验证: T ( n ) = 2 × ( 2 n − 1 − 1 ) + 1 = 2 n − 1 T(n)=2\times (2^{n-1}-1)+1=2^n-1 T(n)=2×(2n11)+1=2n1

归并排序

T ( n ) T(n) T(n) 为长度为 n n n 的数组排序需要比较的次数,得时间复杂度的递推表达式 T ( n ) = 2 × T ( n / 2 ) + n − 1 T(n)=2\times T(n/2) +n-1 T(n)=2×T(n/2)+n1,令 n = 2 k n=2^k n=2k,代入,得 T ( 2 k ) = T ( 2 k − 1 ) + 2 k − 1 T(2^k)=T(2^{k-1})+2^k-1 T(2k)=T(2k1)+2k1,迭代计算,得 T ( 2 k ) = ( k − 1 ) × 2 k − 1 T(2^k)=(k-1)\times 2^k-1 T(2k)=(k1)×2k1,将 k = l o g n k=logn k=logn 代入,得
T ( n ) = n l o g n − n + 1 T(n)=nlogn-n+1 T(n)=nlognn+1

非递归程序编写

二叉树遍历方式

  • 前序遍历
    非递归写法
    vector<int> res;
    void preOrder(TreeNode *root)
    {
        if(root==NULL)return ;
        TreeNode *p=root;
        stack<TreeNode*> stk;
        while(p!=NULL || stk.size())
        {
            while(p!=NULL)
            {
                res.emplace_back(p->val);
                stk.emplace(p);
                p=p->left;
            }
            if(stk.size())
            {
                p=stk.top();
                stk.pop();
                p=p->right;
            }
        }
    }
  • 中序遍历
    非递归写法
    vector<int> res;
    void midOrder(TreeNode* root)
    {
        if(root==NULL)return ;
        stack<TreeNode*> stk;
        TreeNode* p=root;
        while(p!=NULL || stk.size())
        {
            while(p!=NULL)
            {
                stk.emplace(p);
                p=p->left;
            }
            if(stk.size())
            {
                p=stk.top();
                res.emplace_back(p->val);
                stk.pop();
                p=p->right;
            }
        }
    }
  • 后序遍历
    非递归写法
    vector<int> res;
    void postOrder(TreeNode* root)
    {
        if(root==NULL)return ;
        stack<TreeNode*>stk;
        TreeNode* p=root,*pre;
        while(p!=NULL || stk.size())
        {
            while(p!=NULL)
            {
                stk.emplace(p);
                p=p->left;
            }
            p=stk.top();
            stk.pop();
            if(p->right==NULL || p->right==pre)
            {
                res.emplace_back(p->val);
                pre=p;
                p=NULL;
            }
            else
            {
                stk.emplace(p);
                p=p->right;
            }
        }        
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小z吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值