算法自学

算法自学,第1张

算法自学

参考资料:

https://www.zhihu.com/question/39948290https://blog.csdn.net/zw6161080123/article/details/80639932 算法引入

我们先考虑一个经典问题:
小明要爬上一个n级的台阶, 他每次可以走1级或2级, 求不同的走法数.

经典递归算法

代码:

int solve(int n) {
	if (n == 1)return 1;
	else if (n == 2)return 2;
	else return solve(n - 1) + solve(n - 2);
}
int main() {
	int n;
	cin >> n;
	cout << solve(n);
	return 0;
}

时间复杂度: O(2^n)
我们可以发现, 上述算法包含着大量的重复计算:
假设n=5, 根据上述算法, 我们需要先计算solve(4)和solve(3)
而要计算solve(4), 必须先计算solve(3)和solve(2)
我们注意到solve(3)被计算了两次.
当问题规模更大时, 重复计算会更多, 导致问题不能在理想的时间内得到解决.

动态规划算法

如果我们将已经解决的问题的结果保存下来, 在解决新问题时直接调取旧问题的结果, 就能节省不少时间, 这就是最简单的动态规划

#include
#include
using namespace std;
int dp[100];
void solve(int n) {
	if (n <= 2) return;
	else {
		for (int i = 3; i <= n; i++) {
			dp[i] = dp[i - 1] + dp[i - 2];
		}
	}
}
int main() {
	dp[1] = 1, dp[2] = 2;
	int n;
	cin >> n;
	solve(n);
	cout << dp[n];
	return 0;
}

时间复杂度: O(n)

动态规划算法 能用动态规划解决的问题的特点

最优化原理: 问题的最优解所包含的子问题的解也是最优的.无后效性: 某个阶段的决策一旦确定, 就不受后续决策的影响.有重叠的子问题(该性质不是必须的, 但是若没有这条性质, 动态规划算法将不具有优势). 算法实现的步骤

    创建一个数组(数组的维数与问题相关), 用以保存子问题的结果.设置数组的边界值.求出状态转移方程, 即找到每个状态与上一个状态的关系.返回需要的值.
经典例题 1.最长不连续递增子列

对应题目: LeetCode第300题

代码实现:

int lengthOfLIS(int* nums, int numsSize){
    int dp[2505];
    for(int i=0;inums[j]){
                dp[i]=fmax(dp[i],dp[j]+1);		//状态转移方程
            }
        }
    }
    int max=0;
    for(int i=0;imax) max=dp[i];
    }
    return max;
}
// 时间复杂度O(n^2)

算法改进:
定义tail数组, tail[i]表示: 长度为i+1的所有递增子列的最小尾元素.
我们容易直到, tail数组是严格单调递增的.
状态转移定义为:

    若当前元素e大于tail[i], 则tail[i+1]=e(得到了一个长度为i+2的递增子列, 最小尾元素为e)若当前元素e小于等于tail[i], 二分查找到tail数组中第一个大于e的数字, 并将其更新为e

当我们遍历整个数组后, tail数组中的元素个数即为最长递增子列的长度.

代码实现:

class Solution {
public:
    int lengthOfLIS(vector& nums) {
        int tail[2505];
        tail[0]=nums[0];
        int ans=1;
        int len=nums.size();
        for(int i=1;itail[ans-1]){
                tail[ans++]=nums[i];
            }
            else if(nums[i]==tail[ans-1]) continue;
            else{
                int L=0,R=ans-1,flag=0;
                while(L<=R){
                    int mid=(L+R)>>1;
                    if(nums[i]>tail[mid]) L=mid+1;
                    else{
                        flag=mid;
                        R=mid-1;
                    }
                }
                if(nums[i]==tail[flag]) continue;
                tail[flag]=nums[i];
            }
        }
        return ans;
    }
};
//时间复杂度O(nlogn)
2. 数组最大连续子序列和

对应题目: LeetCode第53题

代码实现:

class Solution {
public:
    int maxSubArray(vector& nums) {
        int len=nums.size();
        int dp[100005];
        dp[0]=nums[0];
        int max=dp[0];
        for(int i=1;imax) max=dp[i];
        }
        return max;
    }
};

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5714028.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存