leetcode解题思路分析(十三)85-91题

  1. 最大矩形
    给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

在84题中,我们求了一个柱状图中最大矩形。而本题只要首先把矩阵改变为一个显示每列最大长度的柱状图,即可用84题的栈方法求解

class Solution {
public:
    //leetcode 84那道题的求法
    int maxArea(vector<int>& matrix) {
        stack<int> s;
        s.push(-1);
        int max_area = 0, height, width;
        for(int i = 0; i < matrix.size(); ++i) {
            while(s.top() != -1 && matrix[i] <= matrix[s.top()]) {
                height = matrix[s.top()];
                s.pop();
                width = i - s.top() - 1;
                max_area = max(max_area, height*width);
            }
            s.push(i);
        }
        while(s.top() != -1) {
            height = matrix[s.top()];
            s.pop();
            width = matrix.size() - s.top() - 1;
            max_area = max(max_area, height*width);
        }
        return max_area;
    }

    int maximalRectangle(vector<vector<char>>& matrix) {
        if(matrix.size() == 0) return 0;
        int size = matrix[0].size(), max_area = 0;
        vector<int>dp(size, 0);
        for(int i = 0; i < matrix.size(); ++i) {
            for(int j = 0; j < size; ++j) {
                //逐行更新dp数组,dp[j]代表第j列的柱子高度
                //当matrix[i][j] == '0' ,dp[j] = 0,这很重要,仔细想一下
                dp[j] = matrix[i][j] == '1' ? dp[j] + 1 : 0;
            }
            //逐行更新此时连续柱体形成的矩形面积
            max_area = max(max_area, maxArea(dp));
        }
        return max_area;
    }
};


  1. 分隔链表
    给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
    你应当保留两个分区中每个节点的初始相对位置。

本题的解法是新建两个链表分别存放小于和大于等于的值,然后拼接两个链表即可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
    	if(!head || !head->next) return head;

    	ListNode* l_less_than = new ListNode(head->val);
    	ListNode* l_larger_than = new ListNode(head->val);

    	ListNode* l_less_than_bak = l_less_than;
    	ListNode* l_larger_than_bak = l_larger_than;
    	ListNode* head_bak = head;
    	head_bak = head_bak->next;
    	while(head_bak){
    		ListNode* new_node = new ListNode(head_bak->val);
    		if(head_bak->val < x){
    			l_less_than_bak->next = new_node;
    			l_less_than_bak = l_less_than_bak->next;
    		}else{
    			l_larger_than_bak->next = new_node;
    			l_larger_than_bak = l_larger_than_bak->next;
    		}
    		head_bak = head_bak->next;
    	}
    	if(head->val < x){
    		l_larger_than = l_larger_than->next;
    	}else{
    		l_less_than = l_less_than->next;
    	}

    	l_less_than_bak->next = l_larger_than;
//    	//for debug
////    	TraverseListNode(l_less_than_bak);
//    	TraverseListNode(l_less_than);

    	return l_less_than ? l_less_than : l_larger_than;
    }
};


  1. 扰乱字符串
    给出两个长度相等的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。

检测s2是否是s1的扰乱字符串,则检测s2的子串和s1的子串关系,再依次检测至整串即可,所以可以采用动态规划解决:我们用dp[i][j][L]表示S(i:i+L)和T(j:j+L)两个子串是否扰动

class Solution {
public:
    bool isScramble(string s1, string s2) {
        if (s1.size() != s2.size()) return false;
        if (s1.empty()) return true;
        int N = s1.size();
        vector<vector<vector<bool> > > dp(N + 1, vector<vector<bool> >(N, vector<bool>(N, false)));
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                dp[1][i][j] = s1[i] == s2[j];
            }
        }
        for (int len = 2; len <= N; ++len) {
            for (int i = 0; i < N && i + len - 1 < N; ++i) {
                for (int j = 0; j < N && j + len - 1 < N; ++j) {
                    for (int k = 1; k < len; ++k) {
                        if (dp[k][i][j] && dp[len - k][i + k][j + k]) {
                            dp[len][i][j] = true;
                            break;
                        }
                        if (dp[k][i][j + len - k] && dp[len - k][i + k][j]) {
                            dp[len][i][j] = true;
                            break;
                        }
                    }
                }
            }
        }
        return dp[N][0][0];
    }
};
  1. 合并两个有序数组
    给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
    因为第一个数组给出了尾部的额外空间,因此本题解法为从第一个数组后面开始比较两数组并填入,这样可以不需要额外空间,时间成本为遍历两个数组
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p = m - 1;
        int q = n - 1;
        int len = m + n - 1;
        while (p >= 0 && q >= 0) 
            nums1[len--] = nums1[p] > nums2[q] ? nums1[p--] : nums2[q--];
        while (q >= 0) 
            nums1[len--] = nums2[q--]; //最后 nums2有残留就一定是在最前面的
    }
};
  1. 格雷编码
    格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
    给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。

本题可以通过观察发现:对于一个dp[n - 1],dp[n]的前一半值其实是在dp[n - 1]前加一个0,而后一半则是dp[n - 1]的镜像前面加一个1,由此可以用动态规划求解

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> result(1);
        result[0] = 0;
        for(int i = 1; i <= n; i++){
            int e = 1 << (i - 1);                           //i - 1位结果前增加一位1
            for(int j = result.size() - 1; j >= 0; j--){    // 镜像排列
                result.push_back(e + result[j]);
            }
        }
        return result;       
    }
};
  1. 子集2
    给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
    说明:解集不能包含重复的子集。

本题也是采用回溯法求所有子集,但是和78题子集1的区别在于会存在重复元素,也因此会有重复的解,因此需要考虑剪枝

class Solution {
public:
    vector<vector<int>> result;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<int> tmp(nums.size());
        subsetsWithDup(nums, tmp, 0, 0);
        return result;
    }

    void subsetsWithDup(vector<int>& nums, vector<int>& tmp, int pos, int t_pos) {
        if (pos >= nums.size()) {
            result.emplace_back(vector<int>(tmp.begin(), tmp.begin() + t_pos));
            return;
        }
        tmp[t_pos] = nums[pos];
        subsetsWithDup(nums, tmp, pos+1, t_pos+1);     // 选择当前数
        while (pos+1 < nums.size() && nums[pos] == nums[pos+1]) pos ++;   // 不选择当前数时,后续相等的数也要跳过
        subsetsWithDup(nums, tmp, pos+1, t_pos);
    }
};


  1. 解码方法
    一条包含字母 A-Z 的消息通过以下方式进行了编码:
    ‘A’ -> 1
    ‘B’ -> 2

    ‘Z’ -> 26
    给定一个只包含数字的非空字符串,请计算解码方法的总数。

本题采用动态规划求解:数字小于26大于0,则dp[i] = dp[i - 1] + dp[i - 2],否则dp[i] = dp[i - 2]

class Solution {
public:
int numDecodings(string s) {
    if (s[0] == '0') return 0;
    int pre = 1, curr = 1;//dp[-1] = dp[0] = 1
    for (int i = 1; i < s.size(); i++) {
        int tmp = curr;
        if (s[i] == '0')
            if (s[i - 1] == '1' || s[i - 1] == '2') curr = pre;
            else return 0;
        else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6'))
            curr = curr + pre;
        pre = tmp;
    }
    return curr;
}
};
发布了129 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/104456877