力扣刷题记录

leetcode刷题记录

1、两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

vector与数组无区别

方法一:暴力遍历

class Solution {
public:
	vector<int> twoSum(vector<int>& nums, int target) {
	int i=0;
	int j=0;
	for( i=0;i<nums.size();i++)`
	{
		for( j=i+1;j<nums.size();j++)`
		{
		if(nums[i]+nums[j]==target)`
			{
				return {i,j};`
			}
		}
	}
	return {};
	}
};

方法二:哈希表

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

auto可以自动转变类型;

unordered_map无序映射是存储键值和映射值组合形成的元素的关联容器,它允许根据键快速检索单个元素。

2、两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int len1=1;   //l1和l2的长度
        int len2=1;
        ListNode* p=l1;
        ListNode* q=l2;
        while(p->next!=NULL)
        {
            len1++;
            p=p->next;
        }
        while(q->next!=NULL)
        {
            len2++;
            q=q->next;
        }
        if(len1>len2)
        {
            for(int i=0;i<len1-len2;i++)
            {
                
                q->next=new ListNode(0);
                q=q->next;

                
            }
            
        }else{
            for(int i=0;i<len2-len1;i++)
            {
                
               
                p->next=new ListNode(0);
                p=p->next;
                
                
            }
        }
        p=l1;
        q=l2;
        bool count=false;   //进位
        int i=0;
        ListNode* l3=new ListNode(-1);
        ListNode* w=l3;
        while(p!=NULL&&q!=NULL)
        {
            i=count+p->val+q->val;
            w->next=new ListNode(i%10);
            count=i>=10?true:false;
            p=p->next;
            q=q->next;
            w=w->next;
          
        }
        if(count)
        {
            w->next=new ListNode(1);
            w=w->next;
        }
        return l3->next;
    }

};

ListNode是包含当前值和地址,所以使用前应判断指向是否为空

简化后:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* l3=new ListNode(-1);//用来存储结果
        ListNode* w=l3;
        bool count=false;
        int sum=0;
        while(l1!=NULL || l2!=NULL)
        {
            sum=0;
            if(l1!=NULL)
            {
                sum+=l1->val;
                l1=l1->next;
            }
            if(l2!=NULL)
            {
                sum+=l2->val;
                l2=l2->next;
            }
            sum+=count;
            w->next=new ListNode(sum%10);
            w=w->next;
            count=sum>=10?true:false;

        }
        if(count)
        {
            w->next=new ListNode(1);
        }
        return l3->next;
    }

};
3、无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

方法一:滑动窗口

其中再次涉及到哈希表

unordered_map介绍(单向迭代器)

count(),返回的是被查找元素的个数。如果有,返回1;否则,返回0

erase()删除一个元素

find()返回被查找元素的位置,没有则返回map.end()。

map与unordered_map相比:

map底层实现为红黑数,undered_map底层实现为哈希表,两者均不能有重复的建,均支持[]运算符

map与multimap相比:

两者底层实现均为红黑树,但是multimap支持重复的键,不支持[]运算符

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len=0;
        unordered_map<char, int> hashtable;
        for(int i=0;i<s.size();i++)
        {
            for(int j=i;j<s.size();j++)
            {
                auto it=hashtable.find(s[j]);
               
                if(it!=hashtable.end())
                {
                    break;
                }
                hashtable[s[j]]=j;
            }
            if(hashtable.size()>len)
            {
                len=hashtable.size();
            }
            
            hashtable.clear();

        }
        return len;
    }
};
unordered_set介绍(单向迭代器)

unordered_set::insert插入元素
unordered_set::find寻找元素位置
unordered_set::erase删除具体位置的元素unordered_set::count(e)判断内部是否有e,返回1/0

简化后:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 哈希集合,记录每个字符是否出现过
        unordered_set<char> occ;
        int n = s.size();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1, ans = 0;
        // 枚举左指针的位置,初始值隐性地表示为 -1
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.erase(s[i - 1]);
            }
            while (rk + 1 < n && !occ.count(s[rk + 1])) {
                // 不断地移动右指针
                occ.insert(s[rk + 1]);
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1);
        }
        return ans;
    }
};


4、寻找两个正序数组的中位数

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。

进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
    
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
    
输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000

输入:nums1 = [], nums2 = [1]
输出:1.00000

方法一:合并数组,直接取中位数(O(m+n))

vector介绍
sort(v.begin(),v.end());排序
push_back()/pop_back();插入/删除(尾部)
size()元素的个数
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int len1=nums1.size();
        int len2=nums2.size();
        if(len1==0)
        {
            if(len2%2==0)
            {
                return (nums2[len2/2]+nums2[len2/2-1])/2.0;
            }
            else
            {
                return nums2[len2/2];
            }
        }
        if(len2==0)
        {
            if(len1%2==0)
            {
                return (nums1[len1/2]+nums1[len1/2-1])/2.0;
            }
            else
            {
                return nums1[len1/2];
            }
        }
        for(int i=0;i<len2;i++)
        {
            nums1.push_back(nums2[i]);
        }
        sort(nums1.begin(),nums1.end());
        int len=nums1.size();
        if(len%2==0)
        {
            return (nums1[len/2]+nums1[len/2-1])/2.0;
        }
        else{
            return nums1[len/2];
        }

    }
};

方法二:二分法

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

		if (n > m)  //保证数组1一定最短 //为了加快速度 对长度短的进行二分
		{
			return findMedianSortedArrays(nums2, nums1);
		}
		int LMax1 = 0, LMax2 = 0, RMin1 = 0, RMin2 = 0, c1, c2, lo = 0, hi = n;
		while (lo <= hi) {
			c1 = (hi + lo + 1) / 2;
			c2 = (m + n) / 2 - c1;

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

			if (LMax1 > RMin2)
				hi = c1 - 1;
			else if (LMax2 > RMin1)
				lo = c1 + 1;
			else
				break;
		}
		if ((m + n) % 2)
			return min(RMin1, RMin2);
		else
			return ((int64_t)max(LMax1, LMax2) + (int64_t)min(RMin1, RMin2)) / 2.0;
	}
};
349、两个数的交集

给定两个数组,编写一个函数来计算它们的交集。

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

方法一:两轮循环,依次比较,借用哈希表,从O(m*n)下降至O(m+n)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1,set2;
        for(auto num:nums1)  //顺序访问
        {
            set1.insert(num);
        }
        for(auto num:nums2)
        {
            set2.insert(num);
        }
        return intersectionSet(set1,set2);
    }

    vector<int> intersectionSet(unordered_set<int> set1,unordered_set<int> set2){
        if(set1.size()>set2.size())
        {
            return intersectionSet(set2,set1);
        }
        vector<int> result;
        for(auto num:set1)
        {
            if(set2.count(num))
            {
                result.push_back(num);
            }
        }
        return result;
    }
};
19、删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.

方法一:我们可以设想假设设定了双指针 p 和 q 的话,当 q 指向末尾的 NULL,p 与 q 之间相隔的元素个数为 n 时,那么删除掉 p 的下一个指针就完成了要求。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *empty= new ListNode(0);
        empty->next=head;
        ListNode *a=empty;
        ListNode *b=empty;
        for(int i=0;i<n+1;i++)
        {
            b=b->next;
        }
        while(b)
        {
            a=a->next;
            b=b->next;
        }
        ListNode *delNode=a->next;
        a->next=a->next->next;
        delete delNode;

        ListNode *ret=empty->next;
        delete empty;
        return ret;
        // return empty->next;
        // empty=empty->next;
        // return empty;

   
941、有效的山脉数组

给定一个整数数组 A,如果它是有效的山脉数组就返回 true,否则返回 false

让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组:

给定一个整数数组A,如果它是有效的山脉数组就返回true,否则返回false。
让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组:
1\A.length >= 3
2\在 0 < i < A.length - 1 条件下,存在 i 使得:
A[0] < A[1] < ... A[i-1] < A[i]
A[i] > A[i+1] > ... > A[A.length - 1]

方法一:双指针法

class Solution {
public:
    bool validMountainArray(vector<int>& A) {
        if(A.size()<3)
        {
            return false;
        }
        int i=0,j=A.size()-1;
        while(i<A.size()-1 && A[i]<A[i+1])
        {
            ++i;
        }
        while(j>0&&A[j-1]>A[j])
        {
            j--;
        }
        if(i==0||j==A.size()-1)
        {
            return false;
        }
        return (i==j)?true:false; 
    }
};
11、盛最多水的容器

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

img

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

方法一:暴力枚举

class Solution {
public:
    int maxArea(vector<int>& height) {
        if(height.size()<2)
        {
            return 0;
        }
        int Max=0;
        for(int i=0;i<height.size()-1;i++)
        {
            for(int j=i;j<height.size();j++)
            {
                Max=Max<min(height[i],height[j])*(j-i)?min(height[i],height[j])*(j-i):Max;
            }
        }
        return Max;

    }
};

方法二:双指针

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

    }
};
16、最近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2)

方法一:利用排序+双指针,时间复杂度降低至O(n^2)

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        
        int num2=10000;  //记录每次的差值
        int num=0;  //记录结果
        if(nums.size()<3)
        {
            return 0;
        }
        sort(nums.begin(),nums.end());
        int i=nums.size()-3;
        while(i>=0)
        {
            int j=i+1,k=nums.size()-1;
            while(j<k)
            {
                num=abs(nums[i]+nums[j]+nums[k]-target)<num2?nums[i]+nums[j]+nums[k]:num;
                num2 = abs(nums[i] + nums[j] + nums[k] - target)<num2? abs(nums[i] + nums[j] + nums[k] - target):num2;

                if(nums[i]+nums[j]+nums[k]-target==0)
                {
                    return target;
                }else if(nums[i]+nums[j]+nums[k]-target>0)
                {
                    k--;
                }else
                {
                    j++;
                }
            }

            i--;
        }
        return num;

    }
};
18、四数之和

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

**注意:**答案中不可以包含重复的四元组。

示例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

方法一:参考之前的四数之和

class Solution {
public:
    std::vector<std::vector<int>> fourSum(std::vector<int>& nums, int target) {
        std::vector<std::vector<int>> result;
        if (nums.size() < 4)
        {
            return result;
        }
        sort(nums.begin(), nums.end());
        int i = nums.size() - 4;
        while (i >= 0)
        {
            while (i > 0 && nums[i] == nums[i - 1])
            {
                i--;
            }
            int j = nums.size() - 3;
            while (j > i)
            {
                while (j > i+1 && nums[j] == nums[j - 1])
                {
                    j--;
                }
                int k = j + 1, m = nums.size() - 1;
                while (k < m)
                {
                    if (nums[i] + nums[j] + nums[k] + nums[m] == target)
                    {
                        result.push_back({ nums[i],nums[j],nums[k],nums[m] });
                        k++;
                        m--;
                        while (k < m && nums[k] == nums[k - 1])
                        {
                            k++;
                        }
                        while (k < m && nums[m] == nums[m + 1])
                        {
                            m--;
                        }
                    }
                    else if (nums[i] + nums[j] + nums[k] + nums[m] > target)
                    {
                        m--;
                    }
                    else {
                        k++;
                    }

                }
                j--;
            }
            i--;
        }
        return result;

    }
};
36、有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

img

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例:

输入:
[
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"]
]
输出: true

方法一:依次访问,用三个数组代表行、列、方块((i / 3) * 3 + j / 3)。这里全初始化为0,用减“1”来给对应位置赋值1,如果有不是0的就为假、

class Solution {
public:
    bool isValidSudoku(std::vector<std::vector<char>>& board) {
        
        std::vector<std::vector<int>> row(9,std::vector<int>(9,0));
        std::vector<std::vector<int>> col(9, std::vector<int>(9, 0));
        std::vector<std::vector<int>> boxes(9, std::vector<int>(9, 0));
        for (int i = 0; i < 9; i++)
        {
            for (int j = 0; j < 9; j++)
            {
                if (board[i][j] == '.')
                {
                    continue;
                }
                int index= (i / 3) * 3 + j / 3;
                int val = board[i][j] - '1';
                if (row[i][val] == 0 && col[j][val]==0 && boxes[index][val] == 0)
                {
                    row[i][val] = 1;
                    col[j][val] = 1;
                    boxes[index][val] = 1;
                }
                else {
                    return false;
                }
            }
        }
        return true;
    }
};
30、串联所有单词的子串

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

//示例1:
//输入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
//输出:[0,9]
//解释:
从索引 09 开始的子串分别是 "barfoo""foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。

方法一:

利用两个hash表,表一用来存储数组的字符串;

表二用来存储不同位置的字符串;

比较两个hash表是否相同。

注意点:

1、unordered_map.substr(start,size)后面的是尺寸

2、unordered_map判断相同的时候,顺序不同也不算相同

3、把比较相同转化为内部的变量比较

class Solution {
public:
	std::vector<int> findSubstring(std::string s, std::vector<std::string>& words) {
		std::vector<int> result;
		int wordNum = words.size();
		if (wordNum == 0)
		{
			return result;
		}
		int wordLen = words[0].size();
		std::unordered_map<std::string, int> Num1;
		for (int i = 0; i < wordNum; i++)
		{
			Num1[words[i]]++;
		}
		//for (const auto &pair : Num1) {

		//	std::cout << pair.first << ": " << pair.second << '\n';

		//}
		std::unordered_map<std::string, int> Num2;
		for (int i = 0; i < s.size() - wordLen * wordNum + 1; i++)
		{
			
			//std::cout << i << std::endl;
			int num = 0;
			while (num<wordNum)
			{
				//std::cout << num << std::endl;
				std::string ret = s.substr(i + num*wordLen, wordLen);
				//std::cout << i + num * wordLen<< i + (num + 1) * wordLen << ret << std::endl;
				//std::cout << "0" << ret << std::endl;
				if (Num1[ret]!=0)
				{
				//std::cout << "1"<<ret << std::endl;

					Num2[ret]++;
					if (Num2[ret] > Num1[ret])
					{
						break;
					}
				//std::cout << "2" << ret << std::endl;
				}
				else
				{
					break;
				}
				if (num == wordNum-1)
				{
					result.push_back(i);
				}
				num++;
			}

			Num2.clear();
			
		}
		return result;

	}
};
49、字母异位词分组

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

方法一:

哈希表:利用每个字符串的ASCII值,乘积+和可以确定一个字符串的小写字母;并以此为key值。对应的不同排序的字符串为value值。

注意:在涉及到大规模运算的时候,要防止溢出,所以此处用unsigned int。

class Solution {
public:
	std::vector<std::vector<std::string>> groupAnagrams(std::vector<std::string>& strs) {
		std::vector<std::vector<std::string>> result;
		if (strs.size() == 0)
		{
			return result;
		}

		std::unordered_map<unsigned int, std::vector<std::string>> map1;
		for (int i = 0; i < strs.size(); i++)
		{
			// 设置unsigned int 防止溢出 
			unsigned int  num1 = 0;
			unsigned int num2 = 1;
			for (int j = 0; j < strs[i].size(); j++)
			{
				num1 += strs[i][j];
				num2 *= strs[i][j];
			}
			map1[num1+num2].push_back(strs[i]);
		}
		for (const auto &pair:map1)
		{
			result.push_back(pair.second);
		}
		return result;

	}
};

452、用最少数量的箭引爆气球

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。

一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。

//实例一;
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球

方法一:排序+贪心算法

//注意lambda的使用
sort(points.begin(), points.end(), [](const std::vector<int>& a, const std::vector<int>& b) {
            return a[1] < b[1];
        });
class Solution {
public:
    int findMinArrowShots(std::vector<std::vector<int>>& points) {
        if (points.size() == 0)
        {
            return 0;
        }
        sort(points.begin(), points.end(), [](const std::vector<int>& a, const std::vector<int>& b) {
            return a[1] < b[1];
        });
        int pos = points[0][1];
        int num = 1;
        for (const auto &it : points)
        {
            if (it[0] > pos)
            {
                pos = it[1];
                ++num;
            }
        }
        return num;

    }
};

双指针

42、接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

方法一:

抓住关键思想:每个位置的水量 等于 两侧的最大边中较小的边减去当前位置的值。

class Solution {
public:
    int trap(std::vector<int>& height) {
        if (height.size() <= 1)
        {
            return 0;
        }
        int num = 0;
        for (int i = 1; i < height.size() - 1; ++i)
        {
            num += IMax(height, i);
        }
        return num;
        
    }

    int IMax(std::vector<int>& height,int k)
    {
        if (k<1 || k>height.size() - 1)
        {
            return 0;
        }
        int leftMax = 0;
        int rightMax =0;
        for (int i = 0; i < k; ++i)
        {
            leftMax = std::max(leftMax,height[i]);
        }
        for (int j = height.size() - 1; j > k; --j)
        {
            rightMax = std::max(rightMax, height[j]);
        }

        return std::min(leftMax, rightMax)>height[k]? std::min(leftMax, rightMax) - height[k]:0;

    }
};

方法二:

对上一个方法进行改进,上一个方法是O(n^2),这个是O(n);利用动态规划的思想,把n次遍历找最值的问题转化为提前存储好对应的最大值。

class Solution {
public:
    int trap(std::vector<int>& height) {
        if (height.size() <= 1)
        {
            return 0;
        }
        int left = 0;
        int right = 0;
        std::vector<int> leftMax;
        std::vector<int> rightMax(height.size(),0);
        for (int i = 0; i < height.size(); ++i)
        {
            left = std::max(left, height[i]);
            leftMax.push_back(left);
        }
        for (int i = height.size() - 1; i > -1; --i)
        {
            right = std::max(right, height[i]);
            rightMax[i] = right;
        }
        
        int num = 0;
        for (int i = 1; i < height.size()-1; ++i)
        {
            if (std::min(leftMax[i], rightMax[i]) > height[i])
            {
                num += std::min(leftMax[i], rightMax[i]) - height[i];
            }
        }
        return num;
    }

};

方法三:双指针

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

};
61、旋转列表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数
示例一:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1: 5->1->2->3->4->NULL
向右旋转 2: 4->5->1->2->3->NULL

方法一:先把链表首尾连接;
记录链表的长度;
利用规律是从len-k%len的位置开始,即表头位置;
最后再断开表头和表尾。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head)
        {
            return nullptr;
        }
        ListNode* end = head;
        int len = 1;
        while (end->next)
        {
            end = end->next;
            ++len;
        }
        end->next = head;
        for (int i = 0; i <= len - k % len; ++i)
        {
            end = end->next;
        }
        ListNode* start = end;
        for (int i = 0; i < len - 1; ++i)
        {
            end = end->next;
        }
        end->next = nullptr;
        return start;
    }
};

递归,深度优先搜索

98. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
方法一:递归
注意:需要左右两边都是真才是真,不能只考虑单独一边。体现在最后一个return上。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {

        return helper(root,LONG_MIN,LONG_MAX);
    }
    bool helper(TreeNode* root,long long downer,long long upper)
    {
        if(!root)
        {
            return true;
        }
        if( root->val<=downer ||root->val >= upper)
        {
            return false;
        }else
        {
            return helper(root->left,downer,root->val) && helper(root->right,root->val,upper);
        }

        
    }
};

方法二:中序遍历(不太熟悉)
按中序遍历的顺序是升序的,如果不满足就不是。所以与前一个元素进行比较大小。

class Solution {
public:
    bool isValidBST(TreeNode* root) {
        long long upper = (long long)INT_MIN - 1;
        std::stack <TreeNode*> ret;
        while(!ret.empty() || root)
        {
            while(root)
            {
                ret.push(root);
                root=root->left;
            }
            //此循环后root指向空
            root=ret.top();
            std::cout<<root->val<<std::endl;
            ret.pop();
            if(root->val<=upper)
            {
                return false;
            }
            upper=root->val;
            root=root->right;
        }
        return true;
    }
};
100、相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

输入:       1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

输出: true
输入:      1          1
          /           \
         2             2

        [1,2],     [1,null,2]

输出: false

方法一:递归实现,有点参考98题的第一种方法。必须左右两边是真才真,这也是递归需要注意的问题。利用空指针的条件判断为真。

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(!p&&!q)
        {
            return true;
        }else if(!p&&q)
        {
            return false;
        }else if(p&&!q)
        {
            return false;
        }else
        {
            if(p->val==q->val)
            {
                return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
            }else
            {
                return false;
            }
        }
    }
};
101、对称二叉树

给定一个二叉树,检查它是否是镜像对称的 。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

方法一:递归。把镜像问题转化为判断两个子树是否对称。在100题中,判断相同的树可以借鉴。只需要思考:相同的树是判断对应的左右子树,即左子树和左子树对比,右子树和右子树对比;此题中考虑镜像,可以用左子树和右子树对比,右子树和左子树对比。

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(!root)
        {
            return true;
        }else
        {
            return isSame(root->left,root->right);
        }
    }
    bool isSame(TreeNode* root1,TreeNode * root2)
    {
        if(!root1&&!root2)
        {
            return true;
        }else if(!root1&&root2)
        {
            return false;
        }else if(root1&&!root2)
        {
            return false;
        }else
        {
            if(root1->val==root2->val)
            {
                return isSame(root1->left,root2->right)&&isSame(root1->right,root2->left);
            }else
            {
                return false;
            }
        }
    }
};

方法二:迭代。思想大致是一样的,这里用到队列,把可能相同的元素放在一起存入。

**首先我们引入一个队列,这是把递归程序改写成迭代程序的常用方法。**初始化时我们把根节点入队两次。每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像),然后将两个结点的左右子结点按相反的顺序插入队列中。当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束。

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(!root)
        {
            return true;
        }else
        {
            return isSame(root->left,root->right);
        }
    }
    bool isSame(TreeNode* root1,TreeNode * root2)
    {
        std::queue<TreeNode*> ret;
        ret.push(root1);
        ret.push(root2);
        while(!ret.empty())
        {
            root1=ret.front();
            ret.pop();
            root2=ret.front();
            ret.pop();
            if(!root1&&!root2) 
            {
                continue;
            }
            if((!root1||!root2)||(root1->val!=root2->val))
            {
                return false;
            }
            ret.push(root1->left);
            ret.push(root2->right);
            ret.push(root1->right);
            ret.push(root2->left);
        }
        return true;
    }
};
104、二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

示例:
给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度是3。

方法一:递归 :max(l,r)+1

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(!root)
        {
            return 0;
        }
        return std::max(maxDepth(root->left),maxDepth(root->right))+1;

    }
};

方法二:广度优先搜索

每次拓展下一层的时候,不同于广度优先搜索的每次只从队列里拿出一个节点,我们需要将队列里的所有节点都拿出来进行拓展,这样能保证每次拓展完的时候队列里存放的是当前层的所有节点,即我们是一层一层地进行拓展,最后我们用一个变量 ans 来维护拓展的次数,该二叉树的最大深度即为ans。

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(!root) return 0;
        std::queue<TreeNode*> ret;
        int ans=0;
        ret.push(root);
        while(!ret.empty())
        {
            int ms=ret.size();
            while(ms>0)
            {
                root=ret.front();
                ret.pop();
                if(root->left) ret.push(root->left);
                if(root->right) ret.push(root->right);
                --ms;
            }
            ++ans;
        }
        return ans;
    }
};
105、从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

  3
   / \
  9  20
    /  \
   15   7

方法一:递归。想到利用前序第一个点是树根。

注意:第一:用到哈希表的方法在中序排序中找到树根;

第二:用位置代替多次的拷贝数组。就在原数组上进行操作,只是新的数组用位置表示。
注意:涉及到后序排序的前半段的末尾点和后半段的起始点,需要用preorder的初始点或者末尾点外加左子树的大小或者右子树的大小来计算,不可以直接想当然用topId+1或者topId-1来代替

class Solution {
public:
    std::unordered_map<int, int> ret;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for (int i = 0; i < inorder.size(); ++i)
        {
            ret[inorder[i]] = i;
        }
        for (const auto& iter : ret)
        {
            std::cout << iter.first << iter.second << std::endl;
        }
        return makeTree(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
    }
    TreeNode* makeTree(const vector<int>& preorder,const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right)
    {
        if (preorder_left > preorder_right)
        {
            return nullptr;
        }
        int preorder_top = preorder_left;
        int inorder_top = ret[preorder[preorder_top]];
        TreeNode* root = new TreeNode(preorder[preorder_top]);
        int size = inorder_top - inorder_left;
        root->left = makeTree(preorder, inorder, preorder_left + 1, preorder_left+size, inorder_left, inorder_top - 1);
        root->right= makeTree(preorder, inorder, preorder_left+size+1, preorder_right, inorder_top+1, inorder_right);
        return root;
    }
};

106. 从中序与后序遍历序列构造二叉树

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

方法类似于105题目,但是注意顶点的取值:涉及到后序排序的前半段的末尾点和后半段的起始点,需要用postorder的初始点或者末尾点外加左子树的大小或者右子树的大小来计算,不可以直接想当然用topId+1或者topId-1来代替

class Solution {
    std::unordered_map<int, int> inHash;
public:
    TreeNode* buildTree(std::vector<int>& inorder, std::vector<int>& postorder) {
        int len = inorder.size();
        if (len == 0)
        {
            return nullptr;
        }
        for (int i=0;i<len;++i)
        {
            inHash[inorder[i]] = i;
        }
        return makeTree(inorder, postorder, 0, len - 1, 0, len - 1);
    }
    TreeNode* makeTree(const std::vector<int>& inorder, const std::vector<int>& postorder, int inLeft, int inRight, int postLeft, int postRight)
    {
        if (postLeft > postRight)
        {
            return nullptr;
        }
        TreeNode* root =new TreeNode(postorder[postRight]);
        int topId = inHash[postorder[postRight]];
        int len2 = inRight - topId;
        root->left = makeTree(inorder, postorder, inLeft, topId-1, postLeft, postRight-len2- 1);
        root->right = makeTree(inorder, postorder, topId + 1, inRight, postRight-len2, postRight-1);
        //涉及到后序排序的前半段的节点和后半段的起始点,需要用初始或者末尾点加上左子树的大小或者右子树的大小来计算,不可以直接想当然用topId+1或者topId-1来代替
        return root;
    }
};
17、电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

方法一:(回溯法)穷举法(递归+DFS)

注意三处注释的部分

class Solution {
public:
    std::vector<std::string> letterCombinations(std::string digits) {
        std::vector<std::string> combinations;
        if (digits.empty())
        {
            return combinations;
        }
        std::unordered_map<char, std::string> phoneMap;
        phoneMap['2'] = "abc";
        phoneMap['3'] = "def";
        phoneMap['4'] = "ghi";
        phoneMap['5'] = "jkl";
        phoneMap['6'] = "mno";
        phoneMap['7'] = "pqrs";
        phoneMap['8'] = "tuv";
        phoneMap['9'] = "wxyz";
        std::string combination;
        trackBack(combinations, phoneMap, 0, digits, combination);
        

        return combinations;

    }
//如果此处用std::vector<std::string> & combinations的&。那么改变只是拷贝值,和原来的值无关。
    void trackBack(std::vector<std::string>& combinations, const std::unordered_map<char, std::string> &phoneMap, int index, const std::string digits, std::string combination)
    {
        //size()和length()功能和代码完全一样
        if (index == digits.size())
        {
            combinations.push_back(combination);
        }
        else
        {
            char digit = digits[index];
            //当Map容器使用const修饰,不可以用operator[]访问键值元素。因为[]是可以改变map的非const操作
            std::string letter = phoneMap.at(digit);
            for (const auto& iter : letter)
            {
                combination.push_back(iter);
                trackBack(combinations, phoneMap, index + 1, digits, combination);
                combination.pop_back();
            }
        }



    }
};
108. 将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

方法一:分治

class Solution {
public:
    TreeNode* sortedArrayToBST(std::vector<int>& nums) {
        return makeTree(nums, 0, nums.size() - 1);
    }

    TreeNode* makeTree(std::vector<int>& nums, int left, int right)
    {
        if (left>right)
        {
            return nullptr;
        }
        int mid = (left+right) / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = makeTree(nums, left, mid-1);
        root->right = makeTree(nums, mid + 1, right);
        return root;
    }

};
109、有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

方法一:分治+递归

注意:取链表中位数的方法

class Solution {
 public:
     TreeNode* sortedListToBST(ListNode* head) {
         ListNode* end = head;
         while (end)
         {
             end = end->next;
         }
         return makeTree(head, head, end);
     }
     TreeNode* makeTree(ListNode* head, ListNode* left, ListNode* right)
     {
         if (left == right)
         {
             return nullptr;
         }
         ListNode* mid = midNode(head,left,right);
         TreeNode* root = new TreeNode(midNode(head,left,right)->val);
         root->left = makeTree(head, left, mid);
         root->right = makeTree(head, mid->next, right);
         return root;
     }

     ListNode* midNode(ListNode* head,ListNode* left, ListNode* right)
     {
         ListNode* slow = left;
         ListNode* fast = left;
         while (fast!=right&&fast->next!=right)
         {
             slow = slow->next;
             fast = fast->next->next;
         }
         return slow;
     }
 };
110、平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:true

方法一:自顶而下

改善方面:每个节点的长度计算需要重复计算其左右子节点的长度

class Solution {
public:
	bool isBalanced(TreeNode* root) {
		if (!root)
		{
			return true;
		}
		return abs(getLength(root->left) - getLength(root->right)) < 2 && isBalanced(root->left) && isBalanced(root->right);

	}
	int getLength(TreeNode* root)
	{
		if (!root)
		{
			return 0;
		}
		return std::max(getLength(root->left), getLength(root->right)) + 1;
	}
};

方法二:自底而上

避免了重复计算

class Solution {
public:
	bool isBalanced(TreeNode* root) {
		return getLength(root) != -1;

	}
	int getLength(TreeNode* root)
	{
		if (!root)
		{
			return 0;
		}
		int left = getLength(root->left);
		if (left == -1) return -1;
		int right = getLength(root->right);
		if (right == -1) return -1;
		return (abs(left - right) < 2) ? std::max(left,right) + 1 : -1;
	}
};
111、二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

**说明:**叶子节点是指没有子节点的节点。

示例一:

img

输入:root = [3,9,20,null,null,15,7]
输出:2

示例二:

输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

方法一:从左右子树中找更小值进行递归

易犯错误:忘记考虑节点只有一个孩子的情况,误把其当作叶子节点。

class Solution {
public:
	int minDepth(TreeNode* root) {
		if (!root)
		{
			return 0;
		}
		if (!root->left && !root->right)
		{
			return  1;
		}
		int mid = INT_MAX;
        //两个if()判断用来应对节点只有左孩子和右孩子的情况
		if (root->left)
		{
			mid = std::min(mid, minDepth(root->left));
		}
		if (root->right)
		{
			mid = std::min(mid, minDepth(root->right));
		}
		return mid+1;
	}
};

方法二:广度优先搜索

第一次访问到的叶节点就是对应的最小深度,这里利用队列存储pair<节点,深度>

class Solution {
public:
    int minDepth(TreeNode *root) {
        if (root == nullptr) {
            return 0;
        }

        queue<pair<TreeNode *, int> > que;
        que.emplace(root, 1);
        while (!que.empty()) {
            TreeNode *node = que.front().first;
            int depth = que.front().second;
            que.pop();
            if (node->left == nullptr && node->right == nullptr) {
                return depth;
            }
            if (node->left != nullptr) {
                que.emplace(node->left, depth + 1);
            }
            if (node->right != nullptr) {
                que.emplace(node->right, depth + 1);
            }
        }

        return 0;
    }
};

112、路径总和1

方法一:

递归,深度优先搜索

class Solution {
public:
	  bool hasPathSum(TreeNode* root, int sum) {
          if(!root)
          {
              return false;
          }
          if (!root->left && !root->right&&root->val == sum)
		  {
			  return true;
		  }
          return hasPathSum(root->left, sum - root->val)||hasPathSum(root->right, sum - root->val);
		//   bool result = false;
		//   if (root->left)
		//   {
		// 	  result = result || hasPathSum(root->left, sum - root->val);
		//   }
		//   if (root->right)
		//   {
		// 	  result = result || hasPathSum(root->right, sum - root->val);
		//   }
		//   return result;
	  }
};
113、路径总和 2

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 22,

          5
         / \
        4   8
       /   / \
      11  13  4
     /  \    / \
    7    2  5   1

返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

方法一:深度优先搜索

注意:有的时候可以借用共享内存

std::vector<std::vector> result;
std::vector ret;

class Solution {
public:
    std::vector<std::vector<int>> result;
    std::vector<int> ret;
    std::vector <std::vector<int >> pathSum(TreeNode* root, int sum) {
        dfs(root, sum);
        return result;
    }
    void dfs(TreeNode* root, int sum)
    {
        if (!root)
        {
            return ;
        }
        ret.push_back(root->val);
        if (!root->left && !root->right && root->val == sum)
        {
            result.push_back(ret);
        }
        dfs(root->left, sum - root->val);
        dfs(root->right, sum - root->val);
        ret.pop_back();
    }

};

字符串

5、最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

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

示例2:

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

方法一:动态规划

回文子串,若s[i–>j]是回文子串,则s[i+1–>j-1]肯定是回文子串。因此产生一个关系,P(i,j)=P(i+1,j−1)∧(Si==Sj)。其中P(i,j)是i到j为回文子串的概率。

同时考虑边界情况。长度为1的时候,肯定是回文子串;长度为2的时候,如果s[i]==s[j],那么也是回文子串。

注意:在状态转移方程中,我们是从长度较短的字符串向长度较长的字符串进行转移的,因此一定要注意动态规划的循环顺序。

注意2:这里的长度需要注意处理,因为长度如果正常计算,考虑数字越界的情况。

长度为最外层迭代变量。

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n));
        string ans;
        for (int l = 0; l < n; ++l) {
            for (int i = 0; i + l < n; ++i) {
                int j = i + l;
                if (l == 0) {
                    dp[i][j] = 1;
                } else if (l == 1) {
                    dp[i][j] = (s[i] == s[j]);
                } else {
                    dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]);
                }
                if (dp[i][j] && l + 1 > ans.size()) {
                    ans = s.substr(i, l + 1);
                }
            }
        }
        return ans;
    }
};
6、Z字型变化

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1:

输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"

示例 2:

输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
    解释:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

方法一:按行取,主要是找寻其中规律

思路

按照与逐行读取 Z 字形图案相同的顺序访问字符串。

算法

按照(2 * numRows - 2)为一个循环

1、在这个循环中,第一个和第numRows个所在的行只有一个;行 0 中的字符位于索引k(2⋅numRows−2) 处;行 numRows−1 中的字符位于索引 k(2⋅numRows−2)+numRows−1 处;

2、其余行是两个元素,行 i中的字符位于索引 k(2⋅numRows−2)+i 以及 (k+1)(2⋅numRows−2)−i 处。

注意:注释的是待优化的部分(思想相同),未注释是已经优化的。

class Solution {
public:
    std::string convert(std::string s, int numRows) {
        std::string result;
        int len = s.size();
        if (len == 0 || numRows < 1)
        {
            return result;
        }
        if (numRows == 1)
        {
            return s;
        }
        //for (int i = 0; i < numRows; ++i)
        //{
        //    for (int j = 0; j < len; ++j)
        //    {
        //        if (j %(2 * numRows - 2) == i)
        //        {
        //            if(j%(2 * numRows - 2) == 0 || j %(2 * numRows - 2) == numRows-1)
        //            {
        //                result.push_back(s[j]);
        //            }
        //            else
        //            {
        //                result.push_back(s[j]);
        //                if (j - i + 2 * numRows - 2 - i < len)
        //                {
        //                    result.push_back(s[j - i + 2 * numRows - 2 - i]);
        //                }

        //            }
        //            
        //        }
        //    }
        //}
        for (int i = 0; i < numRows; ++i)
        {
            for (int j = 0; j + i < len; j += (2 * numRows - 2))
            {
                result += s[j + i];
                if (i != 0 && i != numRows - 1 && (2 * numRows - 2) - i + j < len)
                {
                    result += s[(2 * numRows - 2) - i + j];
                }
            }
        }
        return result;
    }
};

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值