Leetcode刷题记录

 

目录

 

1.两数之和

2.两数相加

3.无重复字符的最长子串

5.最长回文子串

4.寻找两个有序数组的中位数

11.盛最多水的容器

15.三数之和

20.有效的括号

17.电话号码的组合

19.删除链表的倒数第N个结点

21.合并两个有序链表

10.正则表达式匹配

22.括号生成

31.下一个排列

33.搜索旋转排序数组

155.最小栈

34.在排序数组中查找元素的第一个和最后一个位置

23.合并K个排序链表   

39.组合总和(子集选取问题 DFS)

40.组合总和2

46.全排列

 47.全排列2

78.子集

90.子集2

42.接雨水

152.乘积最大子序列

55.跳跃游戏

56.合并区间

24.两两交换链表中的结点

6.Z字型变换


1.两数之和
 

1)用双指针解决,首先进行稳定排序。

2)因为返回的是原数组中的下标,应该用结构体保存数值和下标的对应关系

注意:如果nodes用的是在外部定义vector就会导致超出内存限制,改为内部大小确定的数组即可解决。

struct Node{
    int index;
    int value;
};

bool cmp(const Node &a,const Node &b)
{
    return a.value nodes;


class Solution {
public:
    vector twoSum(vector& nums, int target) {
        int len=nums.size();
        int left=0;
        int right=len-1;
        vector ans;
        Node nodes[len];
        for(int i=left;i<=right;i++)
        {
            Node tmp;
            tmp.value=nums[i];
            tmp.index=i;
            //nodes.push_back(tmp);
            nodes[i]=tmp;
        }
        //stable_sort(nodes.begin(),nodes.end(),cmp);
        stable_sort(nodes,nodes+len,cmp);
        while(left

2.两数相加

题目:给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

注意:逆序是指链表的头部存低位,尾部存高位,直接从头到尾遍历链表即可。

          返回的ans_l不能只设置为一个结点指针ListNode*,它必须是一个指向确定结点的指针,否则其没有next值。但是这个结点的值无所谓,最后返回时会跳过该结点。相当于创建了一个任意值的头结点。

          这里的链表是不带头结点的链表,但是返回的是链表首结点的指针。

          判断结果是否可能以0作为最高位,只要判断最后carry是否为0即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/*我的代码*/
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        //stack s1,s2,ans;
        ListNode* p1=l1;
        ListNode* p2=l2;
        ListNode* ans_l=new ListNode(0);//这个结点的值是什么无所谓,只是为了创建一个next为NULL的结点
        ListNode* p=ans_l;

        int carry=0;
        while(p1!=NULL&&p2!=NULL)
        {
            int tmp=p1->val+p2->val+carry;
            if(tmp>=10)
            {
                carry=tmp/10;
                tmp=tmp%10;
            }
            else carry=0;
            p->next=new ListNode(tmp);
            p=p->next;
            p1=p1->next;
            p2=p2->next;
        }
        while(p1!=NULL)
        {
            int tmp=p1->val+carry;
            if(tmp>=10)
            {
                carry=tmp/10;
                tmp=tmp%10;
            }
            else carry=0;
            p->next=new ListNode(tmp);
            p=p->next;
            p1=p1->next;
        }
        while(p2!=NULL)
        {
            int tmp=p2->val+carry;
            if(tmp>=10)
            {
                carry=tmp/10;
                tmp=tmp%10;
            }
            else carry=0;
            p->next=new ListNode(tmp);
            p=p->next;
            p2=p2->next;
        }
        if(carry!=0) 
        {
           p->next=new ListNode(carry);
        }

        return ans_l->next;
    }
};
/*评论中更简洁的代码,只要一个循环*/
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        
        ListNode* dummy = new ListNode(0);
        ListNode* curr  = dummy;
        int carry = 0;
        
        while (l1 || l2) {
            
            int val1 = l1 ? l1->val : 0;
            int val2 = l2 ? l2->val : 0;
            
            int sum = val1 + val2 + carry;
            carry = sum / 10;
            
            curr->next = new ListNode(sum % 10);
            curr = curr->next;
            
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }
        
        if (carry) curr->next = new ListNode(carry);
        
        return dummy->next;
    }
};

3.无重复字符的最长子串

关键点:滑动窗口+Hashmap

建立一个字符串到索引的映射,而不是使用集合来判断一个字符是否存在。 当我们找到重复的字符时,我们可以立即跳过该窗口,下一次从下一个字符开始。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        map mp;
        int len=s.length();
        int maxlen=0,nowlen=0;
        for(int i=0;imaxlen)
                    maxlen=nowlen;
            }
            else //如果有重复的,将重复元素之后的元素再次加入map
            {
                int j=mp[s[i]];
                nowlen=0;
                mp.clear();
                for(int k=j+1;k<=i;k++)
                {
                    mp[s[k]]=k;
                    nowlen++;
                }
                if(nowlen>maxlen)
                    maxlen=nowlen;
            }
        }
        return maxlen;
    }
};

5.最长回文子串

法一:中心拓展法

回文中心的两侧互为镜像。因此,回文可以从他的中心展开,并且只有 2n-1 个这样的中心(一个元素为中心的情况有 n 个,两个元素为中心的情况有 n-1 个)

从左向右枚举回文中心,分奇回文和偶回文。

注意:1. 要特判空字符和单个字符的情况(直接返回) “”和“ ”不一样,长度分别为0和1

          2. C++ string中substr()用法: 

            string substr(int pos,int len) ;

int find1(string s,int len,int mid,int& left,int& right)
{
    int i=mid,j=mid;
    while(i>=0&&j<=len-1&&s[i]==s[j])
    {
        left=i;
        right=j;
        i--;
        j++;
    }
     return right-left+1;
}

int find2(string s,int len,int mid,int& left,int& right)
{
    int i=mid,j=mid+1;
    while(i>=0&&j<=len-1&&s[i]==s[j])
    {
        left=i;
        right=j;
        i--;
        j++;
    }
    return right-left+1;
} 

class Solution {
public:
    string longestPalindrome(string s) {
        int len=s.length();
        if(len==0||len==1) return s;
        int maxlen=0;
        int left,right;
        int ans_left,ans_right;
        
        for(int i=0;imaxlen)
            {
                ans_left=left;
                ans_right=right;
                maxlen=cnt1;
            }
            int cnt2=find2(s,len,i,left,right);
            if(cnt2>maxlen)
            {
                ans_left=left;
                ans_right=right;
                maxlen=cnt2;
            }
        }
        return s.substr(ans_left,maxlen);
    }
};

法二:动态规划法

首先从头遍历一遍字符串,设置初始状态:dp[i][i]=1 dp[i][i+1]=2(if s[i]=s[i+1])

然后子串长度递增的方式,依次填充dp数组。

int dp[1010][1010];
class Solution {
public:
    string longestPalindrome(string s) {
        int len=s.length();
        if(len==0||len==1) return s;
        memset(dp,0,sizeof(dp));
        int maxcnt=0;
        int left=0,right=0;
        for(int i=0;imaxcnt)
                    {
                        maxcnt=dp[i][j];
                        left=i;
                        right=j;
                    }
                }
                
             }
        }
      return s.substr(left,right-left+1);      
    }
};

4.寻找两个有序数组的中位数

参考:LeetCode题解

1.中位数:将一个集合划分成长度相等的两个子集,其中一个子集中的元素总是大于另一个子集中的元素。

2.割: 通过切一刀,能够把有序数组分成左右两个部分,切的那一刀就被称为割(Cut),割(Cut)的左右会有两个元素,分别是左边最大值和右边最小值。割可以割在两个数中间,也可以割在1个数上,如果割在一个数上,那么这个数即属于左边,也属于右边。

对于两个数组,设Ci为第i个数组的割(下标值),满足题意的割法应满足以下条件:

LMaxi为第i个数组割后的左元素.

RMini为第i个数组割后的右元素。

Leetcode刷题记录_第1张图片

首先,LMax1<=RMin1,LMax2<=RMin2 这是肯定的,因为数组是有序的,左边肯定小于右边!,而如果割(Cut)在某个数上,则左右相等。

其次,LMax1<=RMin2,LMax2<=RMin1 

Leetcode刷题记录_第2张图片

如果左半边全小于右半边,如果左边的元素个数相加刚好等于k, 那么第k个元素就是Max(LMax1, LMax2),这个比较好理解的,因为Max(LMax1, LMax2)肯定是左边k个元素的最大值,因为合并后的数组是有序,第k个元素肯定前面k个元素中最大的那个。

那么如果 LMax1>RMin2,说明数组1的左边元素太大(多),我们把C1减小,C2=k-C1也就相应的增大。LMax2>RMin1同理,把C2减小,C1=k-C2也就相应的增大。

3.两个数组的最大问题是,它们合并后,m+n总数可能为奇, 也可能为偶,所以我们得想法让m+n总是为偶数

通过虚拟加入‘#’,我们让m转换成2m+1 ,n转换成2n+1, 两数之和就变成了2m+2n+2,恒为偶数。

注意是虚拟加,其实根本没这一步,通过下面的转换,我们可以保证虚拟加后每个元素跟原来的元素一一对应

注:割在‘#’上Ci为偶数,而偶数直接除以2和减1之后再除2结果是不同的,所以刚好表示Lmaxi和Rmini是两个不同的元素。

     而割在数字上Ci为奇数, 而奇数直接除以2和减1之后再除2结果是相同的,所以刚好表示Lmaxi和Rmini是两个相同的元素。

Leetcode刷题记录_第3张图片

4.最快的割(Cut)是使用二分法,

有2个数组,我们对哪个做二分呢? 根据之前的分析,我们知道了,只要C1或C2确定,另外一个也就确定了。这里,为了效率,我们肯定是选长度较短的做二分,假设为C1。

LMax1>RMin2,把C1减小,C2增大。—> C1向左二分

LMax2>RMin1,把C1增大,C2减小。—> C1向右二分

如果C1或C2已经到头了怎么办?

这种情况出现在:如果有个数组完全小于或大于中值。假定n

C1 = 0 —— 数组1整体都在右边了,所以都比中值大,中值在数组2中,简单的说就是数组1割后的左边是空了,所以我们可以假定LMax1 = INT_MIN

C1 =2n —— 数组1整体都在左边了,所以都比中值小,中值在数组2中 ,简单的说就是数组1割后的右边是空了,所以我们可以假定RMin1= INT_MAX,来保证LMax2

C2 = 0—— 数组2整体在右边了,所以都比中值大,中值在数组1中 ,简单的说就是数组2割后的左边是空了,所以我们可以假定LMax2 = INT_MIN

C2 = 2m—— 数组2整体在左边了,所以都比中值小,中值在数组1中, 简单的说就是数组2割后的右边是空了,为了让LMax1 < RMin2恒成立,我们可以假定RMin2 = INT_MAX

#include 
#include 
using namespace std;

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))

class Solution {
public:
	double findMedianSortedArrays(vector& nums1, vector& nums2) {
		int n = nums1.size();
		int m = nums2.size();

		if (n > m)  //保证数组1一定最短
		{
			return findMedianSortedArrays(nums2, nums1);
		}

		// Ci 为第i个数组的割,比如C1为2时表示第1个数组只有2个元素。LMaxi为第i个数组割后的左元素。RMini为第i个数组割后的右元素。
		int LMax1, LMax2, RMin1, RMin2, c1, c2, lo = 0, hi = 2 * n;  //我们目前是虚拟加了'#'所以数组1是2*n长度

		while (lo <= hi)   //二分
		{
			c1 = (lo + hi) / 2;  //c1是二分的结果
			c2 = m + n - c1;

			LMax1 = (c1 == 0) ? INT_MIN : nums1[(c1 - 1) / 2];
			RMin1 = (c1 == 2 * n) ? INT_MAX : nums1[c1 / 2];
			LMax2 = (c2 == 0) ? INT_MIN : nums2[(c2 - 1) / 2];
			RMin2 = (c2 == 2 * m) ? INT_MAX : nums2[c2 / 2];

			if (LMax1 > RMin2)
				hi = c1 - 1;
			else if (LMax2 > RMin1)
				lo = c1 + 1;
			else
				break;
		}
		return (max(LMax1, LMax2) + min(RMin1, RMin2)) / 2.0;
	}
};


int main(int argc, char *argv[])
{
	vector nums1 = { 2,3, 5 };
	vector nums2 = { 1,4,7, 9 };
	
	Solution solution;
	double ret = solution.findMedianSortedArrays(nums1, nums2);
	return 0;
}

以下是我自己的代码,已经尽可能优化了,能够求解小数据,大数据会超时。

思路是合并两个数组,在找到满足要求后的元素个数后提前终止。

class Solution {
public:
    double findMedianSortedArrays(vector& nums1, vector& nums2) {
        vector ans;
        int len1=nums1.size();
        int len2=nums2.size();
        int i=0,j=0;
        int m=len1+len2;
        bool flag=false;
        if(m&1==1)     //数量之和为奇数
        {
            m=m>>1+1;
            flag=true;
        }
        else m=m>>1; //数量之和为偶数
        while(i

11.盛最多水的容器

设置双指针i,j,指向容器两端,逐渐向中间收缩并记录最大值。

每次选定围成水槽两板height[i]height[j]中较小的对应指针,向中间收缩,这是因为

  • 水槽的高度由两板中的短板决定,每次收缩,都会导致水槽底边宽度-1
  • 因此,若想找到比当前最大值更大的水槽,那么水槽的短板高必须要高于上一个水槽短板高,而只有向内移动短板,有可能达成这一条件(若移动长板,下个水槽的面积一定小于当前水槽面积)

为什么一定能找到最优解:

Leetcode刷题记录_第4张图片

如图所示 height[0]8,但是由于左右分支有很多重叠部分,真正被放弃的只有①部分。

①部分的特点: 均是从0出发的区间。因为1->8中,height[0]7、0->6、0->5...高度最大也不会超过height[0],但是宽度一定比0->8小,所以水槽容量不可能再增加,所以可以舍弃。

在第二层,height[8]7,但真正被放弃的也只有②部分。

②部分的特点: 终点均是8,而在1->8中,水槽的高度已经是8的最大高度height[8],之后无论是2->8、3->8....高度最大也不会超过height[8],但是宽度一定比1->8小,所以水槽容量不可能再增加,所以可以舍弃。

class Solution {
public:
    int maxArea(vector& height) {
        int len=height.size();
        int i=0,j=len-1;
        int ans=0;
        while(ians)
                ans=now;
            if(height[i]

15.三数之和

还是双指针的思想,先将数组排序,每次选择一个非正数,对它左边的序列用双指针遍历。

mid是排序后数组中最后一个非正数。

要注意去重,如果指针移动后的元素和之前一个元素相同则要继续移动指针。

class Solution {
public:
    vector> threeSum(vector& nums) {
        int len=nums.size();
        sort(nums.begin(),nums.end());
        vector >ans;
        vector tmp;
        
        /*找到最大的负数或者是0的下标*/
        int mid=len-1;
        for(int i=0;i0)
                mid=i;
        //cout<

20.有效的括号

map mp;

class Solution {
public:
    bool isValid(string s) {
        int len=s.length();
        if(len==0) return true;
        if(len==1) return false;
        stack st;
        mp['(']=1;
        mp[')']=1;
        mp['{']=2;
        mp['}']=2;
        mp['[']=3;
        mp[']']=3;
        for(int i=0;i

17.电话号码的组合

主要是用DFS进行递归,我自己用的方法有点暴力,主要是2-5都只有三个字符,7、8、9要单独讨论一下,代码有些冗余。可以直接用map映射。

注意:向string字符串追加字符c 只能用ans=ans+c,不可以用ans+=c!

/*我的代码*/
void DFS(vector &res,string digits,string ans,int k,int len)
{
    if(k==len)
    {
        res.push_back(ans);
        return;
    }

    if(digits[k]=='7')
    {
        ans=ans+'p';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'q';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'r';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'s';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        
    }
    else if(digits[k]=='8')
    {
        ans=ans+'t';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'u';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'v';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
    }
    else if(digits[k]=='9')
    {
        ans=ans+'w';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'x';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'y';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
        ans=ans+'z';
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k); 
    }
    else
    {
       for(int t=0;t<3;t++)
    {
        //ans[k]='a'+(digits[k]-2)*3+t;
        char c='a'+(digits[k]-'2')*3+t;
        //char c='*';
        ans=ans+c;
        DFS(res,digits,ans,k+1,len);
        ans.erase(ans.begin()+k);
    }
    }
}


class Solution {
public:
    vector letterCombinations(string digits) {
        int len=digits.length();
        string ans;
        vector res;
        if(len==0) return res;
        DFS(res,digits,ans,0,len);
        return res;
    }
};

/*看题解之后改的代码*/
class Solution {
public:
        vector res;
        map mp;
    vector letterCombinations(string digits) {
        mp['2']="abc";
        mp['3']="def";
        mp['4']="ghi";
        mp['5']="jkl";
        mp['6']="mno";
        mp['7']="pqrs";
        mp['8']="tuv";
        mp['9']="wxyz";
        int len=digits.length();
        string ans;
        res.clear();
        if(len==0) return res;
        DFS(digits,ans,0,len);
        return res;
    }
    
    void DFS(string digits,string ans,int k,int len)
    {
        if(k==len)
        {
            res.push_back(ans);
            return;
        }
        string s=mp[digits[k]];
        for(int i=0;i

别人更简洁的代码:

注意map的赋值方式,并且需要在函数内部赋值。

string now放在参数位置,就可以减少添加和删去的步骤。

class Solution {
public:
    unordered_map mp;
    vector letterCombinations(string digits) {
        mp = {{2, "abc"}, {3, "def"}, {4, "ghi"}, {5, "jkl"}, 
              {6, "mno"}, {7, "pqrs"}, {8, "tuv"}, {9, "wxyz"}};
        vector res;
        if (digits == "")
            return res;
        helper(digits, 0, "", res);
        return res;
    }
    void helper(string digits, int cur, string now, vector& res)
    {
        if (cur == digits.size())
            res.push_back(now);
        string s = mp[digits[cur] - '0'];
        for (int i = 0; i < s.size(); ++i)
            helper(digits, cur+1, now+s.substr(i, 1), res);
        
    }
};

19.删除链表的倒数第N个结点

细节:一定要判断每个可能的NULL情况。题目参数中的head是指向头结点的,这里的链表是带头结点的。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(head==NULL||head->next==NULL) return NULL;
        ListNode* p1=head;
        ListNode* p2=head;
        for(int i=0;inext;
        }
        if(p2==NULL) return head->next;
        while(p2->next!=NULL) //注意此时p1指向的是待删除结点的前一个结点
        {
            p1=p1->next;
            p2=p2->next;
        }
        p1->next=p1->next->next;//把p1之后一个结点删除
        return head;
        
    }
};

21.合并两个有序链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1==NULL) return l2;
        if(l2==NULL) return l1;
        ListNode*p1=l1,*p2=l2;
        ListNode*ans=new ListNode(0);
        ListNode*p=ans;
        while(p1!=NULL&&p2!=NULL)
        {
            if(p1->valval)
            {
                 p->next=p1;
                 p=p->next;
                 p1=p1->next;
            }
            else
            {
                p->next=p2;
                p=p->next;
                p2=p2->next;
            }
        }
/* 直接把后面链表接上就好了..
        while(p1!=NULL)
        {
                 p->next=p1;
                 p=p->next;
                 p1=p1->next;
        }
        while(p2!=NULL)
        {
                 p->next=p2;
                 p=p->next;
                 p2=p2->next;
        }
*/
        p->next=(p1==NULL)?p2:p1;
        return ans->next;
    }
};

10.正则表达式匹配

参考题解:思路——leetcode某题解  代码——leetcode另一题解

vector>flag(len1 + 1, vector(len2 + 1, false));  
flag[0][0] = true;边界情况
这里flag[i][j]表示pattern[0~j-1]是否可以匹配str[0~i-1],(注意flag[i][j] 对于pattern[j - 1] str[i - 1])
首先列出动态转移方程
        第一种情况:
        pattern[j - 1] == str[i - 1] || pattern[j - 1] == '.'    //元素相等或为通配符‘.’
        有flag[i][j] = flag[i - 1][j - 1];

        第二种情况:
        pattern[j - 1] == '*'                 //p串的当前元素为‘*’
        第二种情况的第一个分支 
        str[i - 1] == pattern[j - 2] || pattern[j - 2] == '.'        //1)s中当前元素的*之前一个元素相等或为通配符‘.’
        有flag[i][j] = (flag[i][j - 2] || flag[i][j - 1] || flag[i - 1][j]);(三种情况分别对应:*号让前面字符出现0次、出现1次、出现2次及以上)

    出现0次:直接将 ‘a*’这个整体跳过,考虑子串是否匹配

    出现1次:将‘*’跳过,将其变成一个常规字符来考虑是否匹配

    出现多次:‘a*’整体保留,s中指针往前移动一个,考虑子串是否匹配
        第二种情况第二个分支                  //(2)若*前元素和s当前元素不相等,则只能看成出现0次
        flag[i][j] = flag[i][j - 2];(*号只能让前面的字符出现0次)

        第三种情况:
        flag[i][j] = false;               //该算法是为了尽可能找到所有可能为true的情况,其余情况均为false

其次在动态规划前需要考虑好初始条件
        for (int i = 1; i <= len2; ++i) {   //s串为空串时
            if (pattern[i - 1] == '*')
                flag[0][i] = flag[0][i - 1] || flag[0][i - 2];   这是为了解决类似  "" 和 ".*.*a*a*b*"这种情况 因为这种也是符合的

// flag[0][i] = flag[0][i - 1]   => ""和“.*”匹配

//  flag[0][i] =flag[0][i - 2]   =>""和“a*”匹配

代码明天补上~~

22.括号生成

使用递归解决,left表示已经有的左括号的数目,right表示已经有的右括号的数目,依次向当前字符串中插入左括号和右括号。

class Solution {
public:
    vector generateParenthesis(int n) {
       vector ans;
        Traceback("",0,0,n,ans);
        return ans;
       
    }
    void Traceback(string now,int left,int right,int n,vector &ans)
    {
        if(leftn||right>n) return;
        if(left==n&&right==n) ans.push_back(now);
        Traceback(now+'(',left+1,right,n,ans);
        Traceback(now+')',left,right+1,n,ans);
    }
};

31.下一个排列

思路:从后往前遍历,找到第一个i,满足nums[i]>nums[i-1],说明从i到末尾这已经是一个最大的数了,此时应该从后面选择一个比nums[i-1]大的数中最小的一个,和nums[i-1],说明将i-1位前进了一步(更新成了稍大的一个数),再将i及i之后的数进行排序,变成一个最小的数,即可得到全排列。

class Solution {
public:
    void nextPermutation(vector& nums) {
        int len=nums.size();
        int i;
        int mark;
        for( i=len-1;i>0;i--)
        {
            if(nums[i]>nums[i-1])
            {
                int min=i;
                for(int j=i;jnums[i-1]&&nums[j]

33.搜索旋转排序数组

思路:直接进行二分法,分以下情况讨论:

(1)mid直接等于target,直接返回

(2)在左半边递增区域:

①target在left和mid之间

②target不在之间

(2)在右半边递增区域:

①target在mid和right之间

②target不在之间

class Solution {
public:
    int search(vector& nums, int target) {
        int left=0,right=nums.size()-1;
        while(left<=right)
        {
            int mid=(left+right)/2;
            if(nums[mid]==target)
                return mid;
            if(nums[mid]>=nums[left]) //左部分较长,mid在左半边
            {
                if(target>=nums[left]&&targetnums[mid]&&target<=nums[right])
                    left=mid+1;
                else right=mid-1;
            }
        }
            return -1;
    }
};

155.最小栈

即如何在常数时间内获得栈内的最小元素?

方法一:辅助栈

另有一个栈min,存放当前栈内最小的元素。若min栈空或入栈元素比min栈顶元素小,则入min栈。出栈时要判断s栈栈顶元素是否和min栈栈顶元素相等,若相等,min栈栈顶元素也要出栈。

class MinStack {
public:
    stack s;//数据栈
    stack min;//辅助栈
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        s.push(x);
        if(min.empty()||x<=min.top())
        {
            min.push(x);
        }
    }
    
    void pop() {
        if(s.top()==min.top())
            min.pop();
        s.pop();
    }
    
    int top() {
        return s.top();
    }
    int getMin() {
        return min.top();
    }
};

方法二:每次入栈两个元素——当前元素和栈内当前最小元素

class MinStack {
public:
    /** initialize your data structure here. */
    stack s;
    MinStack() {
        
    }
    
    void push(int x) {
        if(s.empty())
        {
            s.push(x);
            s.push(x);
        }
        else
        {
            int temp=s.top();
            s.push(x);
            if(x

34.在排序数组中查找元素的第一个和最后一个位置

思路:即查找有序数组中第一个大于等于target的元素 和 第一个大于target的元素(下标减1即可)

         数组从左到右一定是先不满足,后开始满足的。如果找不到target,返回的是应该插入target的位置。

PS:这时候需要对upper_bound进行判断,可以直接写一个函数寻找最后一个满足某条件的元素。

class Solution {
public:
    vector searchRange(vector& nums, int target) {
        vector ans;
        ans.push_back(-1);
        ans.push_back(-1);
        
        int len=nums.size();
        if(len==0) return ans;
        int ans_left=binarySearch_1(nums,target,0,len-1);
        int ans_right=binarySearch_2(nums,target,0,len-1);
        if(nums[ans_left]==target)
        {
            ans[0]=ans_left;
            if(nums[ans_right]==target)
                ans[1]=ans_right;
            else ans[1]=ans_right-1;
        }
        return ans;
    }
    
    /*寻找第一个大于等于target的数*/
    int binarySearch_1(vector& nums,int target,int left,int right) 
    {
        while(left=target)
                right=mid;
            else left=mid+1;
        }
        return left;
    }
    
    /*寻找第一个大于target的数*/
    int binarySearch_2(vector& nums,int target,int left,int right) 
    {
        while(lefttarget)
                right=mid;
            else left=mid+1;
        }
        return left;
    }

 /*寻找最后一个小于等于target的数*/
    int binarySearch_2(vector& nums,int target,int left,int right) 
    {
        while(left

23.合并K个排序链表   

思路:循环遍历每个链表的第一个结点,找到其中最小的那个,记录下来。

优化1:用容量为K的最小堆优先队列,把链表的头结点都放进去,然后出队当前优先队列中最小的,挂上链表,,然后让出队的那个节点的下一个入队,再出队当前优先队列中最小的,直到优先队列为空。

优化2:分治法

分解:将K个链表分成两部分,前半部分和后半部分。将两部分各合并成一个链表l1、l2。

合并:将l1和l2两个链表合并成一个链表。

注意:合并两个链表用到了递归法的思想!!即寻找两个链表头结点中较小的那个作为头结点,然后减小问题规模,继续递归。边界是到达链表尾部,直接返回NULL。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {  //暴力解法
public:
    ListNode* mergeKLists(vector& lists) {
        int K=lists.size();
        ListNode* head=new ListNode(0); //头指针
        ListNode* p=head;
        while(1)
        {
            int min=INT_MAX;
            int i,min_i=-1;
            int cnt_null=0;  //统计空指针数目
            for(i=0;ivalval;
                }
                if(lists[i]==NULL) cnt_null++;
            }
            if(cnt_null==K) break;
            //ListNode tmp=ListNode(min);
            //p->next=&tmp;
            //cout<next=lists[min_i];
            p=p->next;
            lists[min_i]=lists[min_i]->next;
        }
        return head->next;
    }
};

class Solution {  //分治解法
public:
    ListNode* mergeKLists(vector& lists) {
        int K=lists.size();
        if(K==0) return NULL;
        ListNode*head = merge(lists,0,K-1);
        return head;
};
    
    /*将下标从left到right的所有链表合并成一个链表*/
    ListNode* merge(vector& lists,int left,int right)
    {
        if(left>right) return NULL;
        if(left==right) return lists[left];
        int mid=left+(right-left)/2;
        //cout<valval)
        {
            l1->next=mergeTwoList(l1->next,l2);
            return l1;
        }
        else{
            l2->next=mergeTwoList(l1,l2->next);
            return l2;
        }
    }
};

39.组合总和(子集选取问题 DFS)

要点:①原数组无重复元素 ②数组内某个元素可以重复选取 ③解集不能包含重复的组合。

思路:采用DFS深度搜索,每个结点的两个分支是当前结点选还是不选,由于可以重复选,所以在左分支深度仍然为k。

分析:解集不能包含重复的组合  ——> 要对原数组进行排序

          数组内某个元素可以重复选取

class Solution {
public:
    vector> combinationSum(vector& candidates, int target) {
        vector> res;
        vector tmp;
        sort(candidates.begin(),candidates.end());
        DFS(candidates,target,res,0,tmp,0);
        return res;
    }
    
    
    void DFS(vector& candidates,int target,vector>& res,int sum,vector& tmp,int k)
    {
        if(sum>target||k>=candidates.size())
        {
            return;
        }
        if(sum==target)
        {
            res.push_back(tmp);
            return;
        }
        tmp.push_back(candidates[k]);
        DFS(candidates,target,res,sum+candidates[k],tmp,k); //选
        tmp.pop_back();
        DFS(candidates,target,res,sum,tmp,k+1);  //不选
    }
};

40.组合总和2

题目要点:①原数组内可能存在重复元素 ②原数组内某个元素不能重复选取(但是相同的数字可能重复出现,仔细体会一下题目表达)  ③解集不能包含重复的组合。

分析:本题不同于上题,不再是一个子集选取问题,变成了为变长数组tmp的每个位置选组合适的元素,总的原则是tmp数组的相同位置不能选重复元素(因为tmp数组中元素也是有序的,两个tmp数组相同位置出现相同元素则一定是重复解),tmp数组的不同位置元素可以重复!!!!!

要理清两个概念,在同一个DFS函数中,for循环中的每个点相对于tmp数组来说是同一个位置的,而为了“解集不能包含重复的组合”,必须保证同一个for循环中,指针i在遍历的时候,candidates[i] != candidates[i-1] 。

另外,对于每个当前结点的子结点,也就是tmp数组的下一个位置,元素是可以重复的(如[1,1,6]这样的解),所以要讨论的是指针k后面的一个元素。

对于候选数组排序的目的在于便于判断,tmp的相同位置是否选择了相同的元素!

以上两道题的区别:39题是数组内某个元素可以重复选取,40题是原数组内可能存在重复元素但原数组内某个元素不能重复选取。看起来似乎都可能出现[1,1,6]这样的解,但是仔细思考一下本质是不同的!

39题某个重复选取的次数是任意的(只要sum

所以40题需要一个指针k来指向原候选数组的每个元素!!

class Solution {
public:
    vector> combinationSum2(vector& candidates, int target) {
        vector> res;
        vector tmp;
        sort(candidates.begin(),candidates.end());
        DFS(candidates,res,tmp,target,0,0);
        return res;
    }
    
    void DFS(vector& candidates,vector>& res,vector& tmp,int target,int sum,int k)
    {
        if(sum>target)
        {
            return;
        }
        if(sum==target)
        {
            res.push_back(tmp);
            return;
        }

        for(int i=k;ik&&candidates[i]==candidates[i-1]) //同一个位置不能有重复元素
                continue;
            if(candidates[i]>target) continue;
            tmp.push_back(candidates[i]);
            DFS(candidates,res,tmp,target,sum+candidates[i],i+1);
            tmp.pop_back();
        }
    }
    
};

46.全排列

题目要求:没有重复元素的数字序列,返回所有全排列。

分析:这是排列问题,要求tmp各个位置元素不能相同,考虑使用mark数组标记该元素之前是否已经出现过。

tips:可以用vector的构造函数,快速构造标记数组 

 vector mark(nums.size(),false);
class Solution {
public:
    vector> permute(vector& nums) {
        vector> res;
        vector tmp;
        vector mark;
        int len=nums.size();
        for(int i=0;i& nums,vector& mark,vector>& res,vector& tmp,int len,int k)
    {
        if(k==len)
        {
            res.push_back(tmp);
            return;
        }
        for(int i=0;i

 47.全排列2

题目要求:给定一个可包含重复数字的序列,返回所有不重复的全排列。

思路:可包含重复元素,所以tmp不同位置的结点可以重复。

          不重复的排列,所以tmp相同位置的结点不能重复,也就是同一个for循环的兄弟结点不能重复,我用的笨办法是用flag来记录一下for循环中左兄弟结点是什么,为了保证相同元素出现在一起,先要对nums进行一下排序。

          由于是全排列问题,所以原数列中的每个元素一定会在tmp中出现,所以每次for循环从nums数组的下标0开始遍历(m叉树、图的m着色问题),选中一个一个元素就对它mark[i]=true,退出这个结点就还原mark[i]=false。

          39、40题是寻找一个满足条件的子集,且40题要求原数组中每个元素最多出现一次,所以可以有一个指向原数组的下标k,每次都选定一个i之后,将下标k定位到i,之后从k之后的元素中考虑.

Leetcode刷题记录_第5张图片

class Solution {
public:
    vector> permuteUnique(vector& nums) {
        vector> res;
        vector tmp;
        int len=nums.size();
        vector mark(len,false);
        sort(nums.begin(),nums.end());
        DFS(nums,mark,res,tmp,len,0);
        return res;
    }
    
    void DFS(vector nums,vector& mark,vector>& res,vector& tmp,int len,int k)
    {
        if(k==len)
        {
            res.push_back(tmp);
            return;
        }
        int flag=INT_MAX;
        for(int i=0;i

78.子集

题目要求:不含重复元素的数组,返回该数组所有可能的子集集合,解集不能包含重复的子集。

思路:典型的选取子集问题,讨论原数组的每个元素,选还是不选。(二分支)

class Solution {
public:
    vector> subsets(vector& nums) {
        vector> res;
        vector tmp;
        DFS(nums,res,tmp,nums.size(),0);
        return res;
    }
    
    void DFS(vector& nums,vector>& res,vector& tmp,int len,int k)
    {
        if(k==len)  //已经
        {
            res.push_back(tmp);
            return;
        }
        tmp.push_back(nums[k]);
        DFS(nums,res,tmp,len,k+1); //选
        tmp.pop_back();
        DFS(nums,res,tmp,len,k+1); //不选 
    }
};

90.子集2

题目要求:给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。

思路:按照上一题的思路对原数组的每一个元素进行选择,但是在最后加入res的时候,借助map讨论该数组之前是否出现过。

          由于对每一种可能情况都讨论了一下,所以复杂度较高,比较暴力。。

优化:参考题解,类似于40题,也有一个指针k在原数组中滑动,但是区别是该题是求子集所以从根结点到每个结点的路径都会放到res中,而40题是寻找一个满足条件的子集(元素之和等于target),所以只有满足了特定条件才放到res。

/*较暴力解法*/
class Solution {
public:
    vector> subsetsWithDup(vector& nums) {
        vector> res;
        vector tmp;
        map,int> mp;
        sort(nums.begin(),nums.end());
        DFS(nums,mp,res,tmp,nums.size(),0);
        return res;
    }
    
    void DFS(vector& nums,map,int>& mp,vector>& res,vector& tmp,int len,int k)
    {
        if(k==len)
        {
            map,int>::iterator it=mp.find(tmp);
            if(it==mp.end())
            {
                mp[tmp]=1;
                res.push_back(tmp);
            }
            return;
        }
        tmp.push_back(nums[k]);
        DFS(nums,mp,res,tmp,len,k+1);//选
        tmp.pop_back();
        DFS(nums,mp,res,tmp,len,k+1);//不选
        map,int>::iterator it=mp.find(tmp);
    }
};
/*优化解法*/
class Solution {
public:
    vector> subsetsWithDup(vector& nums) {
        vector> res;
        vector tmp;
        sort(nums.begin(),nums.end());
        DFS(nums,res,tmp,nums.size(),0);
        return res;
    }
    
    void DFS(vector& nums,vector>& res,vector& tmp,int len,int k)
    {
        res.push_back(tmp);
        for(int i=k;ik&&nums[i]==nums[i-1])
                continue;
            tmp.push_back(nums[i]);
            DFS(nums,res,tmp,len,i+1);
            tmp.pop_back();
        }
    }
};

42.接雨水

Leetcode刷题记录_第6张图片

题解思路:首先找到每个柱子左侧柱子的最大值,和右侧柱子的最大值

                 依次讨论每个柱子的贡献:对于每个柱子而言,只有和比它高的柱子联合在一起才有可能对总water做出贡献,所以要                   找到每个柱子left[i]和right[i]中较小的那个进行联合。

为什么不找left[i]和right[i]中较大的那个?短板原理,每个柱子能产生的贡献是由较小的那个决定的,比如第5个柱子。

class Solution {
public:
    int trap(vector& height) {
        int n=height.size();
        vector left(n),right(n);
        
        for(int i=1;i=0;i--)
            right[i]=max(right[i+1],height[i+1]); //right[i]表示i右边的最大值
        
        int water=0;
        for(int i=0;i

152.乘积最大子序列

思路:使用了两个dp数组

dp_min[ i ] :截止到i的子序列积的最小负数(最小负积)

dp_max[ i ] :截止到i的子序列积的最大正数 (最大正积)

初始值:根据nums[0]的来设置

nums[0]>0 : dp_max[0] = nums[0]   dp_min[0] = 0 (0用来表示不存在,不可能)

nums[0]<0 : dp_max[0] = 0   dp_min[0] =nums[0] 

状态转移方程:根据nums[ i ]的正负分别讨论

nums[ i ]>0 :

dp_max[ i ] = dp_max[ i-1]>0 ? dp_max[ i-1]*nums[i] :nums[ i]  

dp_min[ i ] =dp_min [ i-1] *nums[ i ]

//区别在于若 i 之前的最大正积不存在,nums[ i ]可以站出来充当最大正积,但若是最小正积不存在,nums[ i]是个正数,它也无能为力

nums[ i ]<0 :

dp_max [ i ] = dp_min[ i-1] *nums[ i ]

dp_min[ i  ] = dp_max[ i-1]>0 ? dp_max[ i-1]*nums[ i]: nums[ i]

//注意负数的最大正积要乘上上一个最小负积来更新

class Solution {
public:
    int maxProduct(vector& nums) {
        int n=nums.size();
        vector dp_min(n); //截止到i的子序列积的最小负数
        vector dp_max(n); //截止到i的子序列积的最大正数
        
        /*设置初始值*/
        if(nums[0]>0)
        {
            dp_min[0]=0; //0表示不存在
            dp_max[0]=nums[0];
        }
        else
        {
            dp_min[0]=nums[0];
            dp_max[0]=0;
        }
        
        int max=nums[0];
        for(int i=1;i0)
            {
                dp_min[i]=dp_min[i-1]*nums[i];
                dp_max[i]=dp_max[i-1]>0 ? dp_max[i-1]*nums[i]:nums[i]; //如果i之前最大正积不存在,nums[i]为正,则nums[i]为当前最大正积
            }
            else
            {
                dp_min[i]=dp_max[i-1]>0 ? dp_max[i-1]*nums[i]:nums[i];
                dp_max[i]=dp_min[i-1]*nums[i];
            }
            if(dp_max[i]>max)
                max=dp_max[i]; //更新最大值
        }
        return max;
         }
};

55.跳跃游戏

题意:给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

思路:从后往前递归,对于当前的dest,若能找到前面的一个i使得nums[i]+i > dest,则再将 i 作为dest继续递归。超时。。

题解思路:

  1. 如果所有元素都不为0, 那么一定可以跳到最后;(至少能走一步)
  2. 从后往前遍历,如果遇到nums[i] = 0,就找i前面的元素j,使得nums[j] > i - j。如果找不到,则不可能跳跃到num[i+1],返回false
class Solution { //我的递归思路
public:
    bool canJump(vector& nums) {
        int dest=nums.size()-1;
        return Traceback(nums,dest);
        
    }
    
    bool Traceback(vector& nums,int dest)
    {
        if(dest==0) return true; //第一个点总是可以达到
        bool flag=false;
        for(int i=dest-1;i>=0;i--)  //寻找是否存在路径到达当前的dest
        {
            if(nums[i]+i>=dest)
            flag=Traceback(nums,i);
            if(flag==true)
                return flag;
        }
        return false;  //所有路径都失败,则最终失败
    }
};

class Solution {  //题解思路
public:
    bool canJump(vector& nums) {
        for(int i=nums.size()-2;i>=0;i--)
        {
            if(nums[i]==0)
            {
                int j;
                for( j=i-1;j>=0;j--)
                    if(nums[j]+j>i) break;
                if(j<0) return false;
            }
        }
        return true;
    } 
};

56.合并区间

思路:从前往后找能够包含的集合,将其包含!!我找反了。。

注意下面几组测试样例:

[[0,0],[1,2],[5,5],[2,4],[3,3],[5,6],[5,6],[4,6],[0,0],[1,2],[0,2],[4,5]]
[[2,3],[4,5],[6,7],[8,9],[1,10]]
[[1,2],[4,5],[6,7],[3,8],[9,10]]
[[1,4],[0,0]]

[[201,204],[207,210],[203,212]] !!

class Solution {
public:
    //先排个序,然后扫一遍即可
    static int cmp(const vector &a,const vector &b){
        if(a[0]==b[0])
            return a[1]> merge(vector>& inter) {
        sort(inter.begin(),inter.end(),cmp);
        int i,j;
        vector> res;
        i=0;
        while(i{s,t});
            if(j>=inter.size())
                break;
            i=j;
        }
        return res;
    }
};

24.两两交换链表中的结点

思路:典型的递归。可以发现函数返回的是完成交换后的链表的头结点,所以可以递归完成。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head==NULL) return NULL;  //无结点
        if(head->next==NULL) return head;  //只有一个结点
        ListNode* p =head;
        ListNode* q =head->next;
        p->next=swapPairs(q->next);
        q->next=p;
        return q;
    }
};

6.Z字型变换

思路:为每一行建立一个字符串。从左到右迭代 s,将每个字符添加到合适的行。

         用bool变量goingDown标志当前的方向,只有当我们向上移动到最上面的行或向下移动到最下面的行时,当前方向才会发生改变。

class Solution {
public:
    string convert(string s, int numRows) {
        if(numRows==1) return s;
        int len=s.length();
        int size=numRows res;
        //vector res(min(numRows, int(s.size())));
        for(int i=0;i

7.整数反转

题目特别要求:假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231,  231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

int的最大值:int max = 0x7fffffff  或 INT_MAX (2e31 -1)

int的最小值:int min = 0x80000000  或 INT_MIN (-2e31)

class Solution {
public:
    int reverse(int x) {
        int max = 0x7fffffff, min = 0x80000000;//int的最大值最小值
        queue q;
        int cnt=0;
        while(x!=0)
        {
            q.push(x%10);
            x=x/10;
            cnt++;
        }
        long long int ans=0;

        while(!q.empty())
        {
            int num=q.front();
            q.pop();
            ans=ans*10+num;
        }
        if(ans>max||ans

8.字符串转换整数

注意的是,因为输入是字符串形式,长度任意,所以可能出现超出32位有符号整数的情况,所以要在累加的过程中及时判断,一旦超过就及时退出循环。

class Solution {
public:
    int myAtoi(string str) {
        int len=str.length();
        bool minus=false;
        long long int ans=0;
        int i=0;
        while(str[i]==' ') i++;
        if(i==len) return 0; //说明空字符串
        if(str[i]=='-')
        {
            minus=true; i++;
        }
        else if(str[i]=='+')
        {
            minus=false; i++;
        }  
        while(i='0'&&str[i]<='9')
        {
            ans=ans*10+str[i]-'0';
            if(minus&&(-ans)INT_MAX) return INT_MAX;
            i++;
        }
        
        if(!minus) return ans;
        else return -1*ans;
    }
};

9.回文数

class Solution {
public:
    bool isPalindrome(int x) {
        if(x<0) return false;
        if(x==0) return true;
        vector v;
        while(x!=0)
        {
            v.push_back(x%10);
            x=x/10;
        }
        int len=v.size();
        int i=0,j=len-1;
        while(i<=j&&v[i]==v[j])
        {
            i++;
            j--;
        }
        if(i>j) return true;
        else return false;
    }
};

12.整数转罗马数字

分析:可以像硬币找零那样,由贪心算法进行划分。每次9、90、900和4、40、400的情况要特别注意怎么处理。

class Solution {
public:
    string intToRoman(int num) {
        if(num==4)
        {
            string ans="IV";
            return ans;
        }
        if(num==40)
        {
            string ans="XL";
            return ans;
        }
        if(num==400)
        {
            string ans="CD";
            return ans;
        }
        int nums[7]={1000,500,100,50,10,5,1};
        char sign[7]={'M','D','C','L','X','V','I'};
        vector v;
        int cnt=0;
        for(int i=0;i<7;i++)
        {
            if(num==0) break;
            int t=num/nums[i];
            if(t==0) continue; 
            if(t==4)
            {
              /*
                if(cnt==0)
                {
                    v.push_back(sign[i]);
                    v.push_back(sign[i-1]);
                    cnt+=2;
                }
              */
                //if(cnt>=1&&(v[cnt-1]=='D'||v[cnt-1]=='L'||v[cnt-1]=='V')) //处理900、90、9的情况
                if(cnt>=1&&v[cnt-1]==sign[i-1]) //处理900、90、9的情况
                {
                    v.pop_back();
                    v.push_back(sign[i]);
                    v.push_back(sign[i-2]);
                    cnt++;
                }
                else{
                    v.push_back(sign[i]);
                    v.push_back(sign[i-1]);
                    cnt+=2;
                }
            }
            else{
                for(int j=0;j

这是评论中的代码 不要太简洁啊...

class Solution {
public:
    string intToRoman(int num) {
        int values[]={1000,900,500,400,100,90,50,40,10,9,5,4,1};
        string reps[]={"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
        
        string res;
        for(int i=0; i<13; i++){
            while(num>=values[i]){
                num -= values[i];
                res += reps[i];
            }
        }
        return res;
    }
};

13.罗马数字转整数

看一下我代码巨长的愚蠢办法..

class Solution {
public:
    int romanToInt(string s) {
        int len=s.length();
        int i=len-1;
        int ans=0;
        while(i>=0)
        {
            if(s[i]=='V') //5
            {
                if(i>0&&s[i-1]=='I') 
                {
                    ans+=4;
                    i-=2;
                }
                else
                {
                    ans+=5;
                    i--;
                }
            }
            else if(s[i]=='I') //1
            {
                while(s[i]=='I')
                {
                    ans+=1;
                    i--;
                }
            }
            else if(s[i]=='X')  //10
            {
                if(i>0&&s[i-1]=='I')
                {
                    ans+=9;
                    i-=2;
                }
                else
                {
                    while(s[i]=='X')
                    {
                        ans+=10;
                        i--;
                    }
                }
            }
            else if(s[i]=='L')  //50
            {
                if(i>0&&s[i-1]=='X')
                {
                    ans+=40;
                    i-=2;
                }
                else
                {
                    ans+=50;
                    i--;
                }
            }
            else if(s[i]=='C')  //100
            {
                if(i>0&&s[i-1]=='X')
                {
                    ans+=90;
                    i-=2;
                }
                else
                {
                    while(s[i]=='C')
                    {
                        ans+=100;
                        i--;
                    }
                }
            }
            else if(s[i]=='D')  //500
            {
                if(i>0&&s[i-1]=='C')
                {
                    ans+=400;
                    i-=2;
                }
                else
                {
                    ans+=500;
                    i--;
                }
            }
            else if(s[i]=='M')
            {
                if(i>0&&s[i-1]=='C')
                {
                    ans+=900;
                    i-=2;
                }
                else
                {
                    while(s[i]=='M')
                    {
                        ans+=1000;
                        i--;
                    }
                }
            }
        }
        return ans;
    }
};

评论中的思路:这题懂了就非常简单。首先建立一个HashMap来映射符号和值,然后对字符串从左到右来,如果当前字符代表的值不小于其右边,就加上该值;否则就减去该值。以此类推到最左边的数,最终得到的结果即是答案   妙啊!!

 

你可能感兴趣的:(机试准备)