leetcode刷题记录---2019.9.8 动态规划最长回文子串,回溯dfs排列组合,前中序建树,字符串转整数(stringstream),蓄水池最大面积双指针,三数之和三指针定一动前后

概述:

最长回文子串,用动态规划思路来解,初始化状态。

组合总和,用回溯法解决,根为target,DFS

根据前中序构造二叉树,vector做参数,并不是传的指针

字符串转化成整数,用到了stringstream,对类型进行转换,也处理了溢出

盛最多水的容器,用到了双指针,指向的值较小的那个指针向较大的指针移动

三数之和,采用三指针的做法,排序,固定一个,剪枝,去重,一个都不能少

 

1.最长回文子串

题目描述:给定一个字符串s,假定s最大为1000,找出他的最长回文子串。

参考:https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode/

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

思路:这里使用动态规划的思路来解。核心如图,

1.参数过滤,如果字符串长度为0或1,那么s本身即为回文子串,返回自身。if(len == 0||len ==1) return s;

2.对基本的回文子串(包括1个和两个的情况)赋初值。for(int i=0;i<len;++i){dp[i][i] =1;if(i<len-1&&s[i] == s[i+1]){dp[i][i+1]=1;max = 2;start = i;}}

3.状态转移。前面已经处理了长度为1和2的情况,所以这次从长度为3开始遍历。最长为整个长度len

for(int n = 3;n<len;++n){}

4.设置好边界条件,写入核心。for(int i = 0;i+n-1<len;++i){int j = n+i-1;if(s[i] == s[j]&&dp[i+1][j-1] == 1){dp[i][j] = 1;start = i;max = n;}}

string longestPalindrome(string s) {
        int len = s.size();
        if(len == 0|| len==1) return s;
        int start = 0,max = 1; vector<vector<int>> dp(len,vector<int>(len));
        for(int i = 0;i<len;++i){           //初始化状态
            dp[i][i] = 1;                       //赋初值
            if(i<len-1 && s[i] == s[i+1]){      //中间有两个相同字符
                dp[i][i+1] = 1;
                max = 2;
                start = i;
            }
        }
        for(int n = 3;n<=len;++n){                     //j为右边字符位置,i为左边,n为回文长度
            for(int i = 0;i+n-1<len;++i){
                int j = n+i-1;
                if(s[i]==s[j] && dp[i+1][j-1] == 1){    //dp[i][j]表示从si到sj的字符串是否是回文子串
                    dp[i][j] = 1;
                    start = i;
                    max = n;
                }
            }
        }
        return s.substr(start,max);
    }

2.组合总和第39题

题目描述:给定一个无重复元素的数组candidate和一个指定的数字target。 找出数组中所有可以令数字之和为target的组合。每个数字可以无限选取。

示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]
示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

参考:https://leetcode-cn.com/problems/combination-sum/solution/c-by-xyw-4-5/

回溯法思路:以target为根节点,每个分支做减法,减到0,结算。减到负数,剪枝。

1.对vector中元素排序 sort(candidates.begin(),candidates.end());并给自己的成员变量赋初值。

2.调用DFS,如果target被减为0,那么存储path,并返回。if(target == 0){temp.push_back(path);return ;}

3.剪枝,递归。for(int i = start;i<candidates.size()&&target-candidates[i]>=0;++i){path.push_back(candidates[i]);DFS(i,target-candidates[i]);path.pop_back();}

 

class Solution {
private:
    vector<int> path;
    vector<vector<int>>temp;
    vector<int> candidates;
public:
    
    void DFS(int start,int target){
        if(target == 0){
            temp.push_back(path);
            return ;
        }
        for(int i = start;i<candidates.size()&&target - candidates[i]>=0;++i){
            path.push_back(candidates[i]);
            DFS(i,target-candidates[i]);
            path.pop_back();    
            
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        std::sort(candidates.begin(),candidates.end());
        this->candidates = candidates;
        DFS(0,target);
        return temp;
    }
    
};

3.根据前序中序序列构造二叉树

题目描述:根据二叉树的前序和中序序列构造二叉树,假设树中没有重复的元素。

注意:数组做形参时,自动退化为指针。。。。vector做参数时,不会。

思路:1.找出根节点在中序遍历中的位置。

2.计算左右子树的长度,并根据该长度对前序中左右子树划分


BinaryTreeNode* ConstructCore(int* preorder, int* preOrderLast, int* inorder, int* inOrderLast) //传进来的是可变长数组
{
	//前序遍历的第一个数字是根节点的值
	BinaryTreeNode* root = new BinaryTreeNode();
	root->data = preorder[0];
	root->pLeft = root->pRight = nullptr;
	if (preorder == preOrderLast) 
	{
		if (inorder == inOrderLast && *preorder == *inorder)
		{
			return root;
		}
		else
			throw std::exception("Invalid Input22222222222.");
	}
	int* rootinOrder = inorder;
	while (rootinOrder < inOrderLast && *rootinOrder != root->data)
	{
		++rootinOrder;
		cout << "rootInorder:" << *rootinOrder << '\n';
	}
	if (rootinOrder == inOrderLast && *rootinOrder != root->data)
	{
		throw std::exception("Invalid input111111111.");
	}
	int LeftLength = rootinOrder - inorder;
	int* LeftPreOrderLast = preorder + LeftLength;
	if (LeftLength > 0)
	{
		root->pLeft = ConstructCore(preorder+1,LeftPreOrderLast,inorder,rootinOrder - 1);
	}
	if (LeftLength < preOrderLast - preorder) 
	{
		root->pRight = ConstructCore(LeftPreOrderLast + 1, preOrderLast, rootinOrder + 1, inOrderLast);
	}
	return root;
}


BinaryTreeNode* ReBuildBinaryTree(int* preOrder,int* inOrder,int Length)
{
	if (preOrder != nullptr && inOrder != nullptr && Length > 0) 
	{
		return ConstructCore(preOrder, preOrder + Length - 1, inOrder, inOrder + Length - 1);
	}
	else
		return NULL;
}

4.字符串转换成整数

题目描述:实现一个atoi函数,将字符串转化成整数。

1.该函数会根据需要丢弃无用的开头空格,知道第一个非空格字符

2.第一个非空字符是正负号的话,将该符号与后面尽可能多的连续数字组合,形成整数

3.除了整数外,可能存在多余的字符,这些可以被忽略

4.如果第一个非空字符不是有效的整数字符,,字符串为空或仅包含空白字符。函数不需要转换

5.如果数值超过[−2^31,  2^31 − 1],返回INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) 。

参考:https://leetcode-cn.com/problems/string-to-integer-atoi/solution/shi-yong-stringstream-by-bo-luo-miao-w/

思路:用stringstream来读取数字。string类型常用的方法是find和substr,if(pos == string::npos)

stringstream:

1.类型转换,string到int

2.重复使用stringstream对象,每次转换前要使用clear()方法,效率很高,因为stringstream对象构造和析构非常耗费cpu时间。

class Solution {
public:
    int myAtoi(string str) {
        while(*str.begin() == ' ') str.erase(str.begin());
        if(str == "") return 0;
        stringstream ss;
        ss<<str;
        int n;
        ss>>n;
        return n;
    }
};

5.盛最多水的容器

题目描述:给定n个整数,分别代表坐标中的一个点(i,ai),在坐标中话n条垂直线,每条垂直线的两个端点是(i,ai)(i,0),找出两条线,使他们与x轴共同构成的容器可以容纳最多的水

参考:https://leetcode-cn.com/problems/container-with-most-water/solution/sheng-zui-duo-shui-de-rong-qi-by-leetcode/

思路:双指针,时间复杂度O(n),空间O(1)

1.双指针分别指向开头和结尾,并更新面积 maxArea = max(maxArea,min(height[left],height[right])*(right-left));

2.较短的一边往较大的那边移动指针

为什么可行?网友的理解方法,感觉没问题

由于面积的大小取绝于较短边的长短m,如果想得到比当前更大的面积,边长短的必须要舍弃,如果不舍弃,高最大就是m,不管你另一边怎么增长,面积只会减小.

class Solution {
public:
    int maxArea(vector<int>& height) {
        int maxArea = 0;
        int left = 0;
        int right = height.size()-1;
        while(left<right){
            maxArea = max(maxArea,min(height[left],height[right])*(right-left));
            if(height[left]<height[right]){left++;}
            else{right--;}
        }
        return maxArea;
    }
};

6.三数之和

题目描述:给定包含n个整数的数组,判断数组中是否存在三个元素之和为0,找出所有的满足条件的三元组。

思路:三指针解决

1.排序,从小到大,开头大于0,或者结尾小于0.和根本不可能为0;

2.三指针,先定一个。int fit = nums[i];剪枝if(fit>0) break;去重()避免同样的字符匹配多趟!if(i>0&&nums[i] == nums[i-1])continue;

3.定义剩下两个指针位置,分别位于剩下的头和尾。

4.递归存结果。while(k<r){if(...)}

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        if(nums.size()<3||nums.front()>0||nums.back()<0) return {};
        for(int i = 0;i<nums.size();++i){
            int fit = nums[i];
            if(fit>0) break;
            if(i>0 && fit == nums[i-1]) continue;   //避免重复
            int k = i+1;
            int r = nums.size()-1;
            while(k<r){
                if(nums[k]+nums[r] == -fit){
                    if(k == i+1||r == nums.size()-1){res.push_back(vector<int>{nums[i],nums[k],nums[r]});k++;r--;}
                    else if(nums[k] ==nums[k-1]){k++;}  //避免重复
                    else if(nums[r] == nums[r+1]){r--;}
                    else{res.push_back(vector<int>{nums[i],nums[k],nums[r]});k++;r--;}
                }
                else if(nums[k]+nums[r]<-fit) k++;
                else r--;
            }
            
            
        }
        return res;
    }
};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值