Learning of Algorithm

本文是作者关于算法学习的个人笔记,主要探讨了动态规划的基本思想,以斐波那契数列为实例,解释了如何通过递归和记忆化搜索减少重复计算,以及如何使用滚动数组进一步优化空间复杂度。动态规划的核心在于正确地建立递推关系,将大问题分解为小问题并组合解决,同时在时间和空间之间做出权衡。
摘要由CSDN通过智能技术生成

Learning of Algorithm

Author:CheasonY

Preface

It’s my own private learning note of algorithm. I arrange all these chapters based on my own thoughts and method of cognition. Last sentence meant to point that this whole note was extremely private, which also means it may be lack of systematic and professional.

* b.t.w, I’m not an English-Speaker with strong willing to exercise the ability of Writing in English. That’s why this whole note written by English and unavoidable to maintain some Chinese express and nicknames.

DP(Dynamic Programming)

This chapter is my summary of all efforts and earns in learning Dynamic Programming.

Basic Idea

Recurrence Relation/State Transfer Equation

The core idea of DP is writing correct Recurrence Relation(State Transfer Equation) to split a enormous problem with plenty of repeated computation into many less(or said easier) pieces(i.e. which I called children problem). Then we can solve the initial problem via combining all the children problems.

Instance—Fibonacci Sequence

I have first met the Fibonacci Sequence when I’m a silly high school student while I’m playing mud and learning recursion algorithm at the same time. Fibonacci Sequence can be described through the 0-indexed equation below(The index i in f i f_{i} fi refers to the position of the element):
{ f 0 = 0 f 1 = 1 f n = f n − 1 + f n − 2 , n ≥ 2 \begin{cases} f_{0}=0 \\ f_{1}=1 \\ f_{n}=f_{n-1}+f_{n-2}, n\geq2 \end{cases} f0=0f1=1fn=fn1+fn2,n2
We can easily solve this problem through using recursion algorithm:

int feib(int n){
	// Boundary to end up the children-problem
	if (n==0 || n==1){
		return n;
	}
	return feib(n-1)+feib(n-2);
}

But actually there were plenty of repeated computation in the algorithm above. And the less index of children problem is, the more repeated computation it caused.

e.g. We need compute f ( 4 ) f(4) f(4) and f ( 3 ) f(3) f(3) to get f ( 5 ) f(5) f(5), while we need once more computation of f ( 3 ) f(3) f(3) to get f ( 4 ) f(4) f(4)

It can be proved that as the n large enough, there will cost super-sized waste of time. More professionally, the Time Complexity and the Space Complexity of “Violent Solution by Recursion”(i.e. A joked name for silly solution in Chinese) was Θ ( ( 1 + 5 2 ) n ) \Theta((\frac{1+\sqrt{5}}{2})^n) Θ((21+5 )n) and Θ ( 1 ) \Theta(1) Θ(1). More analysis can be found in 递归暴力求解斐波那契数列的时间复杂度

To cut down the waste of time in this situation, the “easiest” method in DP has been given a birth with the name called Recursion with a Memory Array.

This means we will push every element which was first to be computed into an array. So that we can get the value from the array directly when we need compute it later. It was always coded through Hash List. Which means we can generate a hash list F to store each value of f ( n ) f(n) f(n):

map<int, int> F;

int feib(int n){
	// Boundary to end up the children-problem
	if (n==0 || n==1){
		F[n] = n;
		return n;
	}
	
	// First be computed
	if (!F.count(n)){
		F[n] = feib(n-1)+feib(n-2);
	}
	
	return F[n];
}

Let’s re-discuss the Time Complexity and the Space Complexity after the optimization above. As for the time complexity, every f ( n ) f(n) f(n) need and only need one computation here, while we are solving children problems through a method similar to traversing a binary tree. The image below may help us figure it out more clearly.
在这里插入图片描述

As we talked above, it can be proved that the time complexity here was Θ ( n 2 ) \Theta(n^2) Θ(n2). It has improved a lot from exponential cost in time caused by simple recursion. However, boost always need cost, the space complexity here has been increased to Θ ( n ) \Theta(n) Θ(n) from original Θ ( 1 ) \Theta(1) Θ(1) without any extra cost in space. It also told as Trades-off between time and space(i.e. comes from “空间换时间” in Chinese).

This instance tells us that DP focus on solving a problem by splitting it to many children problems, then deal with them and combine them all up in a Limited Linear Time. More mathematically, the problem we face to can be describe as a kind of “state”, and what we need is the value of current state. What we need to do is combing previous states through correct Recurrence Relation/State Transfer Equation.

Based on all we talked above, DP can be easily regarded as Recursion Algorithm with Linearly Optimization.

Space Optimization—Scrolling Array

Still talking about the instance above(i.e. Fibonacci without any doubt). It’s not difficult to find that our computation was and only was related to three variables through the General Equation when n ≥ 2 n\geq2 n2, which were f ( n ) f(n) f(n), f ( n − 1 ) f(n-1) f(n1) , f ( n − 2 ) f(n-2) f(n2). These three guys will scroll left after each single computation due to their orderly, which means current f ( n − 1 ) f(n-1) f(n1) was the f ( n ) f(n) f(n) in computation at last time. Similarly, current f ( n − 2 ) f(n-2) f(n2) was the f ( n − 1 ) f(n-1) f(n1) in computation at last time.

It can be viewed as all Elements in an array keep scrolling left. It can be obviously described by the equation below. (Suppose that the superscript i refers to the times of operation and the subscript 0 、 1 、 2 0、1、2 012 refer to the index of the element.)
{ f 0 i + 1 = f 1 i f 1 i + 1 = f 2 i f 2 i + 1 = F ( f 0 i , f 1 i ) \begin{cases} f_{0}^{i+1}=f_{1}^{i} \\ f_{1}^{i+1}=f_{2}^{i} \\ f_{2}^{i+1}=F(f_{0}^{i}, f_{1}^{i}) \end{cases} f0i+1=f1if1i+1=f2if2i+1=F(f0i,f1i)
*Addition: The function F here refers to the State Transfer Equation

Here’s a template codes for this idea:

int p = 0;
int q = f[0];
int r = f[1];

for (int i = 2; i <= n; i++){
	p = q;
	q = r;
	r = F(p, q);
}

Then we can get f n f_{n} fn from r when n more than 2. The obstacle of this method may focus on how to confirm the initial value and need talk about the boundary specially.

Applying this method to DP problems can always cut down a plenty of waste in space. More specifically, Space Complexity here is Θ ( 1 ) \Theta(1) Θ(1).

Review the argument about the Fibonacci Sequence, we can optimize the codes by using “Scrolling Array”:

int p = 0;
int q = 0;
int r = 1;

int feib(int n){
	if (n==0||n==1){
		return n;
	}
	
	// Start Scrolling!
	for(int i = 2; i <= n; i++){
		p = q;
		q = r;
		r = p+q;
	}
	return r;
}

We can also figure out that this method make the Time Complexity turning to Θ ( n ) \Theta(n) Θ(n).

Some Classical Problems(Easy)

1) Climbing to the DP paradise(LeetCode-70)

Problem Description:
You are climbing a staircase to DP paradise. It takes n steps to reach the paradise. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Solving Ideas
First let’s try to confirm which value we care about. Obviously the only value here we care about is the number of climbing ways. Suppose that f ( n ) f(n) f(n) specifies the number of climbing ways to reach the place indexed by n.

Then the answer(or said as current state) was clearly f ( n ) f(n) f(n). Meanwhile each step has only two possible value — 1 or 2. It implies we can reach n as we can reach n-1 or n-2.

So we get a recurrence relation here:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2)
* Here it can be told as just a clothed Fibonacci Sequence.

Solving Codes(Optimized with Scrolling Array)

int climbStairs(int n) {
 // Scrolling Array
 int p = 0;
 int q = 0;
 int r = 1;

 for ( int i = 1; i <= n; i++ ){
            p = q;
            q = r;
            r = p+q;
        }  

 return r;
}

2) House Robber Ⅰ(LeetCode-198)
* It’s a really classical series of problems that lead me to the world of DP!!! “打家劫舍” must own its original Chinese name!!!

Problem Description:
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

Solving Ideas

Kadane’s Algorithm

Jay Kadane in CMU first found out this algorithm in 1984. It’s a classical algorithm based on DP thoughts with a long history.

Prompt-Born Problem

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码小狗Codog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值