动态规划——最长递增子序列

1、最长上升子序列

状态转移

dp[i]=max(dp[j])+1 其中 0≤j

dp[i]代表0-i之间的序列中最长上升子序列的长度

程序分析

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {      
        int n=nums.size();
        if(n==0)    return 0;
        if(n==1)    return 1;

		//初始化数组并设定base case
        vector<int> dp(n,1);
        int res=0;
        
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){
                    dp[i] = max(dp[i],dp[j]+1);
                }
            }
            //更新最长子序列长度
            res = max(res,dp[i]);
        }
        return res;
    }
};

复杂度

时间复杂度:O(N^2)
空间复杂度:O(N)

时间优化 O(NlogN)

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 1, n = (int)nums.size();
        if (n == 0) return 0;

		//初始化数组与base case
        vector<int> d(n + 1, 0);
        d[len] = nums[0];
        
        for (int i = 1; i < n; ++i) {
        //特殊情况一直递增
            if (nums[i] > d[len]) d[++len] = nums[i];
            else{
         //不递增则二分查找,进行插入与更新
                int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    }
                    else r = mid - 1;
                }
                d[pos + 1] = nums[i];
            }
        }
        return len;
    }
};

2、最长数对链

状态转移

dp[i] = max(dp[i], dp[j]+1);

程序分析

动态规划

class Solution{
public:
	int findLongestChain(vector<vector<int>>& pairs){
		if(pairs.empty())	return 0;
		
		 //区间排序:按右端点进行排序,若右端点不相等,按右端点由小到大进行排序;若右端点相等,则按左端点由小到大进行排序
		sort(pairs.begin(),pars.end(),[](const auto& a,const auto& b){
			return (a[1]<b[1] || (a[1]==b[1] && a[0]<b[0]);
		});

		int n = pairs.size();
		int res = 0;
		vector<int> dp(n,1);
		for(int i=0;i<n;i++){
			for(int j=0;j<i;j++){
				if(pairs[j][1]<pairs[i][0])
					dp[i] = max(dp[i],dp[j]+1);
			}
			res = max(res,dp[i]);
		}

	  return res;
	}

};

动态规划+二分查找

 int findLongestChain_2(vector<vector<int>>& pairs){
        if(pairs.empty())return 0;
        sort(pairs.begin(),pairs.end(),[](const auto& a,const auto& b){
            return (a[0]<b[0])||(a[0]==b[0]&&a[1]<b[1]);
        });
        vector<vector<int>> dp;
        for(auto& p:pairs){
            //二分法寻找大于等于p[0]的最小值dp[i][1]
            int left=0,right=dp.size();
            while(left<right){//进入while循环区间内至少有2个元素,退出循环的极值只有0或size
                int mid=left+((right-left)>>1);
                if(dp[mid][1]>=p[0])right=mid;
                else left=mid+1;
            }
            //dp[size-1][1]
            if(left>=dp.size())dp.emplace_back(p);
            //dp[left][1]大于(等于)p[0]同时也大于p[1],那么我们更新dp[left]为p,这样可以将left变小,以便形成最长的数对链
            else if(dp[left][1]>p[1])dp[left]=p;
        }
        return dp.size();
    }

区间贪心法

  int findLongestChain(vector<vector<int>>& pairs){
        if(pairs.empty())return 0;
        //区间排序:按右端点进行排序,若右端点不相等,按右端点由小到大进行排序;若右端点相等,则按左端点由小到大进行排序
        sort(pairs.begin(),pairs.end(),[](const auto& a,const auto& b){
            return (a[1]<b[1])||(a[1]==b[1]&&a[0]<b[0]);
        });
        //count初始化为1,用来统计不重复子区间个数的
        int count=1,end=pairs[0][1];
        for(const auto& p:pairs){
            if(p[0]>end){//区间不相交,需要更新边界以及不重复区间个数,注意不能有等号,即区间端点不能连续
                count++;
                end=p[1];
            }
        }
        return count;
    }

3、摆动序列

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size() == 0){
            return 0;
        }
        vector<int> up(nums.size(), 1); // 所有元素, 单独也构成一个摆动序列, 并且长度为 1
        vector<int> down(nums.size(), 1);

        for(int i = 1; i < nums.size(); i++)
            for(int j = 0; j < i; j++)
                if(nums[j] > nums[i]) // 说明 nums[j] -> nums[i] 是一个下降的过程, 那么只有在最长摆动序列中 nums[i] 位置的元素属于上升元素时, 此处的 nums[i] 才可以把它给延长, 并且延长后 nums[i] 位置本身是属于下降的元素
                    down[i] = max(down[i], up[j] + 1); // 
                else if(nums[j] < nums[i])
                    up[i] = max(up[i], down[j] + 1);
                // 相同的元素直接跳过

        int ret = 1;
        for(int i = 0; i < nums.size(); i++){ // 从记录子找到最长的, 这部分代码也可以融入前面的动态规划中, 动态及更新全局最大值
            ret = max(ret, down[i]);
            ret = max(ret, up[i]);
        }
        return ret;
    }
};
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size() == 0){
            return 0;
        }
        int up=1,down=1;
        for(int i=1;i<nums.size();i++){
            if(nums[i]>nums[i-1]){
                up = down +1;
            }
            else if(nums[i]<nums[i-1]){
                down = up+1;
            }
        }
        return max(up,down);
    }
};

你可能感兴趣的:(数据结构与算法总结)