目录

C优选算法精选100道编程题附有图解和源码

目录

C++优选算法精选100道编程题(附有图解和源码)

C++优选算法精选100道编程题(附有图解和源码)



专题一:双指针

双指针介绍

https://i-blog.csdnimg.cn/direct/7fb368b763d743908b1ae776bb72ac40.png


1. 移动零(easy)


https://i-blog.csdnimg.cn/direct/b7c936ebe9e94ab487d5613cf27c8302.png


https://i-blog.csdnimg.cn/direct/887c82fbe9214d58a68f6b04ae9be36e.png


代码如下(示例):

class Solution
{
public:
    void moveZeroes(vector<int>& nums)
    {
        int cur = 0;
        int dest = -1;
        int n = nums.size();
        while (cur < n)
        {
            if (nums[cur] != 0)
            {
                swap(nums[dest + 1], nums[cur]);
                cur++;
                dest++;
            }
            else
            {
                cur++;
            }
        }
    }
};


// 简便版
class Solution
{
public:
    void moveZeroes(vector<int>& nums)
    {
        for (int cur = 0, dest = -1; cur < nums.size(); cur++)
            if (nums[cur]) // 处理⾮零元素
                swap(nums[++dest], nums[cur]);
    }
};

2. 复写零(easy)


https://i-blog.csdnimg.cn/direct/1ae13fa9bb6d4e87a16b2862dc8ee679.png


这里不能从前向后进行复写,会覆盖一些数字!
https://i-blog.csdnimg.cn/direct/ff851bbc3a5a456fac061b74308f2235.png
https://i-blog.csdnimg.cn/direct/17a951cf64274a1ea1765f90b64816ad.png

https://i-blog.csdnimg.cn/direct/efa96b3e65484452a34c25846c949006.png
https://i-blog.csdnimg.cn/direct/0a85470009324c2d81bb986af4b92304.png
https://i-blog.csdnimg.cn/direct/6b26099217524725b7702079ee76fd76.png
https://i-blog.csdnimg.cn/direct/7d1b572417c8401a8abd2e55b95f85dd.png


代码如下(示例):

class Solution
{
public:
	void duplicateZeros(vector<int>& arr)
	{
		// 1. 先找到最后⼀个数
		int cur = 0, dest = -1, n = arr.size();
		while (cur < n)
		{
			if (arr[cur]) dest++;
			else dest += 2;
			if (dest >= n - 1) break;
			cur++;
		}
		// 2. 处理⼀下边界情况
		if (dest == n)
		{
			arr[n - 1] = 0;
			cur--; dest -= 2;
		}
		// 3. 从后向前完成复写操作
		while (cur >= 0)
		{
			if (arr[cur]) arr[dest--] = arr[cur--];
			else
			{
				arr[dest--] = 0;
				arr[dest--] = 0;
				cur--;
			}
		}
	}
};

3. 快乐数(medium)


https://i-blog.csdnimg.cn/direct/d0b35d9dd7724671a5c6d9c464498659.png
https://i-blog.csdnimg.cn/direct/e128dde4572b447ba5705bb0322fec21.png


https://i-blog.csdnimg.cn/direct/667966427b6941108fa2ee27f5342a2f.png
https://i-blog.csdnimg.cn/direct/6e6f5332d4264fe99000661bd01b901a.png
https://i-blog.csdnimg.cn/direct/46022fda81f84112a8215380a22207ba.png


代码如下(示例):

class Solution
{
public:
 int bitSum(int n) // 返回 n 这个数每⼀位上的平⽅和{
 int sum = 0;
 while(n)
 {
 int t = n % 10;
 sum += t * t;
 n /= 10;
 }
 return sum;
 }
 bool isHappy(int n) 
 {
 int slow = n, fast = bitSum(n);
 while(slow != fast)
 {
 slow = bitSum(slow);
 fast = bitSum(bitSum(fast));
 }
 return slow == 1;
 }
};

4. 盛⽔最多的容器(medium)


https://i-blog.csdnimg.cn/direct/ec2ed1d84e034d0d81e8c11b13ac40fb.png
https://i-blog.csdnimg.cn/direct/d2a6181fb8e1455498ce38a267547621.png


https://i-blog.csdnimg.cn/direct/1fb9e05250794e569dedea5611bf2794.png
https://i-blog.csdnimg.cn/direct/5b7f660e45bc4ae1a239e9c3a75cb108.png
https://i-blog.csdnimg.cn/direct/36bc80397a21434ca909108ffe555195.png


代码如下(示例):

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

5. 有效三⻆形的个数(medium)


https://i-blog.csdnimg.cn/direct/45abeecccb714177afe833fb2c8ebe3f.png


https://i-blog.csdnimg.cn/direct/bc4c360e47484ff09e7423a22cc8b524.png
https://i-blog.csdnimg.cn/direct/12044882c9f04a0d94cdae94ac8e161a.png
https://i-blog.csdnimg.cn/direct/ab9334903b3141938257c498b5b2c40b.png


代码如下(示例):

class Solution
{
	public int triangleNumber(int[] nums)
	{
		// 1. 优化:排序
		Arrays.sort(nums);
		// 2. 利⽤双指针解决问题
		int ret = 0, n = nums.length;
		for (int i = n - 1; i >= 2; i--) // 先固定最⼤的数
		{
			// 利⽤双指针快速统计出符合要求的三元组的个数
			int left = 0, right = i - 1;
			while (left < right)
			{
				if (nums[left] + nums[right] > nums[i])
				{
					ret += right - left;
					right--;
				}
				else
				{
					left++;
				}
			}
		}
		return ret;
	}
}

6. 和为 s 的两个数字(easy)


https://i-blog.csdnimg.cn/direct/5659de5aabe94f7b8a210525ebd12374.png


https://i-blog.csdnimg.cn/direct/0ec6b0eb58304bd2bcd37352a75c4ab7.png
https://i-blog.csdnimg.cn/direct/267d739ab7154e13987bbeba75791db5.png


代码如下(示例):

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target)
    {
        int left = 0, right = price.size() - 1;
        while (left < right)
        {
            int sum = price[left] + price[right];
            if (sum > target) right--;
            else if (sum < target) left++;
            else
            {
                return { price[left] , price[right] };
            }
        }
        return { -1 , -1 };
    }
};

7. 三数之和(medium)


https://i-blog.csdnimg.cn/direct/765c8a7b6b0c4c0fa53aad228b504a45.png


https://i-blog.csdnimg.cn/direct/4b1575d41fbe48fe8e15d23308e9d374.png
https://i-blog.csdnimg.cn/direct/f82a7d1fc9a84ee49b3c01b5cde40bcd.png
https://i-blog.csdnimg.cn/direct/cc412c68c4914990a54c6e2d4e7b0766.png


代码如下(示例):

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums)
    {
        vector<vector<int>> net;
        // 第一步:先排序
        sort(nums.begin(), nums.end());
        // 第二步:先固定一个数 a <= 0
        int i = 0, n = nums.size();
        for (i = 0; i < n;)
        {
            if (nums[i] > 0) break;
            else
            {
                // 区间右边用双指针算法
                int left = i + 1, right = n - 1, ret = -(nums[i]);
                while (left < right)
                {
                    int sum = nums[left] + nums[right];
                    if (sum < ret) left++;
                    else if (sum > ret) right--;
                    else
                    {
                        net.push_back({ nums[i] , nums[left] , nums[right] });
                        left++;  right--;
                        // 去重操作
                        while (left < right && nums[left] == nums[left - 1]) left++;
                        while (left < right && nums[right] == nums[right + 1]) right--;
                    }
                }
                i++;
                while (i < n && nums[i] == nums[i - 1]) i++;
            }
        }
        return net;
    }
};


// 测试代码如下
#include <vector>
using namespace std;

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums)
    {
        vector<vector<int>> net;
        // 第一步:先排序
        sort(nums.begin(), nums.end());
        // 第二步:先固定一个数 a <= 0
        int i = 0, n = nums.size();
        for (i = 0; i < n;)
        {
            if (nums[i] > 0) break;
            else
            {
                // 区间右边用双指针算法
                int left = i + 1, right = n - 1, ret = -(nums[i]);
                while (left < right)
                {
                    int sum = nums[left] + nums[right];
                    if (sum < ret) left++;
                    else if (sum > ret) right--;
                    else
                    {
                        net.push_back({ nums[i] , nums[left] , nums[right] });
                        left++;  right--;
                        // 去重操作
                        while (left < right && nums[left] == nums[left - 1]) left++;
                        while (left < right && nums[right] == nums[right + 1]) right--;
                    }
                }

                i++;
                while (i < n && nums[i] == nums[i - 1]) i++;
            }

        }
        return net;
    }
};


int main()
{
    vector<int> v = { -1,0,1,2,-1,-4 };
    Solution s;
    s.threeSum(v);
    return 0;
}

8. 四数之和(medium)


https://i-blog.csdnimg.cn/direct/672a7078e9454972bbdc1dc996ba7729.png


https://i-blog.csdnimg.cn/direct/a81759c286084408bb40e68a1325bacb.png
https://i-blog.csdnimg.cn/direct/e265f2ea661542d6a2a2b73b0216464e.png

写完三数取中之后再写四数取中就会很简单!!!


代码如下(示例):

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        vector<vector<int>> ret;
        // 先排序
        sort(nums.begin() , nums.end());
        // 再依次固定一个数 a
        int n = nums.size();
        for(int i = 0; i < n; )
        {
            // 三数字取和
            for(int j=i+1;j<n;)
            {
                // 两个数字取和
                int left = j+1 , right = n-1 ;
                long long sum = (long long)target - nums[i] - nums[j];
                while(left < right)
                {
                    int mysum = nums[left] + nums[right];
                    if(mysum < sum) left++;
                    else if(mysum > sum) right--;
                    else
                    {
                        ret.push_back({nums[i],nums[j],nums[left],nums[right]});
                        left++,right--;
                        while(left < right && nums[left] == nums[left-1]) left++;
                        while(left < right && nums[right] == nums[right+1]) right--;
                    }
                }
                j++;
                while(j<n && nums[j]==nums[j-1]) j++;
            }
            i++;
            while(i<n && nums[i] == nums[i-1]) i++;
        }
        return ret;
    }
};

专题二:滑动窗口

滑动窗口介绍

https://i-blog.csdnimg.cn/direct/37e028eaa5754b17a7cb505d8ccd3567.png


9. 长度最小的子数组(medium)


https://i-blog.csdnimg.cn/direct/013ab76301e148b8bedd1007f6d6aa8e.png


https://i-blog.csdnimg.cn/direct/caaa7961b791453b9e8f0e9d27892f54.png
https://i-blog.csdnimg.cn/direct/6f297709c0bd4814803881fbbd715467.png
https://i-blog.csdnimg.cn/direct/763a99ff11ad4ee3af6cb03f5f49ee5c.png


代码如下(示例):

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0, ret = INT_MAX;
        for (int left = 0, right = 0; right < nums.size(); ++right)
        {
            sum += nums[right];
            while (sum >= target)
            {
                ret = min(ret, right - left + 1);
                sum -= nums[left];
                left++;
            }
        }
        return ret == INT_MAX ? 0 : ret;
    }
};

10. 无重复字符的最长子串(medium)


https://i-blog.csdnimg.cn/direct/5cc4dd8109a94a39a5468746c8c60740.png


https://i-blog.csdnimg.cn/direct/79d25695e9f8470ab001f71f7997ed44.png
https://i-blog.csdnimg.cn/direct/4b4f74fc29f24e04ab239c8f828786b5.png
https://i-blog.csdnimg.cn/direct/915b673c3629403197a50f9f8d557e56.png


代码如下(示例):

class Solution {
public:
    int lengthOfLongestSubstring(string s)
    {
        int left = 0, right = 0, n = s.size();
        int ret = 0;
        int hash[128] = { 0 };
        while (right < n)
        {
            hash[s[right]]++;
            while (hash[s[right]] > 1) hash[s[left++]]--;
            ret = max(ret, right - left + 1);
            right++;
        }
        return ret;
    }
};

11. 最⼤连续 1 的个数 III(medium)


https://i-blog.csdnimg.cn/direct/8f93c4fb24544ca4a845b19e232aabed.png
https://i-blog.csdnimg.cn/direct/968c235e470c46898067a3aea52e0ad8.png


https://i-blog.csdnimg.cn/direct/8c07d815a39b41ceabffb9665f8e0e80.png
https://i-blog.csdnimg.cn/direct/ae723dc8d0bd434ea9d9567c538cab9e.png


代码如下(示例):

class Solution {
public:
    int longestOnes(vector<int>& nums, int k)
    {
        int ret = 0;
        int left = 0, right = 0, zero = 0;
        int n = nums.size();
        while (right < n)
        {
            if (nums[right] == 0) zero++; // 进窗口
            while (zero > k)
            {
                if (nums[left++] == 0) zero--; // 出窗口
            }
            ret = max(ret, right - left + 1);// 更新结果
            right++;
        }
        return ret;
    }
};

12. 将 x 减到 0 的最⼩操作数 (medium)


https://i-blog.csdnimg.cn/direct/b13227c283754fe5a3d0adb1881b0cba.png


https://i-blog.csdnimg.cn/direct/6c6ff26a2b234874b3c369c3a7fd0542.png
https://i-blog.csdnimg.cn/direct/8797006f13f0433a879714e24c02772b.png


代码如下(示例):

class Solution
{
public:
	int minOperations(vector<int>& nums, int x)
	{
		int sum = 0;
		for (int a : nums) sum += a;
		int target = sum - x;
		// 细节问题
		if (target < 0) return -1;
		int ret = -1;
		for (int left = 0, right = 0, tmp = 0; right < nums.size(); right++)
		{
			tmp += nums[right]; // 进窗⼝
			while (tmp > target) // 判断
				tmp -= nums[left++]; // 出窗⼝
			if (tmp == target) // 更新结果
				ret = max(ret, right - left + 1);
		}
		if (ret == -1) return ret;
		else return nums.size() - ret;
	}
};

13. ⽔果成篮(medium)


https://i-blog.csdnimg.cn/direct/4da38f30d682496daf3031cb6882efb1.png
https://i-blog.csdnimg.cn/direct/36001199aa5447dbbbcd1b8bc1148af2.png


https://i-blog.csdnimg.cn/direct/b6483161a9614222bdb7171d3b1bc954.png
https://i-blog.csdnimg.cn/direct/003482ff2d4f4e918f5d21ee9a9c9532.png
https://i-blog.csdnimg.cn/direct/d182b8a4497b401593324a5b0fce2939.png
https://i-blog.csdnimg.cn/direct/0c762aec08ed4980807683bf8ec51c6a.png


代码如下(示例):

class Solution {
public:
    int totalFruit(vector<int>& fruits)
    {
        int ret = 0;
        unordered_map<int, int> hash;
        for (int left = 0, right = 0; right < fruits.size(); right++)
        {
            // 进窗口
            hash[fruits[right]]++;
            // 判断+出窗口
            while (hash.size() > 2)
            {
                hash[fruits[left]]--;
                if (hash[fruits[left]] == 0) hash.erase(fruits[left]);
                left++;
            }
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};


// 数组优化版本
class Solution
{
public:
    int totalFruit(vector<int>& f)
    {
        int hash[100001] = { 0 }; // 统计窗⼝内出现了多少种⽔果
        int ret = 0;
        for (int left = 0, right = 0, kinds = 0; right < f.size(); right++)
        {
            if (hash[f[right]] == 0) kinds++; // 维护⽔果的种类
            hash[f[right]]++; // 进窗⼝
            while (kinds > 2) // 判断
            {
                // 出窗⼝
                hash[f[left]]--;
                if (hash[f[left]] == 0) kinds--;
                left++;
            }
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

14. 找到字符串中所有字⺟异位词(medium)


https://i-blog.csdnimg.cn/direct/933e47323a804406bbb07f1fa2557c56.png


https://i-blog.csdnimg.cn/direct/47d65afc9c994e479d97ae6f554f9050.png
https://i-blog.csdnimg.cn/direct/489837257ad544d2b46ada16273c16b3.png
https://i-blog.csdnimg.cn/direct/ee6da6eb76574cd0b7a7a6e8a8ada031.png


https://i-blog.csdnimg.cn/direct/93096dac47f74a39bd9a2b1dffef3ac8.png
https://i-blog.csdnimg.cn/direct/f17944a64a2a4294a400bd629e5050cd.png
https://i-blog.csdnimg.cn/direct/d333c6fdacde43808ec4a7d4305ffe40.png
https://i-blog.csdnimg.cn/direct/331246c0356b454e9b9ea6134c46cad8.png
https://i-blog.csdnimg.cn/direct/7c3252efab404ad480587610f9ff5835.png


代码如下(示例):

class Solution
{
public:
	vector<int> findAnagrams(string s, string p)
	{
		vector<int> ret;
		int hash1[26] = { 0 }; // 统计字符串 p 中每个字符出现的个数
		for (auto ch : p) hash1[ch - 'a']++;
		int hash2[26] = { 0 }; // 统计窗⼝⾥⾯的每⼀个字符出现的个数
		int m = p.size();
		for (int left = 0, right = 0, count = 0; right < s.size(); right++)
		{
			char in = s[right];
			// 进窗⼝ + 维护 count
			hash2[in - 'a']++;
			if (hash2[in - 'a'] <= hash1[in - 'a']) count++;
			if (right - left + 1 > m) // 判断
			{
				char out = s[left++];
				// 出窗⼝ + 维护 count
				if (hash2[out - 'a'] <= hash1[out - 'a']) count--;
				hash2[out - 'a']--;
			}
			// 更新结果
			if (count == m) ret.push_back(left);
		}
		return ret;
	}
};

15. 串联所有单词的⼦串(hard)


https://i-blog.csdnimg.cn/direct/e4e0e7a7d8dc48e7b513dfe3259e13bd.png
https://i-blog.csdnimg.cn/direct/0408429e7fec48e1ade0c7fe561cce65.png


https://i-blog.csdnimg.cn/direct/2900ca27f658428f9ee274ff8af79e1a.png
https://i-blog.csdnimg.cn/direct/f6c0e37a9d3b4c969b2ca48acf8f1938.png
https://i-blog.csdnimg.cn/direct/47e1d442c9254981ac36e240df7d94c0.png
https://i-blog.csdnimg.cn/direct/8f33793f713844b7abff0de8d7b591fa.png


代码如下(示例):

class Solution
{
public:
 vector<int> findSubstring(string s, vector<string>& words) 
 {
 vector<int> ret;
 unordered_map<string, int> hash1; // 保存 words ⾥⾯所有单词的频次
 for(auto& s : words) hash1[s]++;
 int len = words[0].size(), m = words.size();
 for(int i = 0; i < len; i++) // 执⾏ len 次
 {
 unordered_map<string, int> hash2; // 维护窗⼝内单词的频次
 for(int left = i, right = i, count = 0; right + len <= s.size(); 
right += len)
 {
 // 进窗⼝ + 维护 count
 string in = s.substr(right, len);
 hash2[in]++;
 if(hash1.count(in) && hash2[in] <= hash1[in]) count++;
 // 判断
 if(right - left + 1 > len * m)
 {
 // 出窗⼝ + 维护 count
 string out = s.substr(left, len);
 if(hash1.count(out) && hash2[out] <= hash1[out]) count--;
 hash2[out]--;
 left += len;
 }
 // 更新结果
 if(count == m) ret.push_back(left);
 }
 }
 return ret;
 }
};

16. 最⼩覆盖⼦串(hard)


https://i-blog.csdnimg.cn/direct/54281b92b03a4a02afc3e48737cc3eeb.png


https://i-blog.csdnimg.cn/direct/5b13c7b712ce48ff83b7c4445966e409.png
https://i-blog.csdnimg.cn/direct/e2828090024d4bdc9fb484350e69b6fa.png
https://i-blog.csdnimg.cn/direct/573916fd84dc44bdaa6e673ace5b7e0d.png
https://i-blog.csdnimg.cn/direct/bc164ac7bb814211b83176d0c9d8898a.png


代码如下(示例):

class Solution
{
public:
	string minWindow(string s, string t)
	{
		int hash1[128] = { 0 }; // 统计字符串 t 中每⼀个字符的频次
		int kinds = 0; // 统计有效字符有多少种
		for (auto ch : t)
			if (hash1[ch]++ == 0) kinds++;
		int hash2[128] = { 0 }; // 统计窗⼝内每个字符的频次
		int minlen = INT_MAX, begin = -1;
		for (int left = 0, right = 0, count = 0; right < s.size(); right++)
		{
			char in = s[right];
			if (++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count
			while (count == kinds) // 判断条件
			{
				if (right - left + 1 < minlen) // 更新结果
				{
					minlen = right - left + 1;
					begin = left;
				}
				char out = s[left++];
				if (hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count
			}
		}
		if (begin == -1) return "";
		else return s.substr(begin, minlen);
	}
};


专题三:二分查找算法

二分查找介绍

https://i-blog.csdnimg.cn/direct/034212e87dbe43b387c04f91aacbd8f1.png

17. ⼆分查找(easy)


https://i-blog.csdnimg.cn/direct/4d011629112d439b8f0230314acdb5b0.png


https://i-blog.csdnimg.cn/direct/9eb90b1040af474db70b6e6030f3aab3.png
https://i-blog.csdnimg.cn/direct/c329c971843e4148a1159b14f21b53c3.png
https://i-blog.csdnimg.cn/direct/dd888b6c7fc54512873d16438d906838.png

https://i-blog.csdnimg.cn/direct/a476f5d631d245b79de5492dd15cb8ba.png


代码如下(示例):

class Solution {
public:
    int search(vector<int>& nums, int target)
    {
        sort(nums.begin(), nums.end());
        int left = 0, right = nums.size() - 1;
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else if (nums[mid] > target) right = mid - 1;
            else return mid;
        }
        return -1;
    }
};

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


https://i-blog.csdnimg.cn/direct/a8185f619ff34c798fd68d56876f2b91.png


https://i-blog.csdnimg.cn/direct/2b5889b5c8804cf99f9532029f7330ce.png
https://i-blog.csdnimg.cn/direct/4d8749d6aaad4682928ef510c56415dc.png


https://i-blog.csdnimg.cn/direct/3346323fff1e4b5c91205d706c3707bb.png
https://i-blog.csdnimg.cn/direct/60d5b72d3e2e46c2aaab64f515584cb6.png


https://i-blog.csdnimg.cn/direct/f744b811b9fb449685fecabf63975d2c.png


代码如下(示例):

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target)
    {
        int left = 0, right = nums.size() - 1;
        if (nums.size() == 0) return { -1,-1 };
        // 先查左端点
        int begin = 0;
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else  right = mid;
        }
        if (nums[left] != target) return { -1,-1 };
        else begin = left;
        // 再查右端点
        left = 0, right = nums.size() - 1;
        while (left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if (nums[mid] <= target) left = mid;
            else  right = mid - 1;
        }
        return { begin,right };
    }
};

19. 搜索插⼊位置(easy)


https://i-blog.csdnimg.cn/direct/dab32e19362d4a61beab3bf2b506e05c.png
https://i-blog.csdnimg.cn/direct/023fb4e5b269472faef24fea78bd805a.png
https://i-blog.csdnimg.cn/direct/d0f2655ee0f645f89d0823f631897834.png


代码如下(示例):

class Solution {
public:
    int searchInsert(vector<int>& nums, int target)
    {
        int left = 0, right = nums.size() - 1;
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else right = mid;
        }
        if (nums[left] < target) return left + 1;
        return left;
    }
};

20. x 的平⽅根(easy)


https://i-blog.csdnimg.cn/direct/0769793525594252a131746515821059.png


https://i-blog.csdnimg.cn/direct/05b34e863a13417682ab715b4cee70a1.png
https://i-blog.csdnimg.cn/direct/c207ea21d08c4993b2b06c7ee0bb3e4b.png
https://i-blog.csdnimg.cn/direct/ddd8d0ac5c36464e992391b06e1b3ac8.png


代码如下(示例):

// 方法一:暴力枚举
class Solution {
public:
	int mySqrt(int x) {
		// 由于两个较⼤的数相乘可能会超过 int 最⼤范围
		// 因此⽤ long long
		long long i = 0;
		for (i = 0; i <= x; i++)
		{
			// 如果两个数相乘正好等于 x,直接返回 i
			if (i * i == x) return i;
			// 如果第⼀次出现两个数相乘⼤于 x,说明结果是前⼀个数
			if (i * i > x) return i - 1;
		}
		// 为了处理oj题需要控制所有路径都有返回值
		return -1;
	}
};



// 方法二:二分
class Solution
{
public:
	int mySqrt(int x)
	{
		if (x < 1) return 0; // 处理边界情况
		int left = 1, right = x;
		while (left < right)
		{
			long long mid = left + (right - left + 1) / 2; // 防溢出
			if (mid * mid <= x) left = mid;
			else right = mid - 1;
		}
		return left;
	}
};

21. 山峰数组的峰顶(easy)


https://i-blog.csdnimg.cn/direct/b0b1f30f023846608b9b8ecb9e552058.png


https://i-blog.csdnimg.cn/direct/0f0aeb6469c447aaa5e6889db839313f.png
https://i-blog.csdnimg.cn/direct/19a868f7a2554a6aa298b54c9f8c3f5b.png

https://i-blog.csdnimg.cn/direct/12acd20bcb224da1932608361a294caa.png


代码如下(示例):

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr)
    {
        // 第一个和最后一个一定不是山峰
        int left = 1, right = arr.size() - 2;
        while (left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if (arr[mid] < arr[mid - 1]) right = mid - 1;
            else left = mid;
        }
        return left;
    }
};

22. 寻找峰值(medium)


https://i-blog.csdnimg.cn/direct/58617aa36e104f97b3ea0c3f3bd29e07.png


https://i-blog.csdnimg.cn/direct/ea47414c6c83456d82d66756117198ed.png
https://i-blog.csdnimg.cn/direct/ebb5c91f7da5479d8ff52aaf41bb107a.png
https://i-blog.csdnimg.cn/direct/ae95ba5cdb314b17bc343cb6db389a6b.png
https://i-blog.csdnimg.cn/direct/1c39ed523c2b4282b955d63ea5c7fa1a.png


代码如下(示例):

class Solution {
public:
    int findPeakElement(vector<int>& nums)
    {
        int left = 0, right = nums.size() - 1;
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[mid + 1]) right = mid;
            else left = mid + 1;
        }
        return left;
    }
};

23. 搜索旋转排序数组中的最⼩值(medium)


https://i-blog.csdnimg.cn/direct/182bb06964c74dd6ae1cdabd3a5da2ce.png
https://i-blog.csdnimg.cn/direct/79b28ea8e40f4acaa6af14e70ba520ca.png


https://i-blog.csdnimg.cn/direct/b9d4e6781f704408a6fe69d06464e36e.png
https://i-blog.csdnimg.cn/direct/41b05e9d50e846fdb44e35a92dc186c7.png

https://i-blog.csdnimg.cn/direct/e6ee551fdbfd4aa79746c2a1639a205b.png


https://i-blog.csdnimg.cn/direct/b09cded330164fc78ef7ba043e5a6eea.png


代码如下(示例):

class Solution {
public:
    int findMin(vector<int>& nums)
    {
        int left = 0, right = nums.size() - 1;
        int x = nums[right];
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < x) right = mid;
            else left = mid + 1;
        }
        return nums[left];
    }
};

24. 0〜n-1 中缺失的数字(easy)


https://i-blog.csdnimg.cn/direct/50c0867e43654812a95b7f3c244e7617.png


https://i-blog.csdnimg.cn/direct/f54f1adb48fd47e78b2440ecd2443d00.png
https://i-blog.csdnimg.cn/direct/70061bd7fe904de29deeb3b67fa5ae22.png
https://i-blog.csdnimg.cn/direct/7f64387b9f1742f9bfb0b5dc87e0dabd.png


代码如下(示例):

class Solution {
public:
    int takeAttendance(vector<int>& records) 
    {
        int left = 0, right = records.size()-1;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            if(records[mid] == mid) left = mid + 1;
            else right = mid;
        }
        return records[left] == left ? left+1 : left;
    }
};

专题四:前缀和

前缀和介绍

https://i-blog.csdnimg.cn/direct/66cb7afce0344c0384c572585f2c9ef2.png


25. 【模板】⼀维前缀和(easy)


https://i-blog.csdnimg.cn/direct/2eac9baf3cf24c5f87433b655fe0663d.png


https://i-blog.csdnimg.cn/direct/e84de561a25c4d2985ea70cf70e1505e.png
https://i-blog.csdnimg.cn/direct/f5e26eafee4741ceb90cb2ed4d683635.png
https://i-blog.csdnimg.cn/direct/67958eacdd934290b6127f41b440e6cc.png
https://i-blog.csdnimg.cn/direct/afca3ea0564d47bd8900175ad62de361.png


代码如下(示例):

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n, q;
    cin >> n >> q;
    // 1. 读入数据
    vector<int> arr(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> arr[i];
    }
    // 2.预处理出来一个前缀和数组
    vector<long long> dp(n + 1);
    for (int i = 0; i <= n; i++) dp[i] = dp[i - 1] + arr[i];

    // 3.使用前缀和数组
    int left = 0, right = 0;
    while (q--)
    {
        cin >> left >> right;
        cout << dp[right] - dp[left - 1] << endl;
    }
    return 0;
}

26. 【模板】⼆维前缀和(medium)


https://i-blog.csdnimg.cn/direct/d96bc5e0d59c4296be4cb8985ab87f78.png
https://i-blog.csdnimg.cn/direct/e9689964a7a8437f8f498f538b72946a.png


https://i-blog.csdnimg.cn/direct/e49bf959c7d04a5f9e150eb447130d7a.png

https://i-blog.csdnimg.cn/direct/2774d3717b12425f8b981a91ff2054ef.png

https://i-blog.csdnimg.cn/direct/43fc16d4741647a0a9fe39973fdd4dca.png


代码如下(示例):

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    // 1. 读入数据
    int n = 0, m = 0, q = 0;
    cin >> n >> m >> q;
    vector<vector<int>> arr(n + 1, vector<int>(m + 1));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> arr[i][j];
    // 2. 预处理前缀和矩阵
    vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1];
    // 3. 使用前缀和矩阵
    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    while (q--)
    {
        cin >> x1 >> y1 >> x2 >> y2;
        cout << dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1] << endl;
    }
}

27. 寻找数组的中心下标(easy)


https://i-blog.csdnimg.cn/direct/f8a8222640a941f788809163ee13cba0.png


https://i-blog.csdnimg.cn/direct/3e3f21595f944ae4bc9ff507efb73cc8.png
https://i-blog.csdnimg.cn/direct/867f9d3d96df487d886378b8c6d8e559.png
https://i-blog.csdnimg.cn/direct/c20a12df1dcf40e9a650de0016c175ad.png


https://i-blog.csdnimg.cn/direct/7b9494ec37b34d35b72b1cbd14e6b12c.png


代码如下(示例):

class Solution {
public:
    int pivotIndex(vector<int>& nums)
    {
        int n = nums.size();
        vector<int> f(n), g(n);
        // 1.预处理前缀和两个数组
        for (int i = 1; i < n; i++)
            f[i] = f[i - 1] + nums[i - 1];
        for (int i = n - 2; i >= 0; i--)
            g[i] = g[i + 1] + nums[i + 1];
        // 2.判断两个数组是否相同
        for (int i = 0; i < n; i++)
            if (f[i] == g[i])
                return i;
        return -1;
    }
};

28. 除⾃⾝以外数组的乘积(medium)


https://i-blog.csdnimg.cn/direct/e20c997876bd40b9a405ff6be93d9fa0.png


https://i-blog.csdnimg.cn/direct/002a916450bf4cd7ae9813e5c9f98728.png
https://i-blog.csdnimg.cn/direct/6cbfefe5882c4e0bb54bb14edc192146.png


代码如下(示例):

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums)
    {
        int n = nums.size();
        vector<int> f(n), g(n);
        // 1.先预处理一下前缀积两个数组
        f[0] = g[n - 1] = 1;
        for (int i = 1; i < n; i++)
            f[i] = f[i - 1] * nums[i - 1];
        for (int i = n - 2; i >= 0; i--)
            g[i] = g[i + 1] * nums[i + 1];
        // 2.判断并且使用
        vector<int> ret(n);
        for (int i = 0; i < n; i++)
        {
            ret[i] = f[i] * g[i];
        }
        return ret;
    }
};

29. 和为 k 的子数组(medium)


https://i-blog.csdnimg.cn/direct/12594f96f241408aa0a6366c17bf5ce1.png


https://i-blog.csdnimg.cn/direct/e31818e49ca04951a72364b60353980a.png
https://i-blog.csdnimg.cn/direct/69a394b8832b4b42b93898a8c0e5d6e6.png
https://i-blog.csdnimg.cn/direct/47d34709690b433cbf5b7c648472517d.png


代码如下(示例):

class Solution {
public:
    int subarraySum(vector<int>& nums, int k)
    {
        unordered_map<int, int> hash;
        hash[0] = 1;
        int sum = 0, ret = 0;
        for (auto x : nums)
        {
            sum += x;
            if (hash.count(sum - k))
                ret += hash[sum - k]; // 统计个数
            hash[sum]++;
        }
        return ret;
    }
};

30. 和可被 K 整除的⼦数组(medium)


https://i-blog.csdnimg.cn/direct/8939e33a2e184fa4b82c4603512908d3.png


https://i-blog.csdnimg.cn/direct/ac67c60762e14c85ba04501173f478e7.png
https://i-blog.csdnimg.cn/direct/c38a85db46484dafb50a9bccb65d7230.png
https://i-blog.csdnimg.cn/direct/bb5157b2b1db44ac81893e6c36473656.png
https://i-blog.csdnimg.cn/direct/739d6a45bcbd46c4ad15094c8e786522.png


代码如下(示例):

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) 
    {
        unordered_map<int, int> hash;
        hash[0 % k] = 1; // 0 这个数的余数
        int sum = 0, ret = 0;
        for (auto x : nums) 
        {
            sum += x;                  // 算出当前位置的前缀和
            int r = (sum % k + k) % k; // 修正后的余数
            if (hash.count(r))
                ret += hash[r]; // 统计结果
            hash[r]++;
        }
        return ret;
    }
};

31. 连续数组(medium)


https://i-blog.csdnimg.cn/direct/2d83b497fb8241b59b3d9eae520db064.png


https://i-blog.csdnimg.cn/direct/c18d8378116f4fd78d9da13047f4c6e2.png
https://i-blog.csdnimg.cn/direct/1f3323f05c3a4023aa8cfdeef7b800dc.png
https://i-blog.csdnimg.cn/direct/e60d8ec7b7b64c57926909389857d142.png


代码如下(示例):

class Solution
{
public:
    int findMaxLength(vector<int>& nums)
    {
        unordered_map<int, int> hash;
        hash[0] = -1; // 默认有⼀个前缀和为 0 的情况
        int sum = 0, ret = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            sum += nums[i] == 0 ? -1 : 1; // 计算当前位置的前缀和
            if (hash.count(sum)) ret = max(ret, i - hash[sum]);
            else hash[sum] = i;
        }
        return ret;
    }
};

32. 矩阵区域和(medium)


https://i-blog.csdnimg.cn/direct/e30c851d7e044ff098b40abbdf5178e9.png


https://i-blog.csdnimg.cn/direct/9c8fa692fb4c4f8e954414f30fd617ab.png
https://i-blog.csdnimg.cn/direct/a38cb03da00a4528a83eee20d0096368.png
https://i-blog.csdnimg.cn/direct/d3385d69989e49aeb5aebb7d3f97c7ce.png


代码如下(示例):

class Solution {
public:
	vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
		int m = mat.size(), n = mat[0].size();
		vector<vector<int>> dp(m + 1, vector<int>(n + 1));
		// 1. 预处理前缀和矩阵
		for (int i = 1; i <= m; i++)
			for (int j = 1; j <= n; j++)
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] +
				mat[i - 1][j - 1];

		// 2. 使⽤
		vector<vector<int>> ret(m, vector<int>(n));
		for (int i = 0; i < m; i++)
			for (int j = 0; j < n; j++)
			{
				int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;
				int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;
				ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] +
					dp[x1 - 1][y1 - 1];
			}

		return ret;
	}
};

专题五:位运算

位运算介绍


https://i-blog.csdnimg.cn/direct/bf00085b07ec4b44937e07db582d010b.png
https://i-blog.csdnimg.cn/direct/7ead9154263f45b9b5f4898f9e4cb97e.png
https://i-blog.csdnimg.cn/direct/9193a06e6a7846568a81927ec7114080.png
https://i-blog.csdnimg.cn/direct/5387dc113100466a8dd946e256ca4e86.png


33. 判断字符是否唯⼀(easy)


https://i-blog.csdnimg.cn/direct/dda3e4fbef1e4a2084d2ad691fd2e0b4.png

https://i-blog.csdnimg.cn/direct/2e1a7312685c49ad871d3d5a2b0827ce.png

https://i-blog.csdnimg.cn/direct/0e6db25aca204d8fb662ca8b64034169.png


代码如下(示例):

class Solution {
public:
    bool isUnique(string astr) {
        // 鹊巢原理优化
        if (astr.size() > 26)
            return false;
        // 利用位图的思想求解
        int bitMap = 0;
        for (auto ch : astr)
        {
            int i = ch - 'a';
            // 先判断字符是否已经出现过
            if (((bitMap >> i) & 1) == 1)
                return false;
            // 把当前字符加⼊到位图中
            bitMap |= 1 << i;
        }
        return true;
    }
};

34. 丢失的数字(easy)


https://i-blog.csdnimg.cn/direct/414d37037e57476188b891cd713609ee.png


https://i-blog.csdnimg.cn/direct/09753300da734605861b77f585b11cdd.png
https://i-blog.csdnimg.cn/direct/73aa8003825545e0bffe626bc0f77775.png
https://i-blog.csdnimg.cn/direct/86ddcf5234b8426da142e3900abc7869.png

https://i-blog.csdnimg.cn/direct/fb3742e3952b4f34a1ed43c9d72c000b.png


代码如下(示例):

class Solution
{
public:
	int missingNumber(vector<int>& nums)
	{
		int ret = 0;
		for (auto x : nums) ret ^= x;
		for (int i = 0; i <= nums.size(); i++) ret ^= i;
		return ret;
	}
};

35. 两整数之和(medium)


https://i-blog.csdnimg.cn/direct/147d3fc836134755a5c1773f4f6820ca.png


https://i-blog.csdnimg.cn/direct/88e06510e46b41aeba286f02c475e3ae.png
https://i-blog.csdnimg.cn/direct/b1e6d11b85144970844efa5bff8a2796.png


代码如下(示例):

class Solution
{
public:
	int getSum(int a, int b)
	{
		while (b != 0)
		{
			int x = a ^ b; // 先算出⽆进位相加的结果
			unsigned int carry = (unsigned int)(a & b) << 1; // 算出进位
			a = x;
			b = carry;
		}
		return a;
	}
};

36. 只出现⼀次的数字 II(medium)


https://i-blog.csdnimg.cn/direct/1ecf5c5cb3ad427dbb64b40074c18c48.png


https://i-blog.csdnimg.cn/direct/2009520d52bd46c5828991716ad5a086.png
https://i-blog.csdnimg.cn/direct/1da85df4955e4622b7494700586d8420.png


代码如下(示例):

class Solution
{
public:
	int singleNumber(vector<int>& nums)
	{
		int ret = 0;
		for (int i = 0; i < 32; i++) // 依次去修改 ret 中的每⼀位
		{
			int sum = 0;
			for (int x : nums) // 计算nums中所有的数的第 i 位的和
				if (((x >> i) & 1) == 1)
					sum++;
			sum %= 3;
			if (sum == 1) ret |= 1 << i;
		}
		return ret;
	}
};

37. 消失的两个数字(hard)


https://i-blog.csdnimg.cn/direct/9511b3459d2345b5a4d73868cc0c0d6d.png


https://i-blog.csdnimg.cn/direct/f5994ac2c37a4f26b80a7f7cff2d6a9b.png
https://i-blog.csdnimg.cn/direct/5a8c91bbcec54045af41ad899a5e1378.png


代码如下(示例):


class Solution
{
public:
	vector<int> missingTwo(vector<int>& nums)
	{
		// 1. 将所有的数异或在⼀起
		int tmp = 0;
		for (auto x : nums) tmp ^= x;
		for (int i = 1; i <= nums.size() + 2; i++) tmp ^= i;
		// 2. 找出 a,b 中⽐特位不同的那⼀位
		int diff = 0;
		while (1)
		{
			if (((tmp >> diff) & 1) == 1) break;
			else diff++;
		}
		// 3. 根据 diff 位的不同,将所有的数划分为两类来异或
		int a = 0, b = 0;
		for (int x : nums)
			if (((x >> diff) & 1) == 1) b ^= x;
			else a ^= x;
		for (int i = 1; i <= nums.size() + 2; i++)
			if (((i >> diff) & 1) == 1) b ^= i;
			else a ^= i;
		return { a, b };
	}
};

专题六:模拟

模拟介绍

https://i-blog.csdnimg.cn/direct/58b292367f494703bc8576697f8896c8.png


38. 替换所有的问号(easy)


https://i-blog.csdnimg.cn/direct/c13c841dcfa9410dbab417c9218bfe72.png


https://i-blog.csdnimg.cn/direct/d397660d75b24ece8df00238cca5b4e6.png
https://i-blog.csdnimg.cn/direct/18a9fcf566834a3ca1e93d755db7a85a.png


代码如下(示例):

class Solution
{
public:
	string modifyString(string s)
	{
		int n = s.size();
		for (int i = 0; i < n; i++)
		{
			if (s[i] == '?') // 替换
			{
				for (char ch = 'a'; ch <= 'z'; ch++)
				{
					if ((i == 0 || ch != s[i - 1]) && (i == n - 1 || ch != s[i+ 1]))
					{
						s[i] = ch;
						break;
					}
				}
			}
		}
		return s;
	}
};

39. 提莫攻击(easy)


https://i-blog.csdnimg.cn/direct/531ccbfd81864463bc55a27ca7287b59.png


https://i-blog.csdnimg.cn/direct/0c000f898f1542308ad32614ac49c7f2.png
https://i-blog.csdnimg.cn/direct/7b36d2a1f92e4996b52c2ac825f485aa.png


代码如下(示例):

class Solution {
public:
    int findPoisonedDuration(vector<int>& timeSeries, int duration)
    {
        int ret = 0;
        for (int i = 1; i < timeSeries.size(); i++)
        {
            int x = timeSeries[i] - timeSeries[i - 1];
            if (x >= duration) ret += duration;
            else ret += x;
        }
        return ret + duration;
    }
};

40. N 字形变换(medium)


https://i-blog.csdnimg.cn/direct/441789f874af4dcbb1eb77fa95e0f54f.png


https://i-blog.csdnimg.cn/direct/62b84bc0414941bea00f9edbcab4a772.png
https://i-blog.csdnimg.cn/direct/e92165dc9d024eac8c3430b807e3fc6b.png


代码如下(示例):

class Solution
{
public:
	string convert(string s, int numRows)
	{
		// 处理边界情况
		if (numRows == 1) return s;
		string ret;
		int d = 2 * numRows - 2, n = s.size();
		// 1. 先处理第⼀⾏
		for (int i = 0; i < n; i += d)
			ret += s[i];
		// 2. 处理中间⾏
		for (int k = 1; k < numRows - 1; k++) // 枚举每⼀⾏
		{
			for (int i = k, j = d - k; i < n || j < n; i += d, j += d)
			{
				if (i < n) ret += s[i];
				if (j < n) ret += s[j];
			}
		}
		// 3. 处理最后⼀⾏
		for (int i = numRows - 1; i < n; i += d)
			ret += s[i];
		return ret;
	}
};

41. 外观数列 (medium)


https://i-blog.csdnimg.cn/direct/6f6a9d4fa9a34c12b63a94f81a5e78c5.png


https://i-blog.csdnimg.cn/direct/beef19fc9a5940229c6024def87f3cd1.png
https://i-blog.csdnimg.cn/direct/43211818f6af41ceb0672753f881ac44.png


代码如下(示例):

class Solution
{
public:
	string countAndSay(int n)
	{
		string ret = "1";
		for (int i = 1; i < n; i++) // 解释 n - 1 次 ret 即可
		{
			string tmp;
			int len = ret.size();
			for (int left = 0, right = 0; right < len; )
			{
				while (right < len && ret[left] == ret[right]) right++;
				tmp += to_string(right - left) + ret[left];
				left = right;
			}
			ret = tmp;
		}
		return ret;
	}
};

42. 数⻘蛙(medium)


https://i-blog.csdnimg.cn/direct/89cf3cbd07554c0dac64ee8c8fec5b0d.png


https://i-blog.csdnimg.cn/direct/48aafca155ec4a6b9047d213f6770eee.png


https://i-blog.csdnimg.cn/direct/e44ca89127bd4ef8a22ed10010bb28eb.png
https://i-blog.csdnimg.cn/direct/03dd37fda11449878dd4372fdbaca385.png


https://i-blog.csdnimg.cn/direct/826a29f036084080a30d0fe74ac714a1.png


代码如下(示例):

class Solution
{
public:
	int minNumberOfFrogs(string croakOfFrogs)
	{
		string t = "croak";
		int n = t.size();
		vector<int> hash(n); // ⽤数组来模拟哈希表
		unordered_map<char, int> index; //[x, x这个字符对应的下标]
		for (int i = 0; i < n; i++)
			index[t[i]] = i;

		for (auto ch : croakOfFrogs)
		{
			if (ch == 'c')
			{
				if (hash[n - 1] != 0) hash[n - 1]--;
				hash[0]++;
			}
			else
			{
				int i = index[ch];
				if (hash[i - 1] == 0) return -1;
				hash[i - 1]--; hash[i]++;
			}
		}
		for (int i = 0; i < n - 1; i++)
			if (hash[i] != 0)
				return -1;
		return hash[n - 1];
	}
};

专题七:分治快排

分治快排介绍

https://i-blog.csdnimg.cn/direct/4c8d9571cc8d404bbe61206dfca5cf37.png


43. 颜色分类(medium)


https://i-blog.csdnimg.cn/direct/ce410000061b4f26bc48c46114ba58d5.png


https://i-blog.csdnimg.cn/direct/8c720e8ff27c463dbbe30e0c70ca9ada.png
https://i-blog.csdnimg.cn/direct/2ae29475f560428eaddb2bcc4a10626a.png
https://i-blog.csdnimg.cn/direct/b16adf016b1e42a888915432b897d8b7.png


代码如下(示例):

class Solution {
public:
    void sortColors(vector<int>& nums)
    {
        int left = -1, right = nums.size(), i = 0;
        while (i < right)
        {
            if (nums[i] == 0) swap(nums[++left], nums[i++]);
            else if (nums[i] == 2) swap(nums[--right], nums[i]);
            else i++;
        }
    }
};

44. 快速排序(medium)


https://i-blog.csdnimg.cn/direct/5ab3cf16d7034777a33504955e2e71e4.png


https://i-blog.csdnimg.cn/direct/775777fcad2745d4bfaf0289d5481637.png
https://i-blog.csdnimg.cn/direct/125fc4fd8d794d4d8ea619cd3050ff34.png


代码如下(示例):

class Solution
{
public:
	vector<int> sortArray(vector<int>& nums)
	{
		srand(time(NULL)); // 种下⼀个随机数种⼦
		qsort(nums, 0, nums.size() - 1);
		return nums;
	}
	// 快排
	void qsort(vector<int>& nums, int l, int r)
	{
		if (l >= r) return;
		// 数组分三块
		int key = getRandom(nums, l, r);
		int i = l, left = l - 1, right = r + 1;
		while (i < right)
		{
			if (nums[i] < key) swap(nums[++left], nums[i++]);
			else if (nums[i] == key) i++;
			else swap(nums[--right], nums[i]);
		}
		// [l, left] [left + 1, right - 1] [right, r]
		qsort(nums, l, left);
		qsort(nums, right, r);
	}
	int getRandom(vector<int>& nums, int left, int right)
	{
		int r = rand();
		return nums[r % (right - left + 1) + left];
	}
};

45. 快速选择算法(medium)


https://i-blog.csdnimg.cn/direct/bb798d097d9d417dbdd68a1fccb2778b.png


https://i-blog.csdnimg.cn/direct/d55d17267d5d41e2983dff242465e5f1.png
https://i-blog.csdnimg.cn/direct/9a485d1db33a479d9662b546927a3775.png


代码如下(示例):

class Solution
{
public:
	int findKthLargest(vector<int>& nums, int k)
	{
		srand(time(NULL));
		return qsort(nums, 0, nums.size() - 1, k);
	}
	int qsort(vector<int>& nums, int l, int r, int k)
	{
		if (l == r) return nums[l];
		// 1. 随机选择基准元素
		int key = getRandom(nums, l, r);
		// 2. 根据基准元素将数组分三块
		int left = l - 1, right = r + 1, i = l;
		while (i < right)
		{
			if (nums[i] < key) swap(nums[++left], nums[i++]);
			else if (nums[i] == key) i++;
			else swap(nums[--right], nums[i]);
		}
		// 3. 分情况讨论
		int c = r - right + 1, b = right - left - 1;
		if (c >= k) return qsort(nums, right, r, k);
		else if (b + c >= k) return key;
		else return qsort(nums, l, left, k - b - c);
	}
	int getRandom(vector<int>& nums, int left, int right)
	{
		return nums[rand() % (right - left + 1) + left];
	}
}; 

46. 最⼩的 k 个数(medium)


https://i-blog.csdnimg.cn/direct/3b447761249443818cff6430f7a9fa17.png


https://i-blog.csdnimg.cn/direct/70d6ce0f407b44a1858426e663f763dc.png

https://i-blog.csdnimg.cn/direct/b18cf340f32f4aeba4f25956ae0c0550.png


代码如下(示例):

class Solution {
public:
	vector<int> getLeastNumbers(vector<int>& nums, int k)
	{
		srand(time(NULL));
		qsort(nums, 0, nums.size() - 1, k);
		return { nums.begin(), nums.begin() + k };
	}
	void qsort(vector<int>& nums, int l, int r, int k)
	{
		if (l >= r) return;
		// 1. 随机选择⼀个基准元素 + 数组分三块
		int key = getRandom(nums, l, r);
		int left = l - 1, right = r + 1, i = l;
		while (i < right)
		{
			if (nums[i] < key) swap(nums[++left], nums[i++]);
			else if (nums[i] == key) i++;
			else swap(nums[--right], nums[i]);
		}
		// [l, left][left + 1, right - 1] [right, r]
		// 2. 分情况讨论
		int a = left - l + 1, b = right - left - 1;
		if (a > k) qsort(nums, l, left, k);
		else if (a + b >= k) return;
		else qsort(nums, right, r, k - a - b);
	}
	int getRandom(vector<int>& nums, int l, int r)
	{
		return nums[rand() % (r - l + 1) + l];
	}
};

专题八:分治归并

分治归并介绍

https://i-blog.csdnimg.cn/direct/24c49f0617aa443b8f66ac4ddfe08791.png


47. 归并排序(medium)


https://i-blog.csdnimg.cn/direct/d7c4a4e4dd264bbd82800557937b4340.png


https://i-blog.csdnimg.cn/direct/ca877455da0c46ee912b37acc767987a.png
https://i-blog.csdnimg.cn/direct/8b47a1de6f8349b0b7f43930a4d6631e.png


代码如下(示例):

class Solution
{
	vector<int> tmp;
public:
	vector<int> sortArray(vector<int>& nums)
	{
		tmp.resize(nums.size());
		mergeSort(nums, 0, nums.size() - 1);
		return nums;
	}
	void mergeSort(vector<int>& nums, int left, int right)
	{
		if (left >= right) return;
		// 1. 选择中间点划分区间
		int mid = (left + right) >> 1;
		// [left, mid] [mid + 1, right]
		// 2. 把左右区间排序
		mergeSort(nums, left, mid);
		mergeSort(nums, mid + 1, right);
		// 3. 合并两个有序数组
		int cur1 = left, cur2 = mid + 1, i = 0;
		while (cur1 <= mid && cur2 <= right)
			tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] :
			nums[cur2++];
		// 处理没有遍历完的数组
		while (cur1 <= mid) tmp[i++] = nums[cur1++];
		while (cur2 <= right) tmp[i++] = nums[cur2++];
		// 4. 还原
		for (int i = left; i <= right; i++)
			nums[i] = tmp[i - left];
	}
};

48. 数组中的逆序对(hard)


https://i-blog.csdnimg.cn/direct/e8ac581e8775438fbdee1f49c8be72b6.png


https://i-blog.csdnimg.cn/direct/e1d8a2b20c5e42c8a74180059fe58641.png
https://i-blog.csdnimg.cn/direct/b105ba1fe5a948578a8416b0bb661d9f.png
https://i-blog.csdnimg.cn/direct/652f91d0323b4bdf935f1c2da560638e.png


代码如下(示例):

class Solution
{
	int tmp[50010];
public:
	int reversePairs(vector<int>& nums)
	{
		return mergeSort(nums, 0, nums.size() - 1);
	}
	int mergeSort(vector<int>& nums, int left, int right)
	{
		if (left >= right) return 0;
		int ret = 0;
		// 1. 找中间点,将数组分成两部分
		int mid = (left + right) >> 1;
		// [left, mid][mid + 1, right]
		// 2. 左边的个数 + 排序 + 右边的个数 + 排序
		ret += mergeSort(nums, left, mid);
		ret += mergeSort(nums, mid + 1, right);
		// 3. ⼀左⼀右的个数
		int cur1 = left, cur2 = mid + 1, i = 0;
		while (cur1 <= mid && cur2 <= right) // 升序的时候
		{
			if (nums[cur1] <= nums[cur2])
			{
				tmp[i++] = nums[cur1++];
			}
			else
			{
				ret += mid - cur1 + 1;
				tmp[i++] = nums[cur2++];
			}
		}
		// 4. 处理⼀下排序
		while (cur1 <= mid) tmp[i++] = nums[cur1++];
		while (cur2 <= right) tmp[i++] = nums[cur2++];
		for (int j = left; j <= right; j++)
			nums[j] = tmp[j - left];
		return ret;
	}
};

49. 计算右侧⼩于当前元素的个数(hard)


https://i-blog.csdnimg.cn/direct/f5438f3e17ba40c299cbc1c49c173c24.png


https://i-blog.csdnimg.cn/direct/c60f6c5091e54b8b80fe73c2d7a5ae9f.png
https://i-blog.csdnimg.cn/direct/90af571b49d246ea84780ac43db54e5c.png
https://i-blog.csdnimg.cn/direct/199ebac97e0547d6adafc8d9ef9422fa.png
https://i-blog.csdnimg.cn/direct/9c18c2ae03cc48d0b82353e500933fd3.png


代码如下(示例):

class Solution
{
	vector<int> ret;
	vector<int> index; // 记录 nums 中当前元素的原始下标
	int tmpNums[500010];
	int tmpIndex[500010];
public:
	vector<int> countSmaller(vector<int>& nums)
	{
		int n = nums.size();
		ret.resize(n);
		index.resize(n);
		// 初始化⼀下 index 数组
		for (int i = 0; i < n; i++)
			index[i] = i;
		mergeSort(nums, 0, n - 1);
		return ret;
	}

	void mergeSort(vector<int>& nums, int left, int right)
	{
		if (left >= right) return;
		// 1. 根据中间元素,划分区间
		int mid = (left + right) >> 1;
		// [left, mid] [mid + 1, right]
		// 2. 先处理左右两部分
		mergeSort(nums, left, mid);
		mergeSort(nums, mid + 1, right);
		// 3. 处理⼀左⼀右的情况
		int cur1 = left, cur2 = mid + 1, i = 0;
		while (cur1 <= mid && cur2 <= right) // 降序
		{
			if (nums[cur1] <= nums[cur2])
			{
				tmpNums[i] = nums[cur2];
				tmpIndex[i++] = index[cur2++];
			}
			else
			{
				ret[index[cur1]] += right - cur2 + 1; // 重点
				tmpNums[i] = nums[cur1];
				tmpIndex[i++] = index[cur1++];
			}
		}
		// 4. 处理剩下的排序过程
		while (cur1 <= mid)
		{
			tmpNums[i] = nums[cur1];
			tmpIndex[i++] = index[cur1++];
		}
		while (cur2 <= right)
		{
			tmpNums[i] = nums[cur2];
			tmpIndex[i++] = index[cur2++];
		}
		for (int j = left; j <= right; j++)
		{
			nums[j] = tmpNums[j - left];
			index[j] = tmpIndex[j - left];
		}
	}
};

50. 翻转对(hard)


https://i-blog.csdnimg.cn/direct/dd6eb2a3cfe24962901316c75f3f76f6.png


https://i-blog.csdnimg.cn/direct/b904b0e90ab647da8a3a049a6a817418.png
https://i-blog.csdnimg.cn/direct/042d794922a04b18a351e4a87c6c8169.png

https://i-blog.csdnimg.cn/direct/9f79737ff822407490df1637ab2adac9.png
https://i-blog.csdnimg.cn/direct/ff32180e72be41bc9aa573aaf3abf0b4.png


代码如下(示例):

class Solution
{
	int tmp[50010];
public:
	int reversePairs(vector<int>& nums)
	{
		return mergeSort(nums, 0, nums.size() - 1);
	}
	int mergeSort(vector<int>& nums, int left, int right)
	{
		if (left >= right) return 0;
		int ret = 0;
		// 1. 先根据中间元素划分区间
		int mid = (left + right) >> 1;
		// [left, mid] [mid + 1, right]
		// 2. 先计算左右两侧的翻转对
		ret += mergeSort(nums, left, mid);
		ret += mergeSort(nums, mid + 1, right);
		// 3. 先计算翻转对的数量
		int cur1 = left, cur2 = mid + 1, i = left;
		while (cur1 <= mid) // 降序的情况
		{
			while (cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
			if (cur2 > right)
				break;
			ret += right - cur2 + 1;
			cur1++;
		}
		// 4. 合并两个有序数组
		cur1 = left, cur2 = mid + 1;
		while (cur1 <= mid && cur2 <= right)
			tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];
		while (cur1 <= mid) tmp[i++] = nums[cur1++];
		while (cur2 <= right) tmp[i++] = nums[cur2++];
		for (int j = left; j <= right; j++)
			nums[j] = tmp[j];

		return ret;
	}
};

专题九:链表

链表常用技巧和操作总结

https://i-blog.csdnimg.cn/direct/706ab133cf164ba3b6a7ec3aac149168.png


51. 两数相加(medium)


https://i-blog.csdnimg.cn/direct/db8553f3c5914fadab55ba8e7ff787b3.png


https://i-blog.csdnimg.cn/direct/114f16e27378427699e2269a61ffe81f.png
https://i-blog.csdnimg.cn/direct/df9030dae9984a60a6ed8e9b4e78b861.png


代码如下(示例):

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
    {
        ListNode* cur1 = l1, * cur2 = l2;
        ListNode* newhead = new ListNode(0);//创建虚拟头节点,记录最终结果
        ListNode* prev = newhead;// 尾指针
        int t = 0;// 进位
        while (cur1 || cur2 || t)
        {
            if (cur1)
            {
                t += cur1->val;
                cur1 = cur1->next;
            }
            if (cur2)
            {
                t += cur2->val;
                cur2 = cur2->next;
            }
            prev->next = new ListNode(t % 10);
            prev = prev->next;
            t /= 10;
        }
        prev = newhead->next;
        delete newhead;
        return prev;
    }
};

52. 两两交换链表中的节点(medium)


https://i-blog.csdnimg.cn/direct/064b14ac776247f091a3d8395b69fc2b.png


https://i-blog.csdnimg.cn/direct/83404827bc8f4691ae80127433a47ab7.png


https://i-blog.csdnimg.cn/direct/597ad1c126e9433ea211cc16c8104546.png


https://i-blog.csdnimg.cn/direct/66c5da0e36bd48efb2bcc4b83f9569d4.png


代码如下(示例):

class Solution
{
public:
    ListNode* swapPairs(ListNode* head)
    {
        if (head == nullptr || head->next == nullptr) return head;
        ListNode* newHead = new ListNode(0);
        newHead->next = head;
        ListNode* prev = newHead, * cur = prev->next, * next = cur->next, * nnext
            = next->next;
        while (cur && next)
        {
            // 交换结点
            prev->next = next;
            next->next = cur;
            cur->next = nnext;
            // 修改指针
            prev = cur; // 注意顺序
            cur = nnext;
            if (cur) next = cur->next;
            if (next) nnext = next->next;
        }
        cur = newHead->next;
        delete newHead;
        return cur;
    }
};

53. 重排链表(medium)


https://i-blog.csdnimg.cn/direct/99fb49b8e96b420bb83b491e88b6fd2d.png


https://i-blog.csdnimg.cn/direct/cf7d10e86b844a278337c0bcba567323.png
https://i-blog.csdnimg.cn/direct/edfd83a3e53b423b9e64a02c3f7fcbd2.png
https://i-blog.csdnimg.cn/direct/d1ed5b99ca004a01b9065ab5eb6d205a.png


代码如下(示例):

class Solution
{
public:
    void reorderList(ListNode* head)
    {
        // 处理边界情况
        if (head == nullptr || head->next == nullptr || head->next->next ==
            nullptr) return;
        // 1. 找到链表的中间节点 - 快慢双指针(⼀定要画图考虑 slow 的落点在哪⾥)
        ListNode* slow = head, * fast = head;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        // 2. 把 slow 后⾯的部分给逆序 - 头插法
        ListNode* head2 = new ListNode(0);
        ListNode* cur = slow->next;
        slow->next = nullptr; // 注意把两个链表给断开
        while (cur)
        {
            ListNode* next = cur->next;
            cur->next = head2->next;
            head2->next = cur;
            cur = next;
        }
        // 3. 合并两个链表 - 双指针
        ListNode* ret = new ListNode(0);
        ListNode* prev = ret;
        ListNode* cur1 = head, * cur2 = head2->next;
        while (cur1)
        {
            // 先放第⼀个链表
            prev->next = cur1;
            cur1 = cur1->next;
            prev = prev->next;
            // 再放第⼆个链表
            if (cur2)
            {
                prev->next = cur2;
                prev = prev->next;
                cur2 = cur2->next;
            }
        }
        delete head2;
        delete ret;
    }
};

54. 合并 K 个升序链表(hard)


https://i-blog.csdnimg.cn/direct/d5925154b3c54f3fb0189a3e31a2d6ab.png


https://i-blog.csdnimg.cn/direct/2dedab3949204427bf6d9bed3a85823b.png
https://i-blog.csdnimg.cn/direct/9a66eee82af149218bb5d68a06021cc8.png
https://i-blog.csdnimg.cn/direct/98f5c36fd76d462e9b840c05748ee360.png


代码如下(示例):

class Solution
{
    struct cmp
    {
        bool operator()(const ListNode* l1, const ListNode* l2)
        {
            return l1->val > l2->val;
        }
    };
public:
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        // 创建⼀个⼩根堆
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap;
        // 让所有的头结点进⼊⼩根堆
        for (auto l : lists)
            if (l) heap.push(l);

        // 合并 k 个有序链表
        ListNode* ret = new ListNode(0);
        ListNode* prev = ret;
        while (!heap.empty())
        {
            ListNode* t = heap.top();
            heap.pop();
            prev->next = t;
            prev = t;
            if (t->next) heap.push(t->next);
        }
        prev = ret->next;
        delete ret;
        return prev;
    }
};





// 递归
class Solution
{
public:
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        return merge(lists, 0, lists.size() - 1);
    }
    ListNode* merge(vector<ListNode*>& lists, int left, int right)
    {
        if (left > right) return nullptr;
        if (left == right) return lists[left];
        // 1. 平分数组
        int mid = left + right >> 1;
        // [left, mid] [mid + 1, right]
        // 2. 递归处理左右区间
        ListNode* l1 = merge(lists, left, mid);
        ListNode* l2 = merge(lists, mid + 1, right);
        // 3. 合并两个有序链表
        return mergeTowList(l1, l2);
    }
    ListNode* mergeTowList(ListNode* l1, ListNode* l2)
    {
        if (l1 == nullptr) return l2;
        if (l2 == nullptr) return l1;
        // 合并两个有序链表
        ListNode head;
        ListNode* cur1 = l1, * cur2 = l2, * prev = &head;
        head.next = nullptr;
        while (cur1 && cur2)
        {
            if (cur1->val <= cur2->val)
            {
                prev = prev->next = cur1;
                cur1 = cur1->next;
            }
            else
            {
                prev = prev->next = cur2;
                cur2 = cur2->next;
            }
        }
        if (cur1) prev->next = cur1;
        if (cur2) prev->next = cur2;
        return head.next;
    }
};

55. K个⼀组翻转链表(hard)


https://i-blog.csdnimg.cn/direct/d245f8fcca484a79ad7d00fdb15f95b0.png


https://i-blog.csdnimg.cn/direct/6c35cb0253dd43caa8e6595afcede30e.png
https://i-blog.csdnimg.cn/direct/dc15fb26752a45769975f697d6669920.png
https://i-blog.csdnimg.cn/direct/406ee8f559c14fdd90b3b50a17da6d00.png


代码如下(示例):

class Solution
{
public:
    ListNode* reverseKGroup(ListNode* head, int k)
    {
        // 1. 先求出需要逆序多少组
        int n = 0;
        ListNode* cur = head;
        while (cur)
        {
            cur = cur->next;
            n++;
        }
        n /= k;
        // 2. 重复 n 次:⻓度为 k 的链表的逆序即可
        ListNode* newHead = new ListNode(0);
        ListNode* prev = newHead;
        cur = head;
        for (int i = 0; i < n; i++)
        {
            ListNode* tmp = cur;
            for (int j = 0; j < k; j++)
            {
                ListNode* next = cur->next;
                cur->next = prev->next;
                prev->next = cur;
                cur = next;
            }
            prev = tmp;
        }
        // 把不需要翻转的接上
        prev->next = cur;
        cur = newHead->next;
        delete newHead;
        return cur;
    }
};

专题十:哈希表

哈希表简介

https://i-blog.csdnimg.cn/direct/8c76c4e7a7fe491db5856b8573954617.png


56. 两数之和(easy)


https://i-blog.csdnimg.cn/direct/56ab09df2b75431990a6b195b7dc5147.png


https://i-blog.csdnimg.cn/direct/cc2d3a423eb04a6e8dc2120a77e05837.png
https://i-blog.csdnimg.cn/direct/cd3ea4e434f74f83adee0ba6e8d1479c.png
https://i-blog.csdnimg.cn/direct/6036205f132c47b9bae41be66b5a6f4e.png


代码如下(示例):

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

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target)
    {
        unordered_map<int, int> hash; // <nums[i], i>
        for (int i = 0; i < nums.size(); i++)
        {
            int x = target - nums[i];
            if (hash.count(x)) return { hash[x] , i };
            hash[nums[i]] = i;
        }
        return { -1 , -1 };
    }
};

57. 判断是否互为字符重排(easy)


https://i-blog.csdnimg.cn/direct/fa4cba65f9b04fb69f62b7668a618069.png


https://i-blog.csdnimg.cn/direct/442c5176c657455993f13f9f4ecab5ee.png
https://i-blog.csdnimg.cn/direct/f95cac875a5a435ca3d77ad075c87d95.png


代码如下(示例):

class Solution {
public:
    bool CheckPermutation(string s1, string s2)
    {
        if (s1.size() != s2.size()) return false;
        int hash[26] = { 0 };
        // 先统计第一个字符串的信息
        for (auto x : s1) hash[x - 'a']++;
        // 再扫描第二个字符串,看看是否能重排
        for (auto x : s2)
        {
            hash[x - 'a']--;
            if (hash[x - 'a'] < 0) return false;
        }
        return true;
    }
};

58. 存在重复元素 I(easy)


https://i-blog.csdnimg.cn/direct/32463362b79a439d9525ae55c53052da.png


https://i-blog.csdnimg.cn/direct/ce144ce88afd4306a7eb4c0e312c51a6.png
https://i-blog.csdnimg.cn/direct/63deeda4b4694100bc7436de9f94de0d.png


代码如下(示例):

class Solution {
public:
    bool containsDuplicate(vector<int>& nums)
    {
        unordered_set<int> hash;
        for (auto x : nums)
        {
            if (hash.count(x)) return true;
            else hash.insert(x);
        }
        return false;
    }
};

59. 存在重复元素 II(easy)


https://i-blog.csdnimg.cn/direct/d63bdd5112894e9eb3f599b296aac01c.png


https://i-blog.csdnimg.cn/direct/aa69556d5b6a49baabbdb20a23208c4f.png
https://i-blog.csdnimg.cn/direct/089dcbcfa9194ee3839a4ff1da8624b9.png


代码如下(示例):

class Solution
{
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k)
    {
        unordered_map<int, int> hash;
        for (int i = 0; i < nums.size(); i++)
        {
            if (hash.count(nums[i]))
            {
                if (i - hash[nums[i]] <= k) return true;
            }
            hash[nums[i]] = i;
        }
        return false;
    }
};

60. 字⺟异位词分组(medium)


https://i-blog.csdnimg.cn/direct/772901b55b4b4622b366ef52d6ede3cd.png


https://i-blog.csdnimg.cn/direct/0fd1c756ac034654b10ba93f20214f9a.png
https://i-blog.csdnimg.cn/direct/02501373055e41318fb7cd2d48e24bd4.png
https://i-blog.csdnimg.cn/direct/c12b6ea0d976425b93dddcf19c69e1be.png


代码如下(示例):

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs)
    {
        unordered_map<string, vector<string>> hash;
        // 1.把所有的字母异位词分组
        for (auto& s : strs)
        {
            string tmp = s;
            sort(tmp.begin(), tmp.end());
            hash[tmp].push_back(s);
        }
        // 2.结果提取出来
        vector<vector<string>> ret;
        for (auto& [x, y] : hash)
        {
            ret.push_back(y);
        }
        return ret;
    }
};

专题十一:字符串

61. 最⻓公共前缀(easy)


https://i-blog.csdnimg.cn/direct/d175d2f5ef05455fa7a65d338604ad8b.png


https://i-blog.csdnimg.cn/direct/43eec1c68bec455fbb6d0062a827e139.png
https://i-blog.csdnimg.cn/direct/02d45f106fc047668c49b7855957aabb.png
https://i-blog.csdnimg.cn/direct/a71cb57e55fe4fd6bd7c662b09997c53.png


代码如下(示例):

class Solution
{
public:
    string longestCommonPrefix(vector<string>& strs)
    {
        // 解法⼀:两两⽐较
        string ret = strs[0];
        for (int i = 1; i < strs.size(); i++)
            ret = findCommon(ret, strs[i]);
        return ret;
    }
    string findCommon(string& s1, string& s2)
    {
        int i = 0;
        while (i < min(s1.size(), s2.size()) && s1[i] == s2[i]) i++;
        return s1.substr(0, i);
    }
};


class Solution
{
public:
    string longestCommonPrefix(vector<string>& strs)
    {
        // 解法⼆:统⼀⽐较
        for (int i = 0; i < strs[0].size(); i++)
        {
            char tmp = strs[0][i];
            for (int j = 1; j < strs.size(); j++)
                if (i == strs[j].size() || tmp != strs[j][i])
                    return strs[0].substr(0, i);
        }
        return strs[0];
    }
};

62. 最⻓回⽂⼦串 (medium)


https://i-blog.csdnimg.cn/direct/9c8c0c0170c144a0b7dc868ab043fa7f.png


https://i-blog.csdnimg.cn/direct/cd68d8eec1f144f3a1717d1cb5656777.png
https://i-blog.csdnimg.cn/direct/b769a921cce544b78076c69f710b503d.png
https://i-blog.csdnimg.cn/direct/4a93b8e3369c45c389bba5731f0c1acf.png


代码如下(示例):

class Solution
{
public:
    string longestPalindrome(string s)
    {
        // 中⼼扩展算法
        int begin = 0, len = 0, n = s.size();
        for (int i = 0; i < n; i++) // 依次枚举所有的中点
        {
            // 先做⼀次奇数⻓度的扩展
            int left = i, right = i;
            while (left >= 0 && right < n && s[left] == s[right])
            {
                left--;
                right++;
            }
            if (right - left - 1 > len)
            {
                begin = left + 1;
                len = right - left - 1;
            }
            // 偶数⻓度的扩展
            left = i, right = i + 1;
            while (left >= 0 && right < n && s[left] == s[right])
            {
                left--;
                right++;
            }
            if (right - left - 1 > len)
            {
                begin = left + 1;
                len = right - left - 1;
            }
        }
        return s.substr(begin, len);
    }
};

63. ⼆进制求和(easy)


https://i-blog.csdnimg.cn/direct/b7dba2d3ac9e45a5afd0155caced96f4.png


https://i-blog.csdnimg.cn/direct/1e06164a7996432cab63096af860b2ef.png
https://i-blog.csdnimg.cn/direct/d1267e2751dd493692b1584f086cc6a9.png


代码如下(示例):

class Solution {
public:
    string addBinary(string a, string b)
    {
        string str;
        int cur1 = a.size() - 1, cur2 = b.size() - 1, t = 0;
        while (cur1 >= 0 || cur2 >= 0 || t)
        {
            if (cur1 >= 0) t += a[cur1--] - '0';
            if (cur2 >= 0) t += b[cur2--] - '0';
            str += t % 2 + '0';
            t /= 2;
        }
        reverse(str.begin(), str.end());
        return str;
    }
};

64. 字符串相乘(medium)


https://i-blog.csdnimg.cn/direct/10160f31241442cf87bdafb701530a87.png


https://i-blog.csdnimg.cn/direct/9f60556eb05f47d0ae0ba770608a42db.png
https://i-blog.csdnimg.cn/direct/ee0be115579143aa9cbbc52570adff98.png
https://i-blog.csdnimg.cn/direct/3538b10ec69b4390ac176553e5aab999.png


代码如下(示例):

class Solution
{
public:
    string multiply(string n1, string n2)
    {
        // 解法:⽆进位相乘后相加,然后处理进位
        int m = n1.size(), n = n2.size();
        reverse(n1.begin(), n1.end());
        reverse(n2.begin(), n2.end());
        vector<int> tmp(m + n - 1);
        // 1. ⽆进位相乘后相加
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                tmp[i + j] += (n1[i] - '0') * (n2[j] - '0');

        // 2. 处理进位
        int cur = 0, t = 0;
        string ret;
        while (cur < m + n - 1 || t)
        {
            if (cur < m + n - 1) t += tmp[cur++];
            ret += t % 10 + '0';
            t /= 10;
        }
        // 3. 处理前导零
        while (ret.size() > 1 && ret.back() == '0') ret.pop_back();
        reverse(ret.begin(), ret.end());
        return ret;
    }
};

专题十二:栈

65. 删除字符中的所有相邻重复项(easy)


https://i-blog.csdnimg.cn/direct/b2b7222e704b42dfa46ca769076328b9.png


https://i-blog.csdnimg.cn/direct/792f04d9ae34407d861f345c9c6dc774.png
https://i-blog.csdnimg.cn/direct/1d7528ac7aaa4c92b749524021a61343.png


代码如下(示例):

class Solution
{
public:
    string removeDuplicates(string s)
    {
        string ret; // 搞⼀个数组,模拟栈结构即可
        for (auto ch : s)
        {
            if (ret.size() && ch == ret.back()) ret.pop_back(); // 出栈
            else ret += ch; // ⼊栈
        }
        return ret;
    }
};

66. ⽐较含退格的字符串(easy)


https://i-blog.csdnimg.cn/direct/cf4193fb6dfb40dd910afcb9a248cb89.png


https://i-blog.csdnimg.cn/direct/ae9f9f368b514542957cd87635ea8f67.png
https://i-blog.csdnimg.cn/direct/8fefb20af8fc479d87c7121d031dccfa.png
https://i-blog.csdnimg.cn/direct/a5a254f1b7eb4b72a2cdfa41373f7d42.png


代码如下(示例):

class Solution
{
public:
    bool backspaceCompare(string s, string t)
    {
        return changeStr(s) == changeStr(t);
    }
    string changeStr(string& s)
    {
        string ret; // ⽤数组模拟栈结构
        for (char ch : s)
        {
            if (ch != '#') ret += ch;
            else
            {
                if (ret.size()) ret.pop_back();
            }
        }
        return ret;
    }
};



class Solution {
public:
    bool backspaceCompare(string s, string t)
    {
        string str1, str2;
        for (auto x : s)
        {
            if (str1.size() && x == '#') str1.pop_back();
            else {
                if (x != '#') str1 += x;
            }
        }
        for (auto x : t)
        {
            if (str2.size() && x == '#') str2.pop_back();
            else {
                if (x != '#') str2 += x;
            }
        }
        return strcmp(str1.c_str(), str2.c_str()) == 0;
    }
};

67. 基本计算器 II(medium)


https://i-blog.csdnimg.cn/direct/c2a1e3e13490421fb94bc773499cf198.png


https://i-blog.csdnimg.cn/direct/e54421983b79464e877c3c46d669cb10.png
https://i-blog.csdnimg.cn/direct/d6543aa5b27f4953a559a326d4b9ea63.png
https://i-blog.csdnimg.cn/direct/926b815e26474b04a6c7db39ceef7f42.png


代码如下(示例):

class Solution
{
public:
    int calculate(string s)
    {
        vector<int> st; // ⽤数组来模拟栈结构
        int i = 0, n = s.size();
        char op = '+';
        while (i < n)
        {
            if (s[i] == ' ') i++;
            else if (s[i] >= '0' && s[i] <= '9')
            {
                // 先把这个数字给提取出来
                int tmp = 0;
                while (i < n && s[i] >= '0' && s[i] <= '9')
                    tmp = tmp * 10 + (s[i++] - '0');
                if (op == '+') st.push_back(tmp);
                else if (op == '-') st.push_back(-tmp);
                else if (op == '*') st.back() *= tmp;
                else st.back() /= tmp;
            }
            else
            {
                op = s[i];
                i++;
            }
        }
        int ret = 0;
        for (auto x : st) ret += x;
        return ret;
    }
};

68. 字符串解码(medium)


https://i-blog.csdnimg.cn/direct/3d6635d1b93b426cbe18be8c12eb4547.png


https://i-blog.csdnimg.cn/direct/be7bd2910fca4cacb9b968842b92a0ab.png
https://i-blog.csdnimg.cn/direct/0b6c1a23732b47b7bddbd62ad54d7e05.png
https://i-blog.csdnimg.cn/direct/18f3adde5e0247e0b6f4b1c86f877679.png
https://i-blog.csdnimg.cn/direct/a224c6866919490bb14a8ecc6c2731d5.png


代码如下(示例):

class Solution
{
public:
    string decodeString(string s)
    {
        stack<int> nums;
        stack<string> st;
        st.push("");
        int i = 0, n = s.size();
        while (i < n)
        {
            if (s[i] >= '0' && s[i] <= '9')
            {
                int tmp = 0;
                while (s[i] >= '0' && s[i] <= '9')
                {
                    tmp = tmp * 10 + (s[i] - '0');
                    i++;
                }
                nums.push(tmp);
            }
            else if (s[i] == '[')
            {
                i++; // 把括号后⾯的字符串提取出来
                string tmp = "";
                while (s[i] >= 'a' && s[i] <= 'z')
                {
                    tmp += s[i];
                    i++;
                }
                st.push(tmp);
            }
            else if (s[i] == ']')
            {
                string tmp = st.top();
                st.pop();
                int k = nums.top();
                nums.pop();
                while (k--)
                {
                    st.top() += tmp;
                }
                i++; // 跳过这个右括号
            }
            else
            {
                string tmp;
                while (i < n && s[i] >= 'a' && s[i] <= 'z')
                {
                    tmp += s[i];
                    i++;
                }
                st.top() += tmp;
            }
        }
        return st.top();
    }
};

69. 验证栈序列(medium)


https://i-blog.csdnimg.cn/direct/a6eb5fc138124efa8e1c47e92f121485.png


https://i-blog.csdnimg.cn/direct/76d7ea913e2043ad8f26e0e8f31fcee8.png

https://i-blog.csdnimg.cn/direct/0a8ff21d411e4539bb4563c33e969029.png


代码如下(示例):

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
    {
        stack<int> st;
        int i = 0, n = pushed.size();
        for (auto x : pushed)
        {
            st.push(x);
            while (st.size() && st.top() == popped[i])
            {
                st.pop();
                i++;
            }
        }
        return i == n;
    }
};

专题十三:队列 + 宽搜 (BFS)

70. N 叉树的层序遍历(medium)


https://i-blog.csdnimg.cn/direct/4bb4379ac88343e08e6d707dea97ea65.png


https://i-blog.csdnimg.cn/direct/c21d65452b5d41e28eed5f0ed99690c7.png
https://i-blog.csdnimg.cn/direct/ddd97fdb64664b9bad612633705d7b00.png


代码如下(示例):

// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};


class Solution {
public:
    vector<vector<int>> levelOrder(Node* root)
    {
        vector<vector<int>> ret;// 记录最终结果
        queue<Node*> q; // 层序遍历需要的队列
        if (root == nullptr) return ret;
        q.push(root);
        while (q.size())
        {
            int sz = q.size(); // 求出本层元素的个数
            vector<int> tmp; //统计本层的节点
            for (int i = 0; i < sz; i++)
            {
                Node* t = q.front(); // 取出对头
                q.pop();
                tmp.push_back(t->val);
                for (Node* child : t->children) //让下一层节点入队列
                {
                    if (child != nullptr) q.push(child);
                }
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

71. ⼆叉树的锯⻮形层序遍历(medium)


https://i-blog.csdnimg.cn/direct/b21aaba8287443c7bedbea6de8735b99.png


https://i-blog.csdnimg.cn/direct/cc2fddf6cfba4922bd57896126bb016f.png
https://i-blog.csdnimg.cn/direct/c40ed141490a43a6b60d3bfebb9a692c.png


代码如下(示例):

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) 
    {
        vector<vector<int>> ret;
        if(root == nullptr) return ret;
        queue<TreeNode*> q;
        q.push(root);
        int level = 1;
        while(q.size())
        {
            int sz = q.size();
            vector<int> tmp;
            for(int i = 0 ; i < sz ; i++)
            {
                auto t = q.front();
                q.pop();
                tmp.push_back(t->val);
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            // 判断是否逆序
            if(level % 2 == 0) reverse(tmp.begin() , tmp.end());
            ret.push_back(tmp);
            level++;
        }
        return ret;
    }
};

72. ⼆叉树的最⼤宽度(medium)


https://i-blog.csdnimg.cn/direct/148ad7108d95440ea5c02df8c122329d.png
https://i-blog.csdnimg.cn/direct/102f71a7f9424066b0b400f9ca479495.png


https://i-blog.csdnimg.cn/direct/5318efea6ce04c439175218140f4b16d.png
https://i-blog.csdnimg.cn/direct/5bbba343f62742248717c5aee91e40b3.png


代码如下(示例):

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int widthOfBinaryTree(TreeNode* root)
    {
        vector<pair<TreeNode*, unsigned int>> q;//用数组模拟队列
        q.push_back({ root , 1 });
        unsigned int ret = 0;
        while (q.size())
        {
            // 先更新这一层的宽度
            auto& [x1, y1] = q[0];
            auto& [x2, y2] = q.back();
            ret = max(ret, y2 - y1 + 1);
            // 让下一层进队列
            vector<pair<TreeNode*, unsigned int>> tmp;//让下一层进入这个队列
            for (auto& [x, y] : q)
            {
                if (x->left) tmp.push_back({ x->left, y * 2 });
                if (x->right) tmp.push_back({ x->right, y * 2 + 1 });
            }
            q = tmp;
        }
        return ret;
    }
};

73. 在每个树⾏中找最⼤值(medium)


https://i-blog.csdnimg.cn/direct/b587e06fc9554abd8c7e3a2230b2942e.png


https://i-blog.csdnimg.cn/direct/7f66784edaf34a1eb083f05cc75d5c5e.png
https://i-blog.csdnimg.cn/direct/53d78130eccc409c8ba854a170489446.png


代码如下(示例):

class Solution
{
public:
    vector<int> largestValues(TreeNode* root)
    {
        vector<int> ret;
        if (root == nullptr) return ret;
        queue<TreeNode*> q;
        q.push(root);
        while (q.size())
        {
            int sz = q.size();
            int tmp = INT_MIN;
            for (int i = 0; i < sz; i++)
            {
                auto t = q.front();
                q.pop();
                tmp = max(tmp, t->val);
                if (t->left) q.push(t->left);
                if (t->right) q.push(t->right);
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

专题十四:优先级队列 (堆)

74. 最后⼀块石头的重量(easy)


https://i-blog.csdnimg.cn/direct/65abb9a93dfb422b8752a46efd70852f.png


https://i-blog.csdnimg.cn/direct/6dff296803ec4d638687f89dc4057fb4.png
https://i-blog.csdnimg.cn/direct/245ac9d1a5a44f4189007aeb8b51846b.png


代码如下(示例):

class Solution
{
public:
    int lastStoneWeight(vector<int>& stones)
    {
        // 1. 创建⼀个⼤根堆
        priority_queue<int> heap;
        // 2. 将所有元素丢进这个堆⾥⾯
        for (auto x : stones) heap.push(x);
        // 3. 模拟这个过程
        while (heap.size() > 1)
        {
            int a = heap.top(); heap.pop();
            int b = heap.top(); heap.pop();
            if (a > b) heap.push(a - b);
        }
        return heap.size() ? heap.top() : 0;
    }
};

75. 数据流中的第 K ⼤元素(easy)


https://i-blog.csdnimg.cn/direct/06d6d81deea746cca732cabaf452933b.png


https://i-blog.csdnimg.cn/direct/da92d29c03384bd5bf6df160ecba4ae8.png

https://i-blog.csdnimg.cn/direct/d485a62eb10c423f950de71986f70aa7.png


代码如下(示例):

class KthLargest
{
    // 创建⼀个⼤⼩为 k 的⼩跟堆
    priority_queue<int, vector<int>, greater<int>> heap;
    int _k;
public:
    KthLargest(int k, vector<int>& nums)
    {
        _k = k;
        for (auto x : nums)
        {
            heap.push(x);
            if (heap.size() > _k) heap.pop();
        }
    }

    int add(int val)
    {
        heap.push(val);
        if (heap.size() > _k) heap.pop();
        return heap.top();
    }
};
/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

76. 前 K 个⾼频单词 (medium)


https://i-blog.csdnimg.cn/direct/0940e08cef3f44a6a4d46eae2e7f7c19.png


https://i-blog.csdnimg.cn/direct/fc748e55720347748ed148e7e225bf0d.png
https://i-blog.csdnimg.cn/direct/17765e38a87646d1abe4d321e7c6433d.png
https://i-blog.csdnimg.cn/direct/c770ba395f5b49208588724fb23274bb.png


代码如下(示例):

class Solution
{
    typedef pair<string, int> PSI;
    struct cmp
    {
        bool operator()(const PSI& a, const PSI& b)
        {
            if (a.second == b.second) // 频次相同,字典序按照⼤根堆的⽅式排列
            {
                return a.first < b.first;
            }
            return a.second > b.second;
        }
    };
public:
    vector<string> topKFrequent(vector<string>& words, int k)
    {
        // 1. 统计⼀下每⼀个单词的频次
        unordered_map<string, int> hash;
        for (auto& s : words) hash[s]++;
        // 2. 创建⼀个⼤⼩为 k 的堆
        priority_queue<PSI, vector<PSI>, cmp> heap;
        // 3. TopK 的主逻辑
        for (auto& psi : hash)
        {
            heap.push(psi);
            if (heap.size() > k) heap.pop();
        }
        // 4. 提取结果
        vector<string> ret(k);
        for (int i = k - 1; i >= 0; i--)
        {
            ret[i] = heap.top().first;
            heap.pop();
        }
        return ret;
    }
};

77. 数据流的中位数(hard)


https://i-blog.csdnimg.cn/direct/1c5887e894f848019d10b01c485251e1.png
https://i-blog.csdnimg.cn/direct/3df77cde21d140109b29d3e681df3355.png


https://i-blog.csdnimg.cn/direct/773ae7faf30e409aa0586bfcc1a9f55f.png
https://i-blog.csdnimg.cn/direct/9455d50248464c6aa627495abda0c278.png
https://i-blog.csdnimg.cn/direct/22c024616d6b45f0a8166fb09c92f859.png
https://i-blog.csdnimg.cn/direct/ecb0dc6fee18403bbfe8810bfe5fa751.png
https://i-blog.csdnimg.cn/direct/3442386c77a04028ae96f69b59a22cbd.png


代码如下(示例):

class MedianFinder
{
    priority_queue<int> left; // ⼤根堆
    priority_queue<int, vector<int>, greater<int>> right; // ⼩根堆
public:
    MedianFinder()
    {}

    void addNum(int num)
    {
        // 分类讨论即可
        if (left.size() == right.size()) // 左右两个堆的元素个数相同
        {
            if (left.empty() || num <= left.top()) // 放 left ⾥⾯
            {
                left.push(num);
            }
            else
            {
                right.push(num);
                left.push(right.top());
                right.pop();
            }
        }
        else
        {
            if (num <= left.top())
            {
                left.push(num);
                right.push(left.top());
                left.pop();
            }
            else
            {
                right.push(num);
            }
        }
    }

    double findMedian()
    {
        if (left.size() == right.size()) return (left.top() + right.top()) /
            2.0;
        else return left.top();
    }
};
/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

专题十五:BFS解决FloodFill算法

BFS和FloodFill介绍

https://i-blog.csdnimg.cn/direct/fcc065ff5c534cd598af96e6ce4178ae.png
https://i-blog.csdnimg.cn/direct/72fd41d4dc7d4d53a3d1befe1356d29c.png


78. 图像渲染(medium)


https://i-blog.csdnimg.cn/direct/118ef876daba4839b19f7aab2f01eadb.png
https://i-blog.csdnimg.cn/direct/f89cc16011d5490eb280a79018e5915e.png


https://i-blog.csdnimg.cn/direct/0c646d2d9e424152bdf569c4b4075bb4.png
https://i-blog.csdnimg.cn/direct/bda860e533ad43ca80a358f59fbae200.png


代码如下(示例):

class Solution {
public:
    typedef pair<int, int> PII;
    int dx[4] = { 0,0,1,-1 };
    int dy[4] = { 1,-1,0,0 };
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
    {
        int prev = image[sr][sc];
        if (prev == color) return image;
        int m = image.size(), n = image[0].size();
        queue<PII> q;
        q.push({ sr,sc });
        while (!q.empty())
        {
            auto [a, b] = q.front();
            image[a][b] = color;
            q.pop();
            for (int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev)
                {
                    q.push({ x,y });
                }
            }
        }
        return image;
    }
};

79. 岛屿数量(medium)


https://i-blog.csdnimg.cn/direct/359695ee27c74576a64f3093754022c3.png


https://i-blog.csdnimg.cn/direct/20a5fbaa8f344ac089a556f6f69e67e3.png
https://i-blog.csdnimg.cn/direct/b1961c0c0ea84956b7c278479333cf5b.png
https://i-blog.csdnimg.cn/direct/d984a1ed1727479587edb10e49e53c89.png


代码如下(示例):

class Solution
{
    int dx[4] = { 1, -1, 0, 0 };
    int dy[4] = { 0, 0, 1, -1 };
    bool vis[301][301];
    int m, n;
public:
    int numIslands(vector<vector<char>>& grid)
    {
        m = grid.size(), n = grid[0].size();
        int ret = 0;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (grid[i][j] == '1' && !vis[i][j])
                {
                    ret++;
                    bfs(grid, i, j); // 把这块陆地全部标记⼀下
                }
            }
        }
        return ret;
    }
    void bfs(vector<vector<char>>& grid, int i, int j)
    {
        queue<pair<int, int>> q;
        q.push({ i, j });
        vis[i][j] = true;
        while (q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for (int k = 0; k < 4; k++)
            {
                int x = a + dx[k], y = b + dy[k];
                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1' &&
                    !vis[x][y])
                {
                    q.push({ x, y });
                    vis[x][y] = true;
                }
            }
        }
    }
};

80. 岛屿的最大面积(medium)


https://i-blog.csdnimg.cn/direct/fcd1fe854bf643f5b8186ae22eb107dc.png
https://i-blog.csdnimg.cn/direct/f3e63c23abc64d8f8eb87fd3e1411876.png


https://i-blog.csdnimg.cn/direct/41c6f96062cd4274ad7200f8014c3611.png
https://i-blog.csdnimg.cn/direct/136fb1c4bba743d38fea442be21cd9c5.png
https://i-blog.csdnimg.cn/direct/879ea26fc23242ff9494e3795dc79f31.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
    bool vis[51][51];
    int m, n;
public:
    int maxAreaOfIsland(vector<vector<int>>& grid)
    {
        m = grid.size(), n = grid[0].size();
        int ret = 0;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (grid[i][j] == 1 && !vis[i][j])
                {
                    ret = max(ret, bfs(grid, i, j));
                }
            }
        }
        return ret;
    }
    int bfs(vector<vector<int>>& grid, int i, int j)
    {
        int count = 0;
        queue<pair<int, int>> q;
        q.push({ i, j });
        vis[i][j] = true;
        count++;
        while (q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for (int k = 0; k < 4; k++)
            {
                int x = a + dx[k], y = b + dy[k];
                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 &&
                    !vis[x][y])
                {
                    q.push({ x, y });
                    vis[x][y] = true;
                    count++;
                }
            }
        }
        return count;
    }
};

81. 被围绕的区域(medium)


https://i-blog.csdnimg.cn/direct/baa71ff17b1d47d2996a9e107a2144f3.png
https://i-blog.csdnimg.cn/direct/65102ad55cce41268a42bf07e621d0ce.png


https://i-blog.csdnimg.cn/direct/f32c2f26adb2489fb24d61ebc1c68649.png
https://i-blog.csdnimg.cn/direct/b44c32a197e6480096100f53a023a0b4.png
https://i-blog.csdnimg.cn/direct/30200ec75fb84f67aa977987269af24f.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
    int m, n;
public:
    void solve(vector<vector<char>>& board)
    {
        m = board.size(), n = board[0].size();
        // 1. 先处理边界上的 'O' 联通块,全部修改成 '.'
        for (int j = 0; j < n; j++)
        {
            if (board[0][j] == 'O') bfs(board, 0, j);
            if (board[m - 1][j] == 'O') bfs(board, m - 1, j);
        }
        for (int i = 0; i < m; i++)
        {
            if (board[i][0] == 'O') bfs(board, i, 0);
            if (board[i][n - 1] == 'O') bfs(board, i, n - 1);
        }
        // 2. 还原
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (board[i][j] == 'O') board[i][j] = 'X';
                else if (board[i][j] == '.') board[i][j] = 'O';
    }
    void bfs(vector<vector<char>>& board, int i, int j)
    {
        queue<pair<int, int>> q;
        q.push({ i, j });
        board[i][j] = '.';
        while (q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for (int k = 0; k < 4; k++)
            {
                int x = a + dx[k], y = b + dy[k];
                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O')
                {
                    q.push({ x, y });
                    board[x][y] = '.';
                }
            }
        }
    }
};

专题十六:BFS解决最短路径问题

最短路径问题介绍

https://i-blog.csdnimg.cn/direct/cb121e4d976a43279f6613ae090b3c41.png


82. 迷宫中离入口最近的出口(medium)


https://i-blog.csdnimg.cn/direct/3780bb388dc34b89b125380395c176eb.png
https://i-blog.csdnimg.cn/direct/716a91be5e714524b3af78d7ffcdd446.png


https://i-blog.csdnimg.cn/direct/212b700340824098b85a2ae2cd192c53.png
https://i-blog.csdnimg.cn/direct/d044c72251d346e7b761b2faabf6a0fa.png
https://i-blog.csdnimg.cn/direct/fe20ac5cda1543a6818aabf73c765f7c.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
public:
    int nearestExit(vector<vector<char>>& maze, vector<int>& e)
    {
        int m = maze.size(), n = maze[0].size();
        bool vis[m][n];
        memset(vis, 0, sizeof vis);
        queue<pair<int, int>> q;
        q.push({ e[0], e[1] });
        vis[e[0]][e[1]] = true;
        int step = 0;
        while (q.size())
        {
            step++;
            int sz = q.size();
            for (int i = 0; i < sz; i++)
            {
                auto [a, b] = q.front();
                q.pop();
                for (int j = 0; j < 4; j++)
                {
                    int x = a + dx[j], y = b + dy[j];
                    if (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == '.'
                        && !vis[x][y])
                    {
                        // 判断是否已经到达出⼝
                        if (x == 0 || x == m - 1 || y == 0 || y == n - 1) return
                            step;
                        q.push({ x, y });
                        vis[x][y] = true;
                    }
                }
            }
        }
        return -1;
    }
};

83. 最⼩基因变化(medium)


https://i-blog.csdnimg.cn/direct/b2f32c28d27b442bb45172873344744b.png
https://i-blog.csdnimg.cn/direct/517074b9988d4ef598f7c4629eb9a8a5.png


https://i-blog.csdnimg.cn/direct/93b304d4e29b4c71b02f6b821012b8d4.png
https://i-blog.csdnimg.cn/direct/96342d6b94da48e79d1c49ac0ed35375.png
https://i-blog.csdnimg.cn/direct/cccd4f9aa01e4619b07a818f772ed6c0.png


代码如下(示例):

class Solution
{
public:
    int minMutation(string startGene, string endGene, vector<string>& bank)
    {
        unordered_set<string> vis; // ⽤来标记已经搜索过的状态
        unordered_set<string> hash(bank.begin(), bank.end()); // 存储基因库⾥⾯的字符串
            string change = "ACGT";
        if (startGene == endGene) return 0;
        if (!hash.count(endGene)) return -1;
        queue<string> q;
        q.push(startGene);
        vis.insert(startGene);
        int ret = 0;
        while (q.size())
        {
            ret++;
            int sz = q.size();
            while (sz--)
            {
                string t = q.front();
                q.pop();
                for (int i = 0; i < 8; i++)
                {
                    string tmp = t; // 细节问题
                    for (int j = 0; j < 4; j++)
                    {
                        tmp[i] = change[j];
                        if (hash.count(tmp) && !vis.count(tmp))
                        {
                            if (tmp == endGene) return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return -1;
    }
};

84. 单词接⻰(hard)


https://i-blog.csdnimg.cn/direct/d992fb79df774150b8d099a7691db7a5.png


https://i-blog.csdnimg.cn/direct/7572dbde6f9d4d4a8772a45b33a2c0fb.png
https://i-blog.csdnimg.cn/direct/55947f06fd714703baf420378df15533.png
https://i-blog.csdnimg.cn/direct/8f9f2291edc24051a5ca7a26a4b39b53.png


代码如下(示例):

class Solution
{
public:
    int ladderLength(string beginWord, string endWord, vector<string>&
        wordList)
    {
        unordered_set<string> hash(wordList.begin(), wordList.end());
        unordered_set<string> vis; // 标记已经搜索过的单词
        if (!hash.count(endWord)) return 0;
        queue<string> q;
        q.push(beginWord);
        vis.insert(beginWord);
        int ret = 1;
        while (q.size())
        {
            ret++;
            int sz = q.size();
            while (sz--)
            {
                string t = q.front();
                q.pop();
                for (int i = 0; i < t.size(); i++)
                {
                    string tmp = t;
                    for (char ch = 'a'; ch <= 'z'; ch++)
                    {
                        tmp[i] = ch;
                        if (hash.count(tmp) && !vis.count(tmp))
                        {
                            if (tmp == endWord) return ret;
                            q.push(tmp);
                            vis.insert(tmp);
                        }
                    }
                }
            }
        }
        return 0;
    }
};

85. 为高尔夫比赛砍树(hard)


https://i-blog.csdnimg.cn/direct/130a3a9ffddd40e4a94b7ca3566d9f20.png
https://i-blog.csdnimg.cn/direct/4087dd8b0bad4d68baa36be5b31ce161.png


https://i-blog.csdnimg.cn/direct/0c7964d132d44317b84bba745b7694f1.png
https://i-blog.csdnimg.cn/direct/2df8f8e3d1be43e79fbad87ec7ca3f78.png
https://i-blog.csdnimg.cn/direct/830d2b3bad2243dfa051e1a20682e6a3.png


代码如下(示例):

class Solution
{
    int m, n;
public:
    int cutOffTree(vector<vector<int>>& f)
    {
        m = f.size(), n = f[0].size();
        // 1. 准备⼯作:找出砍树的顺序
        vector<pair<int, int>> trees;
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (f[i][j] > 1) trees.push_back({ i, j });

        sort(trees.begin(), trees.end(), [&](const pair<int, int>& p1, const
            pair<int, int>& p2)
            {
                return f[p1.first][p1.second] < f[p2.first][p2.second];
            });
        // 2. 按照顺序砍树
        int bx = 0, by = 0;
        int ret = 0;
        for (auto& [a, b] : trees)
        {
            int step = bfs(f, bx, by, a, b);
            if (step == -1) return -1;
            ret += step;
            bx = a, by = b;
        }
        return ret;
    }
    bool vis[51][51];
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
    int bfs(vector<vector<int>>& f, int bx, int by, int ex, int ey)
    {
        if (bx == ex && by == ey) return 0;
        queue<pair<int, int>> q;
        memset(vis, 0, sizeof vis); // 清空之前的数据
        q.push({ bx, by });
        vis[bx][by] = true;
        int step = 0;
        while (q.size())
        {
            step++;
            int sz = q.size();
            while (sz--)
            {
                auto [a, b] = q.front();
                q.pop();
                for (int i = 0; i < 4; i++)
                {
                    int x = a + dx[i], y = b + dy[i];
                    if (x >= 0 && x < m && y >= 0 && y < n && f[x][y] && !vis[x]
                        [y])
                    {
                        if (x == ex && y == ey) return step;
                        q.push({ x, y });
                        vis[x][y] = true;
                    }
                }
            }
        }
        return -1;
    }
};

专题十七:多源BFS

多源最短路径问题介绍

https://i-blog.csdnimg.cn/direct/e3d9230f15b7425ab4a88222dcd9b577.png


86. 01 矩阵(medium)


https://i-blog.csdnimg.cn/direct/89d1fa61cfa24a90a3a93768707ee5c6.png


https://i-blog.csdnimg.cn/direct/d14452acb5bc4db6a121a1ada8d216d7.png
https://i-blog.csdnimg.cn/direct/912a69e22ab34f2cbe06b350773bfe98.png
https://i-blog.csdnimg.cn/direct/f1ef290cc0784b5f9ec267781772b04d.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat)
    {
        int m = mat.size(), n = mat[0].size();

        // dist[i][j] == -1 表⽰:没有搜索过
        // dist[i][j] != -1 表⽰:最短距离
        vector<vector<int>> dist(m, vector<int>(n, -1));
        queue<pair<int, int>> q;
        // 1. 把所有的源点加⼊到队列中
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (mat[i][j] == 0)
                {
                    q.push({ i, j });
                    dist[i][j] = 0;
                }

        // 2. ⼀层⼀层的往外扩
        while (q.size())
        {
            auto [a, b] = q.front(); q.pop();
            for (int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1)
                {
                    dist[x][y] = dist[a][b] + 1;
                    q.push({ x, y });
                }
            }
        }
        return dist;
    }
};

87. ⻜地的数量(medium)


https://i-blog.csdnimg.cn/direct/88a30c0404654a56a9a421167e15e75b.png
https://i-blog.csdnimg.cn/direct/5cb9c05d4ae741389b6c203887a155bb.png


https://i-blog.csdnimg.cn/direct/2330f0fd4fe74ca7981313cb6c0051d3.png
https://i-blog.csdnimg.cn/direct/bbf6df37efae40349a041ef4d0096d68.png
https://i-blog.csdnimg.cn/direct/187ac462eef146008fbb349aaa594b67.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
public:
    int numEnclaves(vector<vector<int>>& grid)
    {
        int m = grid.size(), n = grid[0].size();
        vector<vector<bool>> vis(m, vector<bool>(n));
        queue<pair<int, int>> q;
        // 1. 把边上的 1 加⼊到队列中
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1)
                {
                    if (grid[i][j] == 1)
                    {
                        q.push({ i, j });
                        vis[i][j] = true;
                    }
                }
        // 2. 多源 bfs
        while (q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for (int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 &&
                    !vis[x][y])
                {
                    vis[x][y] = true;
                    q.push({ x, y });
                }
            }
        }
        // 3. 统计结果
        int ret = 0;
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j] == 1 && !vis[i][j])
                    ret++;
        return ret;
    }
};

88. 地图中的最⾼点(medium)


https://i-blog.csdnimg.cn/direct/05dd5b4cb8c442f9bc60d9fffc7d39eb.png
https://i-blog.csdnimg.cn/direct/be0962b4f23549e2aa750d732a2aa0aa.png


https://i-blog.csdnimg.cn/direct/bc8ae2c242fb42fc96895a10ad525896.png
https://i-blog.csdnimg.cn/direct/f26e0f2090a14e2dbeab47bc677bbcb3.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
public:
    vector<vector<int>> highestPeak(vector<vector<int>>& isWater)
    {
        int m = isWater.size(), n = isWater[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1));
        queue<pair<int, int>> q;
        // 1. 把所有的源点加⼊到队列⾥⾯
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (isWater[i][j])
                {
                    dist[i][j] = 0;
                    q.push({ i, j });
                }
        // 2. 多源 bfs
        while (q.size())
        {
            auto [a, b] = q.front(); q.pop();
            for (int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1)
                {
                    dist[x][y] = dist[a][b] + 1;
                    q.push({ x, y });
                }
            }
        }
        return dist;
    }
};

89. 地图分析(medium)


https://i-blog.csdnimg.cn/direct/b7eca1c5c412419c8203946519159d69.png


https://i-blog.csdnimg.cn/direct/616bad622c2a40378fa6902c800e2868.png
https://i-blog.csdnimg.cn/direct/d57e41d2d9254e3aafb643f87247ae26.png


代码如下(示例):

class Solution
{
    int dx[4] = { 0, 0, 1, -1 };
    int dy[4] = { 1, -1, 0, 0 };
public:
    int maxDistance(vector<vector<int>>& grid)
    {
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1));
        queue<pair<int, int>> q;
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j])
                {
                    dist[i][j] = 0;
                    q.push({ i, j });
                }

        int ret = -1;
        while (q.size())
        {
            auto [a, b] = q.front(); q.pop();
            for (int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1)
                {
                    dist[x][y] = dist[a][b] + 1;
                    q.push({ x, y });
                    ret = max(ret, dist[x][y]);
                }
            }
        }
        return ret;
    }
};

专题十八:BFS解决拓扑排序

拓扑排序介绍

https://i-blog.csdnimg.cn/direct/38fcaf25b43c43aaaafd7e03a12862c9.png
https://i-blog.csdnimg.cn/direct/b9f5e63f95474cc79b9053533176d97b.png


90. 课程表(medium)


https://i-blog.csdnimg.cn/direct/55ba1bcac9404eccb33892c2909d98b5.png


https://i-blog.csdnimg.cn/direct/d3855bfcdbcb44cc84759367914c9e3c.png
https://i-blog.csdnimg.cn/direct/56ce81a87e4d45ea84e6df8617bc3ad2.png


代码如下(示例):

class Solution
{
public:
    bool canFinish(int n, vector<vector<int>>& p)
    {
        unordered_map<int, vector<int>> edges; // 邻接表
        vector<int> in(n); // 存储每⼀个结点的⼊度

        // 1. 建图
        for (auto& e : p)
        {
            int a = e[0], b = e[1];
            edges[b].push_back(a);
            in[a]++;
        }
        // 2. 拓扑排序 - bfs
        queue<int> q;
        // 把所有⼊度为 0 的点加⼊到队列中
        for (int i = 0; i < n; i++)
        {
            if (in[i] == 0) q.push(i);
        }
        // 层序遍历
        while (!q.empty())
        {
            int t = q.front();
            q.pop();
            // 修改相连的边
            for (int e : edges[t])
            {
                in[e]--;
                if (in[e] == 0) q.push(e);
            }
        }
        // 3. 判断是否有环
        for (int i : in)
            if (i)
                return false;

        return true;
    }
};

91. 课程表II(medium)


https://i-blog.csdnimg.cn/direct/dc906b53473e49d8af46a8e976ec0b4d.png


https://i-blog.csdnimg.cn/direct/fc09116beedb4c7fa06e8c1a016d5654.png
https://i-blog.csdnimg.cn/direct/361ebea33d3f4abbab0699b0d64431e4.png


代码如下(示例):

class Solution
{
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites)
    {
        // 1. 准备⼯作
        vector<vector<int>> edges(numCourses); // 邻接表存储图
        vector<int> in(numCourses); // 存储每⼀个点的⼊度
        // 2. 建图
        for (auto& p : prerequisites)
        {
            int a = p[0], b = p[1]; // b -> a
            edges[b].push_back(a);
            in[a]++;
        }
        // 3. 拓扑排序
        vector<int> ret; // 统计排序后的结果
        queue<int> q;
        for (int i = 0; i < numCourses; i++)
        {
            if (in[i] == 0) q.push(i);
        }
        while (q.size())
        {
            int t = q.front(); q.pop();
            ret.push_back(t);
            for (int a : edges[t])
            {
                in[a]--;
                if (in[a] == 0) q.push(a);
            }
        }
        if (ret.size() == numCourses) return ret;
        else return {};
    }
};

92. 火星词典(hard)


https://i-blog.csdnimg.cn/direct/43654eef5591449e898468926dcc2ab1.png
https://i-blog.csdnimg.cn/direct/57008ef4d1a243b1b28fcd3b462207ab.png


https://i-blog.csdnimg.cn/direct/214aca8135be458ab0573a1a3de985c3.png
https://i-blog.csdnimg.cn/direct/7e7ca316d545458ba7aec3b8a4d230cf.png
https://i-blog.csdnimg.cn/direct/e0e4756218bc4c20b966ed48f13d4cef.png
https://i-blog.csdnimg.cn/direct/3f0e1c63396244e6983b2731dad0522b.png


代码如下(示例):

class Solution
{
    unordered_map<char, unordered_set<char>> edges; // 邻接表来存储图
    unordered_map<char, int> in; // 统计⼊度
    bool cheak; // 处理边界情况
public:
    string alienOrder(vector<string>& words)
    {
        // 1. 建图 + 初始化⼊度哈希表
        for (auto& s : words)
        {
            for (auto ch : s)
            {
                in[ch] = 0;
            }
        }
        int n = words.size();
        for (int i = 0; i < n; i++)
        {
            for (int j = i + 1; j < n; j++)
            {
                add(words[i], words[j]);
                if (cheak) return "";
            }
        }

        // 2. 拓扑排序
        queue<char> q;
        for (auto& [a, b] : in)
        {
            if (b == 0) q.push(a);
        }
        string ret;
        while (q.size())
        {
            char t = q.front(); q.pop();
            ret += t;
            for (char ch : edges[t])
            {
                if (--in[ch] == 0) q.push(ch);
            }
        }
        // 3. 判断
        for (auto& [a, b] : in)
            if (b != 0) return "";

        return ret;
    }
    void add(string& s1, string& s2)
    {
        int n = min(s1.size(), s2.size());
        int i = 0;
        for (; i < n; i++)
        {
            if (s1[i] != s2[i])
            {
                char a = s1[i], b = s2[i]; // a -> b
                if (!edges.count(a) || !edges[a].count(b))
                {
                    edges[a].insert(b);
                    in[b]++;
                }
                break;
            }
        }
        if (i == s2.size() && i < s1.size()) cheak = true;
    }
};

专题十九:位运算加练

位运算复习


https://i-blog.csdnimg.cn/direct/bf00085b07ec4b44937e07db582d010b.png
https://i-blog.csdnimg.cn/direct/7ead9154263f45b9b5f4898f9e4cb97e.png
https://i-blog.csdnimg.cn/direct/9193a06e6a7846568a81927ec7114080.png
https://i-blog.csdnimg.cn/direct/5387dc113100466a8dd946e256ca4e86.png


96. 位1的个数(easy)


https://i-blog.csdnimg.cn/direct/fdd2344b4ce34904ab41b4f238114caa.png


https://i-blog.csdnimg.cn/direct/c8f664e4605749a092d4a6db2c24c0bc.png


代码如下(示例):

class Solution {
public:
    int hammingWeight(int n)
    {
        int sum = 0;
        int i = 0;
        for (int i = 0; i < 32; i++)
        {
            if ((n >> i) & 1 == 1)
            {
                sum++;
            }
        }
        return sum;
    }
};

97. 比特位计数(easy)


https://i-blog.csdnimg.cn/direct/f0de094d80c64ab98989921e3660a986.png


https://i-blog.csdnimg.cn/direct/ac10672a13c249dcb7ca34461f6f45ea.png


代码如下(示例):

class Solution {
public:
    vector<int> countBits(int n)
    {
        vector<int> nums(n + 1);
        for (int i = 0; i <= n; i++)
        {
            int sum = 0;
            for (int j = 0; j < 32; j++)
            {
                if ((i >> j) & 1 == 1)
                    sum++;
            }
            nums[i] = sum;
        }
        return nums;
    }
};

98. 汉明距离(easy)


https://i-blog.csdnimg.cn/direct/13056fefb46943dfa71386ca498c0dd2.png


https://i-blog.csdnimg.cn/direct/38f1d5a09efb4926aa108bc8f5d7c22f.png


代码如下(示例):

class Solution {
public:
    int hammingDistance(int x, int y)
    {
        int sum = 0;
        for (int i = 0; i < 32; i++)
        {
            if (((x >> i) & 1) ^ ((y >> i) & 1) == 1)
                sum++;
        }
        return sum;
    }
};

99. 只出现一次的数字(easy)


https://i-blog.csdnimg.cn/direct/e22283c6a3c941e495155dd62b2cc1f9.png


https://i-blog.csdnimg.cn/direct/989b4903c8ff4769a22eaf8b68eb65cc.png


代码如下(示例):

class Solution {
public:
    int singleNumber(vector<int>& nums)
    {
        int ret = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            ret ^= nums[i];
        }
        return ret;
    }
};

100. 只出现一次的数字 III(medium)


https://i-blog.csdnimg.cn/direct/9f702fef49ea4c328ebc39820ff5c287.png


https://i-blog.csdnimg.cn/direct/b7340b1a412240f687d711d784bee405.png
https://i-blog.csdnimg.cn/direct/5a4c6dab776a48b9a6f6a3e7b4347f4f.png


代码如下(示例):

int* singleNumber(int* nums, int numsSize, int* returnSize)
{
    int* ans = (int*)calloc(2, sizeof(int));
    int ret = 0;
    int i = 0;
    //计算数组中所有数异或起来的结果ret 
    for (i = 0; i < numsSize; i++) {
        ret ^= nums[i];
    }

    //从低位往⾼位遍历计算ret的⼆进制中哪⼀位是1 
    int pos = 0;
    for (i = 0; i < 32; i++) {
        if (((ret >> i) & 1) == 1) {
            pos = i;
            break;
        }
    }
    //3. 分组异或 
    for (i = 0; i < numsSize; i++) {
        //若当前数pos位为1,作为其中⼀组对ans[0]进⾏异或操作 
        if (((nums[i] >> pos) & 1) == 1) {
            ans[0] ^= nums[i];
        }
        //否则,作为另⼀组对ans[1]进⾏异或操作。 
        else {
            ans[1] ^= nums[i];
        }
    }
    //ans[1]另⼀种计算⽅法 
    //ans[1]=ret^ans[0];
    //更新数组⻓度 
    *returnSize = 2;
    //返回答案数组 
    return ans;
}


整体源代码

代码如下(示例):

using namespace std;
#include<iostream>
#include<vector>


// 双指针问题第一题
//class Solution
//{
//public:
//    void moveZeroes(vector<int>& nums)
//    {
//        int cur = 0;
//        int dest = -1;
//        int n = nums.size();
//        while (cur < n)
//        {
//            if (nums[cur] != 0)
//            {
//                swap(nums[dest + 1], nums[cur]);
//                cur++;
//                dest++;
//            }
//            else
//            {
//                cur++;
//            }
//        }
//    }
//};
//
//
//// 简便版
//class Solution
//{
//public:
//    void moveZeroes(vector<int>& nums)
//    {
//        for (int cur = 0, dest = -1; cur < nums.size(); cur++)
//            if (nums[cur]) // 处理⾮零元素
//                swap(nums[++dest], nums[cur]);
//    }
//};



//class Solution
//{
//public:
//	void duplicateZeros(vector<int>& arr)
//	{
//		// 1. 先找到最后⼀个数
//		int cur = 0, dest = -1, n = arr.size();
//		while (cur < n)
//		{
//			if (arr[cur]) dest++;
//			else dest += 2;
//			if (dest >= n - 1) break;
//			cur++;
//		}
//		// 2. 处理⼀下边界情况
//		if (dest == n)
//		{
//			arr[n - 1] = 0;
//			cur--; dest -= 2;
//		}
//		// 3. 从后向前完成复写操作
//		while (cur >= 0)
//		{
//			if (arr[cur]) arr[dest--] = arr[cur--];
//			else
//			{
//				arr[dest--] = 0;
//				arr[dest--] = 0;
//				cur--;
//			}
//		}
//	}
//};




//class Solution
//{
//public:
//	int bitSum(int n) // 返回 n 这个数每⼀位上的平⽅和{
//		int sum = 0;
//	while (n)
//	{
//		int t = n % 10;
//		sum += t * t;
//		n /= 10;
//	}
//	return sum;
//}
//bool isHappy(int n)
//{
//	int slow = n, fast = bitSum(n);
//	while (slow != fast)
//	{
//		slow = bitSum(slow);
//		fast = bitSum(bitSum(fast));
//	}
//	return slow == 1;
//}
//};





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




//class Solution
//{
//	public int triangleNumber(int[] nums)
//	{
//		// 1. 优化:排序
//		Arrays.sort(nums);
//		// 2. 利⽤双指针解决问题
//		int ret = 0, n = nums.length;
//		for (int i = n - 1; i >= 2; i--) // 先固定最⼤的数
//		{
//			// 利⽤双指针快速统计出符合要求的三元组的个数
//			int left = 0, right = i - 1;
//			while (left < right)
//			{
//				if (nums[left] + nums[right] > nums[i])
//				{
//					ret += right - left;
//					right--;
//				}
//				else
//				{
//					left++;
//				}
//			}
//		}
//		return ret;
//	}
//}



//class Solution {
//public:
//    vector<int> twoSum(vector<int>& price, int target)
//    {
//        int left = 0, right = price.size() - 1;
//        while (left < right)
//        {
//            int sum = price[left] + price[right];
//            if (sum > target) right--;
//            else if (sum < target) left++;
//            else
//            {
//                return { price[left] , price[right] };
//            }
//        }
//        return { -1 , -1 };
//    }
//};





//class Solution {
//public:
//    vector<vector<int>> threeSum(vector<int>& nums)
//    {
//        vector<vector<int>> net;
//        // 第一步:先排序
//        sort(nums.begin(), nums.end());
//        // 第二步:先固定一个数 a <= 0
//        int i = 0, n = nums.size();
//        for (i = 0; i < n;)
//        {
//            if (nums[i] > 0) break;
//            else
//            {
//                // 区间右边用双指针算法
//                int left = i + 1, right = n - 1, ret = -(nums[i]);
//                while (left < right)
//                {
//                    int sum = nums[left] + nums[right];
//                    if (sum < ret) left++;
//                    else if (sum > ret) right--;
//                    else
//                    {
//                        net.push_back({ nums[i] , nums[left] , nums[right] });
//                        left++;  right--;
//                        // 去重操作
//                        while (left < right && nums[left] == nums[left - 1]) left++;
//                        while (left < right && nums[right] == nums[right + 1]) right--;
//                    }
//                }
//                i++;
//                while (i < n && nums[i] == nums[i - 1]) i++;
//            }
//        }
//        return net;
//    }
//};



//#include <vector>
//using namespace std;
//
//class Solution {
//public:
//    vector<vector<int>> threeSum(vector<int>& nums)
//    {
//        vector<vector<int>> net;
//        // 第一步:先排序
//        sort(nums.begin(), nums.end());
//        // 第二步:先固定一个数 a <= 0
//        int i = 0, n = nums.size();
//        for (i = 0; i < n;)
//        {
//            if (nums[i] > 0) break;
//            else
//            {
//                // 区间右边用双指针算法
//                int left = i + 1, right = n - 1, ret = -(nums[i]);
//                while (left < right)
//                {
//                    int sum = nums[left] + nums[right];
//                    if (sum < ret) left++;
//                    else if (sum > ret) right--;
//                    else
//                    {
//                        net.push_back({ nums[i] , nums[left] , nums[right] });
//                        left++;  right--;
//                        // 去重操作
//                        while (left < right && nums[left] == nums[left - 1]) left++;
//                        while (left < right && nums[right] == nums[right + 1]) right--;
//                    }
//                }
//
//                i++;
//                while (i < n && nums[i] == nums[i - 1]) i++;
//            }
//
//        }
//        return net;
//    }
//};
//
//
//int main()
//{
//    vector<int> v = { -1,0,1,2,-1,-4 };
//    Solution s;
//    s.threeSum(v);
//    return 0;
//}



//class Solution {
//public:
//    vector<vector<int>> fourSum(vector<int>& nums, int target)
//    {
//        vector<vector<int>> ret;
//        // 先排序
//        sort(nums.begin(), nums.end());
//        // 再依次固定一个数 a
//        int n = nums.size();
//        for (int i = 0; i < n; )
//        {
//            // 三数字取和
//            for (int j = i + 1; j < n;)
//            {
//                // 两个数字取和
//                int left = j + 1, right = n - 1;
//                long long sum = (long long)target - nums[i] - nums[j];
//                while (left < right)
//                {
//                    int mysum = nums[left] + nums[right];
//                    if (mysum < sum) left++;
//                    else if (mysum > sum) right--;
//                    else
//                    {
//                        ret.push_back({ nums[i],nums[j],nums[left],nums[right] });
//                        left++, right--;
//                        while (left < right && nums[left] == nums[left - 1]) left++;
//                        while (left < right && nums[right] == nums[right + 1]) right--;
//                    }
//                }
//                j++;
//                while (j < n && nums[j] == nums[j - 1]) j++;
//            }
//            i++;
//            while (i < n && nums[i] == nums[i - 1]) i++;
//        }
//        return ret;
//    }
//};



//class Solution {
//public:
//    int minSubArrayLen(int target, vector<int>& nums) {
//        int sum = 0, ret = INT_MAX;
//        for (int left = 0, right = 0; right < nums.size(); ++right)
//        {
//            sum += nums[right];
//            while (sum >= target)
//            {
//                ret = min(ret, right - left + 1);
//                sum -= nums[left];
//                left++;
//            }
//        }
//        return ret == INT_MAX ? 0 : ret;
//    }
//};



//class Solution
//{
//public:
//	int lengthOfLongestSubstring(string s)
//	{
//		int hash[128] = { 0 }; // 使⽤数组来模拟哈希表
//		int left = 0, right = 0, n = s.size();
//		int ret = 0;
//		while (right < n)
//		{
//			hash[s[right]]++; // 进⼊窗⼝
//			while (hash[s[right]] > 1) // 判断
//				hash[s[left++]]--; // 出窗⼝
//			ret = max(ret, right - left + 1); // 更新结果
//			right++; // 让下⼀个元素进⼊窗⼝
//		}
//		return ret;
//	}
//};
//int main()
//{
//	string s = "deabcabca";
//	Solution ret;
//	cout << ret.lengthOfLongestSubstring(s) << endl;
//}



//class Solution {
//public:
//    int lengthOfLongestSubstring(string s)
//    {
//        int left = 0, right = 0, n = s.size();
//        int ret = 0;
//        int hash[128] = { 0 };
//        while (right < n)
//        {
//            hash[s[right]]++;
//            while (hash[s[right]] > 1) hash[s[left++]]--;
//            ret = max(ret, right - left + 1);
//            right++;
//        }
//        return ret;
//    }
//};



//class Solution {
//public:
//    int longestOnes(vector<int>& nums, int k)
//    {
//        int ret = 0;
//        int left = 0, right = 0, zero = 0;
//        int n = nums.size();
//        while (right < n)
//        {
//            if (nums[right] == 0) zero++; // 进窗口
//            while (zero > k)
//            {
//                if (nums[left++] == 0) zero--; // 出窗口
//            }
//            ret = max(ret, right - left + 1);// 更新结果
//            right++;
//        }
//        return ret;
//    }
//};




//class Solution
//{
//public:
//	int minOperations(vector<int>& nums, int x)
//	{
//		int sum = 0;
//		for (int a : nums) sum += a;
//		int target = sum - x;
//		// 细节问题
//		if (target < 0) return -1;
//		int ret = -1;
//		for (int left = 0, right = 0, tmp = 0; right < nums.size(); right++)
//		{
//			tmp += nums[right]; // 进窗⼝
//			while (tmp > target) // 判断
//				tmp -= nums[left++]; // 出窗⼝
//			if (tmp == target) // 更新结果
//				ret = max(ret, right - left + 1);
//		}
//		if (ret == -1) return ret;
//		else return nums.size() - ret;
//	}
//};
#include<unordered_map>



//class Solution {
//public:
//    int totalFruit(vector<int>& fruits)
//    {
//        int ret = 0;
//        unordered_map<int, int> hash;
//        for (int left = 0, right = 0; right < fruits.size(); right++)
//        {
//            // 进窗口
//            hash[fruits[right]]++;
//            // 判断+出窗口
//            while (hash.size() > 2)
//            {
//                hash[fruits[left]]--;
//                if (hash[fruits[left]] == 0) hash.erase(fruits[left]);
//                left++;
//            }
//            ret = max(ret, right - left + 1);
//        }
//        return ret;
//    }
//};
//
//
//// 数组优化版本
//class Solution
//{
//public:
//    int totalFruit(vector<int>& f)
//    {
//        int hash[100001] = { 0 }; // 统计窗⼝内出现了多少种⽔果
//        int ret = 0;
//        for (int left = 0, right = 0, kinds = 0; right < f.size(); right++)
//        {
//            if (hash[f[right]] == 0) kinds++; // 维护⽔果的种类
//            hash[f[right]]++; // 进窗⼝
//            while (kinds > 2) // 判断
//            {
//                // 出窗⼝
//                hash[f[left]]--;
//                if (hash[f[left]] == 0) kinds--;
//                left++;
//            }
//            ret = max(ret, right - left + 1);
//        }
//        return ret;
//    }
//};


//class Solution
//{
//public:
//	vector<int> findAnagrams(string s, string p)
//	{
//		vector<int> ret;
//		int hash1[26] = { 0 }; // 统计字符串 p 中每个字符出现的个数
//		for (auto ch : p) hash1[ch - 'a']++;
//		int hash2[26] = { 0 }; // 统计窗⼝⾥⾯的每⼀个字符出现的个数
//		int m = p.size();
//		for (int left = 0, right = 0, count = 0; right < s.size(); right++)
//		{
//			char in = s[right];
//			// 进窗⼝ + 维护 count
//			hash2[in - 'a']++;
//			if (hash2[in - 'a'] <= hash1[in - 'a']) count++;
//			if (right - left + 1 > m) // 判断
//			{
//				char out = s[left++];
//				// 出窗⼝ + 维护 count
//				if (hash2[out - 'a'] <= hash1[out - 'a']) count--;
//				hash2[out - 'a']--;
//			}
//			// 更新结果
//			if (count == m) ret.push_back(left);
//		}
//		return ret;
//	}
//};



//class Solution
//{
//public:
//	vector<int> findSubstring(string s, vector<string>& words)
//	{
//		vector<int> ret;
//		unordered_map<string, int> hash1; // 保存 words ⾥⾯所有单词的频次
//		for (auto& s : words) hash1[s]++;
//		int len = words[0].size(), m = words.size();
//		for (int i = 0; i < len; i++) // 执⾏ len 次
//		{
//			unordered_map<string, int> hash2; // 维护窗⼝内单词的频次
//			for (int left = i, right = i, count = 0; right + len <= s.size();
//				right += len)
//			{
//				// 进窗⼝ + 维护 count
//				string in = s.substr(right, len);
//				hash2[in]++;
//				if (hash1.count(in) && hash2[in] <= hash1[in]) count++;
//				// 判断
//				if (right - left + 1 > len * m)
//				{
//					// 出窗⼝ + 维护 count
//					string out = s.substr(left, len);
//					if (hash1.count(out) && hash2[out] <= hash1[out]) count--;
//					hash2[out]--;
//					left += len;
//				}
//				// 更新结果
//				if (count == m) ret.push_back(left);
//			}
//		}
//		return ret;
//	}
//};



//class Solution
//{
//public:
//	string minWindow(string s, string t)
//	{
//		int hash1[128] = { 0 }; // 统计字符串 t 中每⼀个字符的频次
//		int kinds = 0; // 统计有效字符有多少种
//		for (auto ch : t)
//			if (hash1[ch]++ == 0) kinds++;
//		int hash2[128] = { 0 }; // 统计窗⼝内每个字符的频次
//		int minlen = INT_MAX, begin = -1;
//		for (int left = 0, right = 0, count = 0; right < s.size(); right++)
//		{
//			char in = s[right];
//			if (++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count
//			while (count == kinds) // 判断条件
//			{
//				if (right - left + 1 < minlen) // 更新结果
//				{
//					minlen = right - left + 1;
//					begin = left;
//				}
//				char out = s[left++];
//				if (hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count
//			}
//		}
//		if (begin == -1) return "";
//		else return s.substr(begin, minlen);
//	}
//};
#include<algorithm>



//class Solution {
//public:
//    int search(vector<int>& nums, int target)
//    {
//        sort(nums.begin(), nums.end());
//        int left = 0, right = nums.size() - 1;
//        while (left <= right)
//        {
//            int mid = left + (right - left) / 2;
//            if (nums[mid] < target) left = mid + 1;
//            else if (nums[mid] > target) right = mid - 1;
//            else return mid;
//        }
//        return -1;
//    }
//};



//class Solution {
//public:
//    vector<int> searchRange(vector<int>& nums, int target)
//    {
//        int left = 0, right = nums.size() - 1;
//        if (nums.size() == 0) return { -1,-1 };
//        // 先查左端点
//        int begin = 0;
//        while (left < right)
//        {
//            int mid = left + (right - left) / 2;
//            if (nums[mid] < target) left = mid + 1;
//            else  right = mid;
//        }
//        if (nums[left] != target) return { -1,-1 };
//        else begin = left;
//        // 再查右端点
//        left = 0, right = nums.size() - 1;
//        while (left < right)
//        {
//            int mid = left + (right - left + 1) / 2;
//            if (nums[mid] <= target) left = mid;
//            else  right = mid - 1;
//        }
//        return { begin,right };
//    }
//};



//// 方法一:暴力枚举
//class Solution {
//public:
//	int mySqrt(int x) {
//		// 由于两个较⼤的数相乘可能会超过 int 最⼤范围
//		// 因此⽤ long long
//		long long i = 0;
//		for (i = 0; i <= x; i++)
//		{
//			// 如果两个数相乘正好等于 x,直接返回 i
//			if (i * i == x) return i;
//			// 如果第⼀次出现两个数相乘⼤于 x,说明结果是前⼀个数
//			if (i * i > x) return i - 1;
//		}
//		// 为了处理oj题需要控制所有路径都有返回值
//		return -1;
//	}
//};
//
//
//
//// 方法二:二分
//class Solution
//{
//public:
//	int mySqrt(int x)
//	{
//		if (x < 1) return 0; // 处理边界情况
//		int left = 1, right = x;
//		while (left < right)
//		{
//			long long mid = left + (right - left + 1) / 2; // 防溢出
//			if (mid * mid <= x) left = mid;
//			else right = mid - 1;
//		}
//		return left;
//	}
//};



//class Solution {
//public:
//    int searchInsert(vector<int>& nums, int target)
//    {
//        int left = 0, right = nums.size() - 1;
//        while (left < right)
//        {
//            int mid = left + (right - left) / 2;
//            if (nums[mid] < target) left = mid + 1;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
//            else right = mid;
//        }
//        if (nums[left] < target) return left + 1;
//        return left;
//    }
//};



//class Solution {
//public:
//    int peakIndexInMountainArray(vector<int>& arr)
//    {
//        // 第一个和最后一个一定不是山峰
//        int left = 1, right = arr.size() - 2;
//        while (left < right)
//        {
//            int mid = left + (right - left + 1) / 2;
//            if (arr[mid] < arr[mid - 1]) right = mid - 1;
//            else left = mid;
//        }
//        return left;
//    }
//};



//class Solution {
//public:
//    int findPeakElement(vector<int>& nums)
//    {
//        int left = 0, right = nums.size() - 1;
//        while (left < right)
//        {
//            int mid = left + (right - left) / 2;
//            if (nums[mid] > nums[mid + 1]) right = mid;
//            else left = mid + 1;
//        }
//        return left;
//    }
//};


//class Solution {
//public:
//    int findMin(vector<int>& nums)
//    {
//        int left = 0, right = nums.size() - 1;
//        int x = nums[right];
//        while (left < right)
//        {
//            int mid = left + (right - left) / 2;
//            if (nums[mid] < x) right = mid;
//            else left = mid + 1;
//        }
//        return nums[left];
//    }
//};



//class Solution {
//public:
//    int takeAttendance(vector<int>& records)
//    {
//        int left = 0, right = records.size() - 1;
//        while (left < right)
//        {
//            int mid = left + (right - left) / 2;
//            if (records[mid] == mid) left = mid + 1;
//            else right = mid;
//        }
//        return records[left] == left ? left + 1 : left;
//    }
//};
//int main()
//{
//    Solution s;
//    vector<int> ret = { 0,1,3,4,5 };
//    cout << s.takeAttendance(ret) << endl;
//    return 0;
//}



//#include<iostream>
//#include<vector>
//using namespace std;
//int main()
//{
//    int n, q;
//    cin >> n >> q;
//    // 1. 读入数据
//    vector<int> arr(n + 1);
//    for (int i = 1; i <= n; i++)
//    {
//        cin >> arr[i];
//    }
//    // 2.预处理出来一个前缀和数组
//    vector<long long> dp(n + 1);
//    for (int i = 0; i <= n; i++) dp[i] = dp[i - 1] + arr[i];
//
//    // 3.使用前缀和数组
//    int left = 0, right = 0;
//    while (q--)
//    {
//        cin >> left >> right;
//        cout << dp[right] - dp[left - 1] << endl;
//    }
//    return 0;
//}



//#include<iostream>
//#include<vector>
//using namespace std;
//int main()
//{
//    // 1. 读入数据
//    int n = 0, m = 0, q = 0;
//    cin >> n >> m >> q;
//    vector<vector<int>> arr(n + 1, vector<int>(m + 1));
//    for (int i = 1; i <= n; i++)
//        for (int j = 1; j <= m; j++)
//            cin >> arr[i][j];
//    // 2. 预处理前缀和矩阵
//    vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
//    for (int i = 1; i <= n; i++)
//        for (int j = 1; j <= m; j++)
//            dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1];
//    // 3. 使用前缀和矩阵
//    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
//    while (q--)
//    {
//        cin >> x1 >> y1 >> x2 >> y2;
//        cout << dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1] << endl;
//    }
//}



//class Solution {
//public:
//    int pivotIndex(vector<int>& nums)
//    {
//        int n = nums.size();
//        vector<int> f(n), g(n);
//        // 1.预处理前缀和两个数组
//        for (int i = 1; i < n; i++)
//            f[i] = f[i - 1] + nums[i - 1];
//        for (int i = n - 2; i >= 0; i--)
//            g[i] = g[i + 1] + nums[i + 1];
//        // 2.判断两个数组是否相同
//        for (int i = 0; i < n; i++)
//            if (f[i] == g[i])
//                return i;
//        return -1;
//    }
//};





//class Solution {
//public:
//    vector<int> productExceptSelf(vector<int>& nums)
//    {
//        int n = nums.size();
//        vector<int> f(n), g(n);
//        // 1.先预处理一下前缀积两个数组
//        f[0] = g[n - 1] = 1;
//        for (int i = 1; i < n; i++)
//            f[i] = f[i - 1] * nums[i - 1];
//        for (int i = n - 2; i >= 0; i--)
//            g[i] = g[i + 1] * nums[i + 1];
//        // 2.判断并且使用
//        vector<int> ret(n);
//        for (int i = 0; i < n; i++)
//        {
//            ret[i] = f[i] * g[i];
//        }
//        return ret;
//    }
//};


//class Solution {
//public:
//    int subarraySum(vector<int>& nums, int k)
//    {
//        unordered_map<int, int> hash;
//        hash[0] = 1;
//        int sum = 0, ret = 0;
//        for (auto x : nums)
//        {
//            sum += x;
//            if (hash.count(sum - k))
//                ret += hash[sum - k]; // 统计个数
//            hash[sum]++;
//        }
//        return ret;
//    }
//};



//class Solution {
//public:
//    int subarraysDivByK(vector<int>& nums, int k)
//    {
//        unordered_map<int, int> hash;
//        hash[0 % k] = 1; // 0 这个数的余数
//        int sum = 0, ret = 0;
//        for (auto x : nums)
//        {
//            sum += x;                  // 算出当前位置的前缀和
//            int r = (sum % k + k) % k; // 修正后的余数
//            if (hash.count(r))
//                ret += hash[r]; // 统计结果
//            hash[r]++;
//        }
//        return ret;
//    }
//};
//
//
//class Solution
//{
//public:
//    int findMaxLength(vector<int>& nums)
//    {
//        unordered_map<int, int> hash;
//        hash[0] = -1; // 默认有⼀个前缀和为 0 的情况
//        int sum = 0, ret = 0;
//        for (int i = 0; i < nums.size(); i++)
//        {
//            sum += nums[i] == 0 ? -1 : 1; // 计算当前位置的前缀和
//            if (hash.count(sum)) ret = max(ret, i - hash[sum]);
//            else hash[sum] = i;
//        }
//        return ret;
//    }
//};



//class Solution {
//public:
//	vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
//		int m = mat.size(), n = mat[0].size();
//		vector<vector<int>> dp(m + 1, vector<int>(n + 1));
//		// 1. 预处理前缀和矩阵
//		for (int i = 1; i <= m; i++)
//			for (int j = 1; j <= n; j++)
//				dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] +
//				mat[i - 1][j - 1];
//
//		// 2. 使⽤
//		vector<vector<int>> ret(m, vector<int>(n));
//		for (int i = 0; i < m; i++)
//			for (int j = 0; j < n; j++)
//			{
//				int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;
//				int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;
//				ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] +
//					dp[x1 - 1][y1 - 1];
//			}
//
//		return ret;
//	}
//};



//class Solution {
//public:
//    bool isUnique(string astr) {
//        // 鹊巢原理优化
//        if (astr.size() > 26)
//            return false;
//        // 利用位图的思想求解
//        int bitMap = 0;
//        for (auto ch : astr)
//        {
//            int i = ch - 'a';
//            // 先判断字符是否已经出现过
//            if (((bitMap >> i) & 1) == 1)
//                return false;
//            // 把当前字符加⼊到位图中
//            bitMap |= 1 << i;
//        }
//        return true;
//    }
//};


//class Solution
//{
//public:
//	int missingNumber(vector<int>& nums)
//	{
//		int ret = 0;
//		for (auto x : nums) ret ^= x;
//		for (int i = 0; i <= nums.size(); i++) ret ^= i;
//		return ret;
//	}
//};
//int main()
//{
//	vector<int> ret = { 3,0,1 };
//	Solution s;
//	cout << s.missingNumber(ret) << endl;
//}
//
//
//class Solution
//{
//public:
//	int getSum(int a, int b)
//	{
//		while (b != 0)
//		{
//			int x = a ^ b; // 先算出⽆进位相加的结果
//			unsigned int carry = (unsigned int)(a & b) << 1; // 算出进位
//			a = x;
//			b = carry;
//		}
//		return a;
//	}
//};


//class Solution
//{
//public:
//	int singleNumber(vector<int>& nums)
//	{
//		int ret = 0;
//		for (int i = 0; i < 32; i++) // 依次去修改 ret 中的每⼀位
//		{
//			int sum = 0;
//			for (int x : nums) // 计算nums中所有的数的第 i 位的和
//				if (((x >> i) & 1) == 1)
//					sum++;
//			sum %= 3;
//			if (sum == 1) ret |= 1 << i;
//		}
//		return ret;
//	}
//};


//
//class Solution
//{
//public:
//	vector<int> missingTwo(vector<int>& nums)
//	{
//		// 1. 将所有的数异或在⼀起
//		int tmp = 0;
//		for (auto x : nums) tmp ^= x;
//		for (int i = 1; i <= nums.size() + 2; i++) tmp ^= i;
//		// 2. 找出 a,b 中⽐特位不同的那⼀位
//		int diff = 0;
//		while (1)
//		{
//			if (((tmp >> diff) & 1) == 1) break;
//			else diff++;
//		}
//		// 3. 根据 diff 位的不同,将所有的数划分为两类来异或
//		int a = 0, b = 0;
//		for (int x : nums)
//			if (((x >> diff) & 1) == 1) b ^= x;
//			else a ^= x;
//		for (int i = 1; i <= nums.size() + 2; i++)
//			if (((i >> diff) & 1) == 1) b ^= i;
//			else a ^= i;
//		return { a, b };
//	}
//};



//class Solution
//{
//public:
//	string modifyString(string s)
//	{
//		int n = s.size();
//		for (int i = 0; i < n; i++)
//		{
//			if (s[i] == '?') // 替换
//			{
//				for (char ch = 'a'; ch <= 'z'; ch++)
//				{
//					if ((i == 0 || ch != s[i - 1]) && (i == n - 1 || ch != s[i+ 1]))
//					{
//						s[i] = ch;
//						break;
//					}
//				}
//			}
//		}
//		return s;
//	}
//};


//class Solution {
//public:
//    int findPoisonedDuration(vector<int>& timeSeries, int duration)
//    {
//        int ret = 0;
//        for (int i = 1; i < timeSeries.size(); i++)
//        {
//            int x = timeSeries[i] - timeSeries[i - 1];
//            if (x >= duration) ret += duration;
//            else ret += x;
//        }
//        return ret + duration;
//    }
//};



//class Solution
//{
//public:
//	string convert(string s, int numRows)
//	{
//		// 处理边界情况
//		if (numRows == 1) return s;
//		string ret;
//		int d = 2 * numRows - 2, n = s.size();
//		// 1. 先处理第⼀⾏
//		for (int i = 0; i < n; i += d)
//			ret += s[i];
//		// 2. 处理中间⾏
//		for (int k = 1; k < numRows - 1; k++) // 枚举每⼀⾏
//		{
//			for (int i = k, j = d - k; i < n || j < n; i += d, j += d)
//			{
//				if (i < n) ret += s[i];
//				if (j < n) ret += s[j];
//			}
//		}
//		// 3. 处理最后⼀⾏
//		for (int i = numRows - 1; i < n; i += d)
//			ret += s[i];
//		return ret;
//	}
//};


//class Solution
//{
//public:
//	string countAndSay(int n)
//	{
//		string ret = "1";
//		for (int i = 1; i < n; i++) // 解释 n - 1 次 ret 即可
//		{
//			string tmp;
//			int len = ret.size();
//			for (int left = 0, right = 0; right < len; )
//			{
//				while (right < len && ret[left] == ret[right]) right++;
//				tmp += to_string(right - left) + ret[left];
//				left = right;
//			}
//			ret = tmp;
//		}
//		return ret;
//	}
//};





//class Solution
//{
//public:
//	int minNumberOfFrogs(string croakOfFrogs)
//	{
//		string t = "croak";
//		int n = t.size();
//		vector<int> hash(n); // ⽤数组来模拟哈希表
//		unordered_map<char, int> index; //[x, x这个字符对应的下标]
//		for (int i = 0; i < n; i++)
//			index[t[i]] = i;
//
//		for (auto ch : croakOfFrogs)
//		{
//			if (ch == 'c')
//			{
//				if (hash[n - 1] != 0) hash[n - 1]--;
//				hash[0]++;
//			}
//			else
//			{
//				int i = index[ch];
//				if (hash[i - 1] == 0) return -1;
//				hash[i - 1]--; hash[i]++;
//			}
//		}
//		for (int i = 0; i < n - 1; i++)
//			if (hash[i] != 0)
//				return -1;
//		return hash[n - 1];
//	}
//};




//class Solution {
//public:
//    void sortColors(vector<int>& nums)
//    {
//        int left = -1, right = nums.size(), i = 0;
//        while (i < right)
//        {
//            if (nums[i] == 0) swap(nums[++left], nums[i++]);
//            else if (nums[i] == 2) swap(nums[--right], nums[i]);
//            else i++;
//        }
//    }
//};



//class Solution
//{
//public:
//	vector<int> sortArray(vector<int>& nums)
//	{
//		srand(time(NULL)); // 种下⼀个随机数种⼦
//		qsort(nums, 0, nums.size() - 1);
//		return nums;
//	}
//	// 快排
//	void qsort(vector<int>& nums, int l, int r)
//	{
//		if (l >= r) return;
//		// 数组分三块
//		int key = getRandom(nums, l, r);
//		int i = l, left = l - 1, right = r + 1;
//		while (i < right)
//		{
//			if (nums[i] < key) swap(nums[++left], nums[i++]);
//			else if (nums[i] == key) i++;
//			else swap(nums[--right], nums[i]);
//		}
//		// [l, left] [left + 1, right - 1] [right, r]
//		qsort(nums, l, left);
//		qsort(nums, right, r);
//	}
//	int getRandom(vector<int>& nums, int left, int right)
//	{
//		int r = rand();
//		return nums[r % (right - left + 1) + left];
//	}
//};



//class Solution
//{
//public:
//	int findKthLargest(vector<int>& nums, int k)
//	{
//		srand(time(NULL));
//		return qsort(nums, 0, nums.size() - 1, k);
//	}
//	int qsort(vector<int>& nums, int l, int r, int k)
//	{
//		if (l == r) return nums[l];
//		// 1. 随机选择基准元素
//		int key = getRandom(nums, l, r);
//		// 2. 根据基准元素将数组分三块
//		int left = l - 1, right = r + 1, i = l;
//		while (i < right)
//		{
//			if (nums[i] < key) swap(nums[++left], nums[i++]);
//			else if (nums[i] == key) i++;
//			else swap(nums[--right], nums[i]);
//		}
//		// 3. 分情况讨论
//		int c = r - right + 1, b = right - left - 1;
//		if (c >= k) return qsort(nums, right, r, k);
//		else if (b + c >= k) return key;
//		else return qsort(nums, l, left, k - b - c);
//	}
//	int getRandom(vector<int>& nums, int left, int right)
//	{
//		return nums[rand() % (right - left + 1) + left];
//	}
//};




//class Solution {
//public:
//	vector<int> getLeastNumbers(vector<int>& nums, int k)
//	{
//		srand(time(NULL));
//		qsort(nums, 0, nums.size() - 1, k);
//		return { nums.begin(), nums.begin() + k };
//	}
//	void qsort(vector<int>& nums, int l, int r, int k)
//	{
//		if (l >= r) return;
//		// 1. 随机选择⼀个基准元素 + 数组分三块
//		int key = getRandom(nums, l, r);
//		int left = l - 1, right = r + 1, i = l;
//		while (i < right)
//		{
//			if (nums[i] < key) swap(nums[++left], nums[i++]);
//			else if (nums[i] == key) i++;
//			else swap(nums[--right], nums[i]);
//		}
//		// [l, left][left + 1, right - 1] [right, r]
//		// 2. 分情况讨论
//		int a = left - l + 1, b = right - left - 1;
//		if (a > k) qsort(nums, l, left, k);
//		else if (a + b >= k) return;
//		else qsort(nums, right, r, k - a - b);
//	}
//	int getRandom(vector<int>& nums, int l, int r)
//	{
//		return nums[rand() % (r - l + 1) + l];
//	}
//};


//class Solution
//{
//	vector<int> tmp;
//public:
//	vector<int> sortArray(vector<int>& nums)
//	{
//		tmp.resize(nums.size());
//		mergeSort(nums, 0, nums.size() - 1);
//		return nums;
//	}
//	void mergeSort(vector<int>& nums, int left, int right)
//	{
//		if (left >= right) return;
//		// 1. 选择中间点划分区间
//		int mid = (left + right) >> 1;
//		// [left, mid] [mid + 1, right]
//		// 2. 把左右区间排序
//		mergeSort(nums, left, mid);
//		mergeSort(nums, mid + 1, right);
//		// 3. 合并两个有序数组
//		int cur1 = left, cur2 = mid + 1, i = 0;
//		while (cur1 <= mid && cur2 <= right)
//			tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] :
//			nums[cur2++];
//		// 处理没有遍历完的数组
//		while (cur1 <= mid) tmp[i++] = nums[cur1++];
//		while (cur2 <= right) tmp[i++] = nums[cur2++];
//		// 4. 还原
//		for (int i = left; i <= right; i++)
//			nums[i] = tmp[i - left];
//	}
//};




//class Solution
//{
//	int tmp[50010];
//public:
//	int reversePairs(vector<int>& nums)
//	{
//		return mergeSort(nums, 0, nums.size() - 1);
//	}
//	int mergeSort(vector<int>& nums, int left, int right)
//	{
//		if (left >= right) return 0;
//		int ret = 0;
//		// 1. 找中间点,将数组分成两部分
//		int mid = (left + right) >> 1;
//		// [left, mid][mid + 1, right]
//		// 2. 左边的个数 + 排序 + 右边的个数 + 排序
//		ret += mergeSort(nums, left, mid);
//		ret += mergeSort(nums, mid + 1, right);
//		// 3. ⼀左⼀右的个数
//		int cur1 = left, cur2 = mid + 1, i = 0;
//		while (cur1 <= mid && cur2 <= right) // 升序的时候
//		{
//			if (nums[cur1] <= nums[cur2])
//			{
//				tmp[i++] = nums[cur1++];
//			}
//			else
//			{
//				ret += mid - cur1 + 1;
//				tmp[i++] = nums[cur2++];
//			}
//		}
//		// 4. 处理⼀下排序
//		while (cur1 <= mid) tmp[i++] = nums[cur1++];
//		while (cur2 <= right) tmp[i++] = nums[cur2++];
//		for (int j = left; j <= right; j++)
//			nums[j] = tmp[j - left];
//		return ret;
//	}
//};



//class Solution
//{
//	vector<int> ret;
//	vector<int> index; // 记录 nums 中当前元素的原始下标
//	int tmpNums[500010];
//	int tmpIndex[500010];
//public:
//	vector<int> countSmaller(vector<int>& nums)
//	{
//		int n = nums.size();
//		ret.resize(n);
//		index.resize(n);
//		// 初始化⼀下 index 数组
//		for (int i = 0; i < n; i++)
//			index[i] = i;
//		mergeSort(nums, 0, n - 1);
//		return ret;
//	}
//
//	void mergeSort(vector<int>& nums, int left, int right)
//	{
//		if (left >= right) return;
//		// 1. 根据中间元素,划分区间
//		int mid = (left + right) >> 1;
//		// [left, mid] [mid + 1, right]
//		// 2. 先处理左右两部分
//		mergeSort(nums, left, mid);
//		mergeSort(nums, mid + 1, right);
//		// 3. 处理⼀左⼀右的情况
//		int cur1 = left, cur2 = mid + 1, i = 0;
//		while (cur1 <= mid && cur2 <= right) // 降序
//		{
//			if (nums[cur1] <= nums[cur2])
//			{
//				tmpNums[i] = nums[cur2];
//				tmpIndex[i++] = index[cur2++];
//			}
//			else
//			{
//				ret[index[cur1]] += right - cur2 + 1; // 重点
//				tmpNums[i] = nums[cur1];
//				tmpIndex[i++] = index[cur1++];
//			}
//		}
//		// 4. 处理剩下的排序过程
//		while (cur1 <= mid)
//		{
//			tmpNums[i] = nums[cur1];
//			tmpIndex[i++] = index[cur1++];
//		}
//		while (cur2 <= right)
//		{
//			tmpNums[i] = nums[cur2];
//			tmpIndex[i++] = index[cur2++];
//		}
//		for (int j = left; j <= right; j++)
//		{
//			nums[j] = tmpNums[j - left];
//			index[j] = tmpIndex[j - left];
//		}
//	}
//};



//class Solution
//{
//	int tmp[50010];
//public:
//	int reversePairs(vector<int>& nums)
//	{
//		return mergeSort(nums, 0, nums.size() - 1);
//	}
//	int mergeSort(vector<int>& nums, int left, int right)
//	{
//		if (left >= right) return 0;
//		int ret = 0;
//		// 1. 先根据中间元素划分区间
//		int mid = (left + right) >> 1;
//		// [left, mid] [mid + 1, right]
//		// 2. 先计算左右两侧的翻转对
//		ret += mergeSort(nums, left, mid);
//		ret += mergeSort(nums, mid + 1, right);
//		// 3. 先计算翻转对的数量
//		int cur1 = left, cur2 = mid + 1, i = left;
//		while (cur1 <= mid) // 降序的情况
//		{
//			while (cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
//			if (cur2 > right)
//				break;
//			ret += right - cur2 + 1;
//			cur1++;
//		}
//		// 4. 合并两个有序数组
//		cur1 = left, cur2 = mid + 1;
//		while (cur1 <= mid && cur2 <= right)
//			tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];
//		while (cur1 <= mid) tmp[i++] = nums[cur1++];
//		while (cur2 <= right) tmp[i++] = nums[cur2++];
//		for (int j = left; j <= right; j++)
//			nums[j] = tmp[j];
//
//		return ret;
//	}
//};

//struct ListNode
//{
//    int val;
//    ListNode* next;
//};


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* cur1 = l1, * cur2 = l2;
//        ListNode* newhead = new ListNode(0);//创建虚拟头节点,记录最终结果
//        ListNode* prev = newhead;// 尾指针
//        int t = 0;// 进位
//        while (cur1 || cur2 || t)
//        {
//            if (cur1)
//            {
//                t += cur1->val;
//                cur1 = cur1->next;
//            }
//            if (cur2)
//            {
//                t += cur2->val;
//                cur2 = cur2->next;
//            }
//            prev->next = new ListNode(t % 10);
//            prev = prev->next;
//            t /= 10;
//        }
//        prev = newhead->next;
//        delete newhead;
//        return prev;
//    }
//};



//class Solution
//{
//public:
//    ListNode* swapPairs(ListNode* head)
//    {
//        if (head == nullptr || head->next == nullptr) return head;
//        ListNode* newHead = new ListNode(0);
//        newHead->next = head;
//        ListNode* prev = newHead, * cur = prev->next, * next = cur->next, * nnext
//            = next->next;
//        while (cur && next)
//        {
//            // 交换结点
//            prev->next = next;
//            next->next = cur;
//            cur->next = nnext;
//            // 修改指针
//            prev = cur; // 注意顺序
//            cur = nnext;
//            if (cur) next = cur->next;
//            if (next) nnext = next->next;
//        }
//        cur = newHead->next;
//        delete newHead;
//        return cur;
//    }
//};



/**
 * 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:
//    void reorderList(ListNode* head)
//    {
//        // 处理边界情况
//        if (head == nullptr || head->next == nullptr || head->next->next ==
//            nullptr) return;
//        // 1. 找到链表的中间节点 - 快慢双指针(⼀定要画图考虑 slow 的落点在哪⾥)
//        ListNode* slow = head, * fast = head;
//        while (fast && fast->next)
//        {
//            slow = slow->next;
//            fast = fast->next->next;
//        }
//        // 2. 把 slow 后⾯的部分给逆序 - 头插法
//        ListNode* head2 = new ListNode(0);
//        ListNode* cur = slow->next;
//        slow->next = nullptr; // 注意把两个链表给断开
//        while (cur)
//        {
//            ListNode* next = cur->next;
//            cur->next = head2->next;
//            head2->next = cur;
//            cur = next;
//        }
//        // 3. 合并两个链表 - 双指针
//        ListNode* ret = new ListNode(0);
//        ListNode* prev = ret;
//        ListNode* cur1 = head, * cur2 = head2->next;
//        while (cur1)
//        {
//            // 先放第⼀个链表
//            prev->next = cur1;
//            cur1 = cur1->next;
//            prev = prev->next;
//            // 再放第⼆个链表
//            if (cur2)
//            {
//                prev->next = cur2;
//                prev = prev->next;
//                cur2 = cur2->next;
//            }
//        }
//        delete head2;
//        delete ret;
//    }
//};


#include <queue>
/**
 * 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
//{
//    struct cmp
//    {
//        bool operator()(const ListNode* l1, const ListNode* l2)
//        {
//            return l1->val > l2->val;
//        }
//    };
//public:
//    ListNode* mergeKLists(vector<ListNode*>& lists)
//    {
//        // 创建⼀个⼩根堆
//        priority_queue<ListNode*, vector<ListNode*>, cmp> heap;
//        // 让所有的头结点进⼊⼩根堆
//        for (auto l : lists)
//            if (l) heap.push(l);
//
//        // 合并 k 个有序链表
//        ListNode* ret = new ListNode(0);
//        ListNode* prev = ret;
//        while (!heap.empty())
//        {
//            ListNode* t = heap.top();
//            heap.pop();
//            prev->next = t;
//            prev = t;
//            if (t->next) heap.push(t->next);
//        }
//        prev = ret->next;
//        delete ret;
//        return prev;
//    }
//};




//// 递归
//class Solution
//{
//public:
//    ListNode* mergeKLists(vector<ListNode*>& lists)
//    {
//        return merge(lists, 0, lists.size() - 1);
//    }
//    ListNode* merge(vector<ListNode*>& lists, int left, int right)
//    {
//        if (left > right) return nullptr;
//        if (left == right) return lists[left];
//        // 1. 平分数组
//        int mid = left + right >> 1;
//        // [left, mid] [mid + 1, right]
//        // 2. 递归处理左右区间
//        ListNode* l1 = merge(lists, left, mid);
//        ListNode* l2 = merge(lists, mid + 1, right);
//        // 3. 合并两个有序链表
//        return mergeTowList(l1, l2);
//    }
//    ListNode* mergeTowList(ListNode* l1, ListNode* l2)
//    {
//        if (l1 == nullptr) return l2;
//        if (l2 == nullptr) return l1;
//        // 合并两个有序链表
//        ListNode head;
//        ListNode* cur1 = l1, * cur2 = l2, * prev = &head;
//        head.next = nullptr;
//        while (cur1 && cur2)
//        {
//            if (cur1->val <= cur2->val)
//            {
//                prev = prev->next = cur1;
//                cur1 = cur1->next;
//            }
//            else
//            {
//                prev = prev->next = cur2;
//                cur2 = cur2->next;
//            }
//        }
//        if (cur1) prev->next = cur1;
//        if (cur2) prev->next = cur2;
//        return head.next;
//    }
//};



//class Solution
//{
//public:
//    ListNode* reverseKGroup(ListNode* head, int k)
//    {
//        // 1. 先求出需要逆序多少组
//        int n = 0;
//        ListNode* cur = head;
//        while (cur)
//        {
//            cur = cur->next;
//            n++;
//        }
//        n /= k;
//        // 2. 重复 n 次:⻓度为 k 的链表的逆序即可
//        ListNode* newHead = new ListNode(0);
//        ListNode* prev = newHead;
//        cur = head;
//        for (int i = 0; i < n; i++)
//        {
//            ListNode* tmp = cur;
//            for (int j = 0; j < k; j++)
//            {
//                ListNode* next = cur->next;
//                cur->next = prev->next;
//                prev->next = cur;
//                cur = next;
//            }
//            prev = tmp;
//        }
//        // 把不需要翻转的接上
//        prev->next = cur;
//        cur = newHead->next;
//        delete newHead;
//        return cur;
//    }
//};



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



//class Solution {
//public:
//    bool CheckPermutation(string s1, string s2)
//    {
//        if (s1.size() != s2.size()) return false;
//        int hash[26] = { 0 };
//        // 先统计第一个字符串的信息
//        for (auto x : s1) hash[x - 'a']++;
//        // 再扫描第二个字符串,看看是否能重排
//        for (auto x : s2)
//        {
//            hash[x - 'a']--;
//            if (hash[x - 'a'] < 0) return false;
//        }
//        return true;
//    }
//};
#include<unordered_set>

//class Solution {
//public:
//    bool containsDuplicate(vector<int>& nums)
//    {
//        unordered_set<int> hash;
//        for (auto x : nums)
//        {
//            if (hash.count(x)) return true;
//            else hash.insert(x);
//        }
//        return false;
//    }
//};z





//class Solution
//{
//public:
//    bool containsNearbyDuplicate(vector<int>& nums, int k)
//    {
//        unordered_map<int, int> hash;
//        for (int i = 0; i < nums.size(); i++)
//        {
//            if (hash.count(nums[i]))
//            {
//                if (i - hash[nums[i]] <= k) return true;
//            }
//            hash[nums[i]] = i;
//        }
//        return false;
//    }
//};
#include<vector>
//int x, y;

//class Solution {
//public:
//    vector<vector<string>> groupAnagrams(vector<string>& strs)
//    {
//        unordered_map<string, vector<string>> hash;
//        // 1.把所有的字母异位词分组
//        for (auto& s : strs)
//        {
//            string tmp = s;
//            sort(tmp.begin(), tmp.end());
//            hash[tmp].push_back(s);
//        }
//        // 2.结果提取出来
//        vector<vector<string>> ret;
//        for (auto& [x, y] : hash)
//        {
//            ret.push_back(y);
//        }
//        return ret;
//    }
//};



//class Solution
//{
//public:
//    string longestCommonPrefix(vector<string>& strs)
//    {
//        // 解法⼀:两两⽐较
//        string ret = strs[0];
//        for (int i = 1; i < strs.size(); i++)
//            ret = findCommon(ret, strs[i]);
//        return ret;
//    }
//    string findCommon(string& s1, string& s2)
//    {
//        int i = 0;
//        while (i < min(s1.size(), s2.size()) && s1[i] == s2[i]) i++;
//        return s1.substr(0, i);
//    }
//};
//
//
//class Solution
//{
//public:
//    string longestCommonPrefix(vector<string>& strs)
//    {
//        // 解法⼆:统⼀⽐较
//        for (int i = 0; i < strs[0].size(); i++)
//        {
//            char tmp = strs[0][i];
//            for (int j = 1; j < strs.size(); j++)
//                if (i == strs[j].size() || tmp != strs[j][i])
//                    return strs[0].substr(0, i);
//        }
//        return strs[0];
//    }
//};


//class Solution
//{
//public:
//    string longestPalindrome(string s)
//    {
//        // 中⼼扩展算法
//        int begin = 0, len = 0, n = s.size();
//        for (int i = 0; i < n; i++) // 依次枚举所有的中点
//        {
//            // 先做⼀次奇数⻓度的扩展
//            int left = i, right = i;
//            while (left >= 0 && right < n && s[left] == s[right])
//            {
//                left--;
//                right++;
//            }
//            if (right - left - 1 > len)
//            {
//                begin = left + 1;
//                len = right - left - 1;
//            }
//            // 偶数⻓度的扩展
//            left = i, right = i + 1;
//            while (left >= 0 && right < n && s[left] == s[right])
//            {
//                left--;
//                right++;
//            }
//            if (right - left - 1 > len)
//            {
//                begin = left + 1;
//                len = right - left - 1;
//            }
//        }
//        return s.substr(begin, len);
//    }
//};



//class Solution {
//public:
//    string addBinary(string a, string b)
//    {
//        string str;
//        int cur1 = a.size() - 1, cur2 = b.size() - 1, t = 0;
//        while (cur1 >= 0 || cur2 >= 0 || t)
//        {
//            if (cur1 >= 0) t += a[cur1--] - '0';
//            if (cur2 >= 0) t += b[cur2--] - '0';
//            str += t % 2 + '0';
//            t /= 2;
//        }
//        reverse(str.begin(), str.end());
//        return str;
//    }
//};


//class Solution {
//public:
//    string multiply(string num1, string num2)
//    {
//        // 无进位相乘后相加,然后处理进位
//        int m = n1.size(), n = n2.size();
//        reverse(n1.begin(), n1.begin());
//        reverse(n2.begin(), n2.begin());
//        vector<int> tmp(m + n - 1);
//        // 1.无进位相乘后相加
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                tmp[i + j] += (n1[i] - '0') * (n2[j] - '0');
//        // 2. 处理进位
//        int cur = 0, t = 0;
//        string ret;
//        while (cur < m + n - 1 || t)
//        {
//            if (cur < m + n - 1) t += tmp[cur++];
//            ret += t % 10 + '0';
//            t /= 10;
//        }
//        // 3. 处理前导零
//        while (ret.size() > 1 && ret.back() == '0') ret.pop_back();
//        reverse(ret.begin(), ret.end());
//        return ret;
//    }
//};
//
//
//class Solution
//{
//public:
//    string multiply(string n1, string n2)
//    {
//        // 解法:⽆进位相乘后相加,然后处理进位
//        int m = n1.size(), n = n2.size();
//        reverse(n1.begin(), n1.end());
//        reverse(n2.begin(), n2.end());
//        vector<int> tmp(m + n - 1);
//        // 1. ⽆进位相乘后相加
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                tmp[i + j] += (n1[i] - '0') * (n2[j] - '0');
//
//        // 2. 处理进位
//        int cur = 0, t = 0;
//        string ret;
//        while (cur < m + n - 1 || t)
//        {
//            if (cur < m + n - 1) t += tmp[cur++];
//            ret += t % 10 + '0';
//            t /= 10;
//        }
//        // 3. 处理前导零
//        while (ret.size() > 1 && ret.back() == '0') ret.pop_back();
//        reverse(ret.begin(), ret.end());
//        return ret;
//    }
//};
#include<string>


//class Solution
//{
//public:
//    string removeDuplicates(string s)
//    {
//        string ret; // 搞⼀个数组,模拟栈结构即可
//        for (auto ch : s)
//        {
//            if (ret.size() && ch == ret.back()) ret.pop_back(); // 出栈
//            else ret += ch; // ⼊栈
//        }
//        return ret;
//    }
//};



//class Solution
//{
//public:
//    bool backspaceCompare(string s, string t)
//    {
//        return changeStr(s) == changeStr(t);
//    }
//    string changeStr(string& s)
//    {
//        string ret; // ⽤数组模拟栈结构
//        for (char ch : s)
//        {
//            if (ch != '#') ret += ch;
//            else
//            {
//                if (ret.size()) ret.pop_back();
//            }
//        }
//        return ret;
//    }
//};


//class Solution {
//public:
//    bool backspaceCompare(string s, string t)
//    {
//        string str1, str2;
//        for (auto x : s)
//        {
//            if (str1.size() && x == '#') str1.pop_back();
//            else {
//                if (x != '#') str1 += x;
//            }
//        }
//        for (auto x : t)
//        {
//            if (str2.size() && x == '#') str2.pop_back();
//            else {
//                if (x != '#') str2 += x;
//            }
//        }
//        return strcmp(str1.c_str(), str2.c_str()) == 0;
//    }
//};



//class Solution
//{
//public:
//    int calculate(string s)
//    {
//        vector<int> st; // ⽤数组来模拟栈结构
//        int i = 0, n = s.size();
//        char op = '+';
//        while (i < n)
//        {
//            if (s[i] == ' ') i++;
//            else if (s[i] >= '0' && s[i] <= '9')
//            {
//                // 先把这个数字给提取出来
//                int tmp = 0;
//                while (i < n && s[i] >= '0' && s[i] <= '9')
//                    tmp = tmp * 10 + (s[i++] - '0');
//                if (op == '+') st.push_back(tmp);
//                else if (op == '-') st.push_back(-tmp);
//                else if (op == '*') st.back() *= tmp;
//                else st.back() /= tmp;
//            }
//            else
//            {
//                op = s[i];
//                i++;
//            }
//        }
//        int ret = 0;
//        for (auto x : st) ret += x;
//        return ret;
//    }
//};

#include<stack>
#include<string>


//class Solution
//{
//public:
//    string decodeString(string s)
//    {
//        stack<int> nums;
//        stack<string> st;
//        st.push("");
//        int i = 0, n = s.size();
//        while (i < n)
//        {
//            if (s[i] >= '0' && s[i] <= '9')
//            {
//                int tmp = 0;
//                while (s[i] >= '0' && s[i] <= '9')
//                {
//                    tmp = tmp * 10 + (s[i] - '0');
//                    i++;
//                }
//                nums.push(tmp);
//            }
//            else if (s[i] == '[')
//            {
//                i++; // 把括号后⾯的字符串提取出来
//                string tmp = "";
//                while (s[i] >= 'a' && s[i] <= 'z')
//                {
//                    tmp += s[i];
//                    i++;
//                }
//                st.push(tmp);
//            }
//            else if (s[i] == ']')
//            {
//                string tmp = st.top();
//                st.pop();
//                int k = nums.top();
//                nums.pop();
//                while (k--)
//                {
//                    st.top() += tmp;
//                }
//                i++; // 跳过这个右括号
//            }
//            else
//            {
//                string tmp;
//                while (i < n && s[i] >= 'a' && s[i] <= 'z')
//                {
//                    tmp += s[i];
//                    i++;
//                }
//                st.top() += tmp;
//            }
//        }
//        return st.top();
//    }
//};
//
//
//class Solution {
//public:
//    bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
//    {
//        stack<int> st;
//        int i = 0, n = pushed.size();
//        for (auto x : pushed)
//        {
//            st.push(x);
//            while (st.size() && st.top() == popped[i])
//            {
//                st.pop();
//                i++;
//            }
//        }
//        return i == n;
//    }
//};




// Definition for a Node.
//class TreeNode {
//public:
//    int val;
//    vector<TreeNode*> children;
//    int left;
//    int right;
//
//    TreeNode() {}
//
//    TreeNode(int _val) {
//        val = _val;
//    }
//
//    TreeNode(int _val, vector<TreeNode*> _children) {
//        val = _val;
//        children = _children;
//    }
//};
//
//
//class Solution {
//public:
//    vector<vector<int>> levelOrder(Node* root)
//    {
//        vector<vector<int>> ret;// 记录最终结果
//        queue<Node*> q; // 层序遍历需要的队列
//        if (root == nullptr) return ret;
//        q.push(root);
//        while (q.size())
//        {
//            int sz = q.size(); // 求出本层元素的个数
//            vector<int> tmp; //统计本层的节点
//            for (int i = 0; i < sz; i++)
//            {
//                Node* t = q.front(); // 取出对头
//                q.pop();
//                tmp.push_back(t->val);
//                for (Node* child : t->children) //让下一层节点入队列
//                {
//                    if (child != nullptr) q.push(child);
//                }
//            }
//            ret.push_back(tmp);
//        }
//        return ret;
//    }
//};



/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
//class Solution {
//public:
//    vector<vector<int>> zigzagLevelOrder(TreeNode* root)
//    {
//        vector<vector<int>> ret;
//        if (root == nullptr) return ret;
//        queue<TreeNode*> q;
//        q.push(root);
//        int level = 1;
//        while (q.size())
//        {
//            int sz = q.size();
//            vector<int> tmp;
//            for (int i = 0; i < sz; i++)
//            {
//                auto t = q.front();
//                q.pop();
//                tmp.push_back(t->val);
//                if (t->left) q.push(t->left);
//                if (t->right) q.push(t->right);
//            }
//            // 判断是否逆序
//            if (level % 2 == 0) reverse(tmp.begin(), tmp.end());
//            ret.push_back(tmp);
//            level++;
//        }
//        return ret;
//    }
//};




///**
// * Definition for a binary tree node.
// * struct TreeNode {
// *     int val;
// *     TreeNode *left;
// *     TreeNode *right;
// *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
// *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
// *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
// * };
// */
//class Solution {
//public:
//    int widthOfBinaryTree(TreeNode* root)
//    {
//        vector<pair<TreeNode*, unsigned int>> q;//用数组模拟队列
//        q.push_back({ root , 1 });
//        unsigned int ret = 0;
//        while (q.size())
//        {
//            // 先更新这一层的宽度
//            auto& [x1, y1] = q[0];
//            auto& [x2, y2] = q.back();
//            ret = max(ret, y2 - y1 + 1);
//            // 让下一层进队列
//            vector<pair<TreeNode*, unsigned int>> tmp;//让下一层进入这个队列
//            for (auto& [x, y] : q)
//            {
//                if (x->left) tmp.push_back({ x->left, y * 2 });
//                if (x->right) tmp.push_back({ x->right, y * 2 + 1 });
//            }
//            q = tmp;
//        }
//        return ret;
//    }
//};



//class Solution
//{
//public:
//    vector<int> largestValues(TreeNode* root)
//    {
//        vector<int> ret;
//        if (root == nullptr) return ret;
//        queue<TreeNode*> q;
//        q.push(root);
//        while (q.size())
//        {
//            int sz = q.size();
//            int tmp = INT_MIN;
//            for (int i = 0; i < sz; i++)
//            {
//                auto t = q.front();
//                q.pop();
//                tmp = max(tmp, t->val);
//                if (t->left) q.push(t->left);
//                if (t->right) q.push(t->right);
//            }
//            ret.push_back(tmp);
//        }
//        return ret;
//    }
//};



//class Solution
//{
//public:
//    int lastStoneWeight(vector<int>& stones)
//    {
//        // 1. 创建⼀个⼤根堆
//        priority_queue<int> heap;
//        // 2. 将所有元素丢进这个堆⾥⾯
//        for (auto x : stones) heap.push(x);
//        // 3. 模拟这个过程
//        while (heap.size() > 1)
//        {
//            int a = heap.top(); heap.pop();
//            int b = heap.top(); heap.pop();
//            if (a > b) heap.push(a - b);
//        }
//        return heap.size() ? heap.top() : 0;
//    }
//};


//class KthLargest
//{
//    // 创建⼀个⼤⼩为 k 的⼩跟堆
//    priority_queue<int, vector<int>, greater<int>> heap;
//    int _k;
//public:
//    KthLargest(int k, vector<int>& nums)
//    {
//        _k = k;
//        for (auto x : nums)
//        {
//            heap.push(x);
//            if (heap.size() > _k) heap.pop();
//        }
//    }
//
//    int add(int val)
//    {
//        heap.push(val);
//        if (heap.size() > _k) heap.pop();
//        return heap.top();
//    }
//};
///**
// * Your KthLargest object will be instantiated and called as such:
// * KthLargest* obj = new KthLargest(k, nums);
// * int param_1 = obj->add(val);
// */


//class Solution
//{
//    typedef pair<string, int> PSI;
//    struct cmp
//    {
//        bool operator()(const PSI& a, const PSI& b)
//        {
//            if (a.second == b.second) // 频次相同,字典序按照⼤根堆的⽅式排列
//            {
//                return a.first < b.first;
//            }
//            return a.second > b.second;
//        }
//    };
//public:
//    vector<string> topKFrequent(vector<string>& words, int k)
//    {
//        // 1. 统计⼀下每⼀个单词的频次
//        unordered_map<string, int> hash;
//        for (auto& s : words) hash[s]++;
//        // 2. 创建⼀个⼤⼩为 k 的堆
//        priority_queue<PSI, vector<PSI>, cmp> heap;
//        // 3. TopK 的主逻辑
//        for (auto& psi : hash)
//        {
//            heap.push(psi);
//            if (heap.size() > k) heap.pop();
//        }
//        // 4. 提取结果
//        vector<string> ret(k);
//        for (int i = k - 1; i >= 0; i--)
//        {
//            ret[i] = heap.top().first;
//            heap.pop();
//        }
//        return ret;
//    }
//};



//class MedianFinder
//{
//    priority_queue<int> left; // ⼤根堆
//    priority_queue<int, vector<int>, greater<int>> right; // ⼩根堆
//public:
//    MedianFinder()
//    {}
//
//    void addNum(int num)
//    {
//        // 分类讨论即可
//        if (left.size() == right.size()) // 左右两个堆的元素个数相同
//        {
//            if (left.empty() || num <= left.top()) // 放 left ⾥⾯
//            {
//                left.push(num);
//            }
//            else
//            {
//                right.push(num);
//                left.push(right.top());
//                right.pop();
//            }
//        }
//        else
//        {
//            if (num <= left.top())
//            {
//                left.push(num);
//                right.push(left.top());
//                left.pop();
//            }
//            else
//            {
//                right.push(num);
//            }
//        }
//    }
//
//    double findMedian()
//    {
//        if (left.size() == right.size()) return (left.top() + right.top()) /
//            2.0;
//        else return left.top();
//    }
//};
///**
// * Your MedianFinder object will be instantiated and called as such:
// * MedianFinder* obj = new MedianFinder();
// * obj->addNum(num);
// * double param_2 = obj->findMedian();
// */




/*
*The task state array is a strange "bitmap" of
*reasons to sleep. Thus "running" is zero, and
*you can test for combinations of others with
*simple bit tests.
*/
//static const char* const task_state_array[] = 
//{
//  "R (running)", /*0 */
//  "S (sleeping)", /*1 */
//  "D (disk sleep)", /*2 */
//  "T (stopped)", /*4 */
//  "t (tracing stop)", /*8 */
//  "X (dead)", /*16 */
//  "Z (zombie)", /*32 */
//};
//
//
//#include <sys/time.h>
//#include <sys/resource.h>
//int getpriority(int which, int who);
//int setpriority(int which, int who, int prio);





// 第一种方法:命令行第三个参数
//#include <stdio.h>
//int main(int argc, char* argv[], char* env[])
//{
//    int i = 0;
//    for (; env[i]; i++) {
//        printf("%s\n", env[i]);
//    }
//    return 0;
//}
//// 第二种方法:通过第三方变量environ获取
//int main(int argc, char* argv[])
//{
//    extern char** environ;
//    int i = 0;
//    for (; environ[i]; i++) {
//        printf("%s\n", environ[i]);
//    }
//    return 0;
//}


//
//#include <stdio.h>
//#include <stdlib.h>
//int main()
//{
//    printf("%s\n", getenv("PATH"));
//    return 0;
//}



//#include <stdio.h>
//#include <unistd.h>
//#include <stdlib.h>
//int g_val = 0;
//int main()
//{
//    pid_t id = fork();
//    if (id < 0) {
//        perror("fork");
//        return 0;
//    }
//    else if (id == 0) { //child
//        printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
//    }
//    else { //parent
//        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
//    }
//    sleep(1);
//    return 0;
//}



//#include <stdio.h>
//#include <unistd.h>
//#include <stdlib.h>
//int g_val = 0;
//int main()
//{
//    pid_t id = fork();
//    if (id < 0) {
//        perror("fork");
//        return 0;
//    }
//    else if (id == 0) { //child,⼦进程肯定先跑完,也就是⼦进程先修改,完成之后,⽗进程
//        再读取
//            g_val = 100;
//        printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
//    }
//    else { //parent
//        sleep(3);
//        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
//    }
//    sleep(1);
//    return 0;
//}



//struct task_struct
//{
//    /*...*/
//    struct mm_struct* mm; //对于普通的⽤⼾进程来说该字段指向他
//    //的虚拟地址空间的⽤⼾空间部分,对于内核线程来说这部分为NULL。
//        struct mm_struct* active_mm; 
//    // 该字段是内核线程使⽤的。当
//    //该进程是内核线程时,它的mm字段为NULL,表⽰没有内存地址空间,可也并不是真正的没有,这是因
//    //为所有进程关于内核的映射都是⼀样的,内核线程可以使⽤任意进程的地址空间。
//        /*...*/
//}



//struct mm_struct
//{
//    /*...*/
//    struct vm_area_struct* mmap; /* 指向虚拟区间(VMA)链表 */
//    struct rb_root mm_rb; /* red_black树 */
//    unsigned long task_size; /*具有该结构体的进程的虚拟地址空间的⼤⼩*/
//    /*...*/
//    // 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。
//    unsigned long start_code, end_code, start_data, end_data;
//    unsigned long start_brk, brk, start_stack;
//    unsigned long arg_start, arg_end, env_start, env_end;
//}



//struct device
//{
//    int id;
//    int vender;
//    int status;
//    void* data;
//    struct device* next;
//    int type;
//    struct task_struct* wait_queue;
//};
//struct list_head
//{
//     tast_struct都是用双向链表链接起来
//    struct list_head* next, * prev;
//};
//
//
// 方法 nice renice命令
//#include<sys/time.h>     
//int getpriority(int which, int who);
//#include<sys/resource.h>
//int setpriority(int which, int who, int prio);
//int a = 0;
//int b = 0;
//
//
//class Solution {
//public:
//    typedef pair<int, int> PII;
//    int dx[4] = { 0,0,1,-1 };
//    int dy[4] = { 1,-1,0,0 };
//    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
//    {
//        int prev = image[sr][sc];
//        if (prev == color) return image;
//        int m = image.size(), n = image[0].size();
//        queue<PII> q;
//        q.push({ sr,sc });
//        while (!q.empty())
//        {
//            auto [a, b] = q.front();
//            image[a][b] = color;
//            q.pop();
//            for (int i = 0; i < 4; i++)
//            {
//                int x = a + dx[i], y = b + dy[i];
//                if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev)
//                {
//                    q.push({ x,y });
//                }
//            }
//        }
//        return image;
//    }
//};




//class Solution
//{
//    int dx[4] = { 1, -1, 0, 0 };
//    int dy[4] = { 0, 0, 1, -1 };
//    bool vis[301][301];
//    int m, n;
//public:
//    int numIslands(vector<vector<char>>& grid)
//    {
//        m = grid.size(), n = grid[0].size();
//        int ret = 0;
//        for (int i = 0; i < m; i++)
//        {
//            for (int j = 0; j < n; j++)
//            {
//                if (grid[i][j] == '1' && !vis[i][j])
//                {
//                    ret++;
//                    bfs(grid, i, j); // 把这块陆地全部标记⼀下
//                }
//            }
//        }
//        return ret;
//    }
//    void bfs(vector<vector<char>>& grid, int i, int j)
//    {
//        queue<pair<int, int>> q;
//        q.push({ i, j });
//        vis[i][j] = true;
//        while (q.size())
//        {
//            auto [a, b] = q.front();
//            q.pop();
//            for (int k = 0; k < 4; k++)
//            {
//                int x = a + dx[k], y = b + dy[k];
//                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1' &&
//                    !vis[x][y])
//                {
//                    q.push({ x, y });
//                    vis[x][y] = true;
//                }
//            }
//        }
//    }
//};




//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//    bool vis[51][51];
//    int m, n;
//public:
//    int maxAreaOfIsland(vector<vector<int>>& grid)
//    {
//        m = grid.size(), n = grid[0].size();
//        int ret = 0;
//        for (int i = 0; i < m; i++)
//        {
//            for (int j = 0; j < n; j++)
//            {
//                if (grid[i][j] == 1 && !vis[i][j])
//                {
//                    ret = max(ret, bfs(grid, i, j));
//                }
//            }
//        }
//        return ret;
//    }
//    int bfs(vector<vector<int>>& grid, int i, int j)
//    {
//        int count = 0;
//        queue<pair<int, int>> q;
//        q.push({ i, j });
//        vis[i][j] = true;
//        count++;
//        while (q.size())
//        {
//            auto [a, b] = q.front();
//            q.pop();
//            for (int k = 0; k < 4; k++)
//            {
//                int x = a + dx[k], y = b + dy[k];
//                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 &&
//                    !vis[x][y])
//                {
//                    q.push({ x, y });
//                    vis[x][y] = true;
//                    count++;
//                }
//            }
//        }
//        return count;
//    }
//};




//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//    int m, n;
//public:
//    void solve(vector<vector<char>>& board)
//    {
//        m = board.size(), n = board[0].size();
//        // 1. 先处理边界上的 'O' 联通块,全部修改成 '.'
//        for (int j = 0; j < n; j++)
//        {
//            if (board[0][j] == 'O') bfs(board, 0, j);
//            if (board[m - 1][j] == 'O') bfs(board, m - 1, j);
//        }
//        for (int i = 0; i < m; i++)
//        {
//            if (board[i][0] == 'O') bfs(board, i, 0);
//            if (board[i][n - 1] == 'O') bfs(board, i, n - 1);
//        }
//        // 2. 还原
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (board[i][j] == 'O') board[i][j] = 'X';
//                else if (board[i][j] == '.') board[i][j] = 'O';
//    }
//    void bfs(vector<vector<char>>& board, int i, int j)
//    {
//        queue<pair<int, int>> q;
//        q.push({ i, j });
//        board[i][j] = '.';
//        while (q.size())
//        {
//            auto [a, b] = q.front();
//            q.pop();
//            for (int k = 0; k < 4; k++)
//            {
//                int x = a + dx[k], y = b + dy[k];
//                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O')
//                {
//                    q.push({ x, y });
//                    board[x][y] = '.';
//                }
//            }
//        }
//    }
//};



//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//public:
//    int nearestExit(vector<vector<char>>& maze, vector<int>& e)
//    {
//        int m = maze.size(), n = maze[0].size();
//        bool vis[m][n];
//        memset(vis, 0, sizeof vis);
//        queue<pair<int, int>> q;
//        q.push({ e[0], e[1] });
//        vis[e[0]][e[1]] = true;
//        int step = 0;
//        while (q.size())
//        {
//            step++;
//            int sz = q.size();
//            for (int i = 0; i < sz; i++)
//            {
//                auto [a, b] = q.front();
//                q.pop();
//                for (int j = 0; j < 4; j++)
//                {
//                    int x = a + dx[j], y = b + dy[j];
//                    if (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == '.'
//                        && !vis[x][y])
//                    {
//                        // 判断是否已经到达出⼝
//                        if (x == 0 || x == m - 1 || y == 0 || y == n - 1) return
//                            step;
//                        q.push({ x, y });
//                        vis[x][y] = true;
//                    }
//                }
//            }
//        }
//        return -1;
//    }
//};






//class Solution
//{
//public:
//    int minMutation(string startGene, string endGene, vector<string>& bank)
//    {
//        unordered_set<string> vis; // ⽤来标记已经搜索过的状态
//        unordered_set<string> hash(bank.begin(), bank.end()); // 存储基因库⾥⾯的字符串
//            string change = "ACGT";
//        if (startGene == endGene) return 0;
//        if (!hash.count(endGene)) return -1;
//        queue<string> q;
//        q.push(startGene);
//        vis.insert(startGene);
//        int ret = 0;
//        while (q.size())
//        {
//            ret++;
//            int sz = q.size();
//            while (sz--)
//            {
//                string t = q.front();
//                q.pop();
//                for (int i = 0; i < 8; i++)
//                {
//                    string tmp = t; // 细节问题
//                    for (int j = 0; j < 4; j++)
//                    {
//                        tmp[i] = change[j];
//                        if (hash.count(tmp) && !vis.count(tmp))
//                        {
//                            if (tmp == endGene) return ret;
//                            q.push(tmp);
//                            vis.insert(tmp);
//                        }
//                    }
//                }
//            }
//        }
//        return -1;
//    }
//};



//class Solution
//{
//public:
//    int ladderLength(string beginWord, string endWord, vector<string>&
//        wordList)
//    {
//        unordered_set<string> hash(wordList.begin(), wordList.end());
//        unordered_set<string> vis; // 标记已经搜索过的单词
//        if (!hash.count(endWord)) return 0;
//        queue<string> q;
//        q.push(beginWord);
//        vis.insert(beginWord);
//        int ret = 1;
//        while (q.size())
//        {
//            ret++;
//            int sz = q.size();
//            while (sz--)
//            {
//                string t = q.front();
//                q.pop();
//                for (int i = 0; i < t.size(); i++)
//                {
//                    string tmp = t;
//                    for (char ch = 'a'; ch <= 'z'; ch++)
//                    {
//                        tmp[i] = ch;
//                        if (hash.count(tmp) && !vis.count(tmp))
//                        {
//                            if (tmp == endWord) return ret;
//                            q.push(tmp);
//                            vis.insert(tmp);
//                        }
//                    }
//                }
//            }
//        }
//        return 0;
//    }
//};



//class Solution
//{
//    int m, n;
//public:
//    int cutOffTree(vector<vector<int>>& f)
//    {
//        m = f.size(), n = f[0].size();
//        // 1. 准备⼯作:找出砍树的顺序
//        vector<pair<int, int>> trees;
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (f[i][j] > 1) trees.push_back({ i, j });
//
//        sort(trees.begin(), trees.end(), [&](const pair<int, int>& p1, const
//            pair<int, int>& p2)
//            {
//                return f[p1.first][p1.second] < f[p2.first][p2.second];
//            });
//        // 2. 按照顺序砍树
//        int bx = 0, by = 0;
//        int ret = 0;
//        for (auto& [a, b] : trees)
//        {
//            int step = bfs(f, bx, by, a, b);
//            if (step == -1) return -1;
//            ret += step;
//            bx = a, by = b;
//        }
//        return ret;
//    }
//    bool vis[51][51];
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//    int bfs(vector<vector<int>>& f, int bx, int by, int ex, int ey)
//    {
//        if (bx == ex && by == ey) return 0;
//        queue<pair<int, int>> q;
//        memset(vis, 0, sizeof vis); // 清空之前的数据
//        q.push({ bx, by });
//        vis[bx][by] = true;
//        int step = 0;
//        while (q.size())
//        {
//            step++;
//            int sz = q.size();
//            while (sz--)
//            {
//                auto [a, b] = q.front();
//                q.pop();
//                for (int i = 0; i < 4; i++)
//                {
//                    int x = a + dx[i], y = b + dy[i];
//                    if (x >= 0 && x < m && y >= 0 && y < n && f[x][y] && !vis[x]
//                        [y])
//                    {
//                        if (x == ex && y == ey) return step;
//                        q.push({ x, y });
//                        vis[x][y] = true;
//                    }
//                }
//            }
//        }
//        return -1;
//    }
//};




//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//public:
//    vector<vector<int>> updateMatrix(vector<vector<int>>& mat)
//    {
//        int m = mat.size(), n = mat[0].size();
//
//        // dist[i][j] == -1 表⽰:没有搜索过
//        // dist[i][j] != -1 表⽰:最短距离
//        vector<vector<int>> dist(m, vector<int>(n, -1));
//        queue<pair<int, int>> q;
//        // 1. 把所有的源点加⼊到队列中
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (mat[i][j] == 0)
//                {
//                    q.push({ i, j });
//                    dist[i][j] = 0;
//                }
//
//        // 2. ⼀层⼀层的往外扩
//        while (q.size())
//        {
//            auto [a, b] = q.front(); q.pop();
//            for (int i = 0; i < 4; i++)
//            {
//                int x = a + dx[i], y = b + dy[i];
//                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1)
//                {
//                    dist[x][y] = dist[a][b] + 1;
//                    q.push({ x, y });
//                }
//            }
//        }
//        return dist;
//    }
//};



//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//public:
//    int numEnclaves(vector<vector<int>>& grid)
//    {
//        int m = grid.size(), n = grid[0].size();
//        vector<vector<bool>> vis(m, vector<bool>(n));
//        queue<pair<int, int>> q;
//        // 1. 把边上的 1 加⼊到队列中
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (i == 0 || i == m - 1 || j == 0 || j == n - 1)
//                {
//                    if (grid[i][j] == 1)
//                    {
//                        q.push({ i, j });
//                        vis[i][j] = true;
//                    }
//                }
//        // 2. 多源 bfs
//        while (q.size())
//        {
//            auto [a, b] = q.front();
//            q.pop();
//            for (int i = 0; i < 4; i++)
//            {
//                int x = a + dx[i], y = b + dy[i];
//                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 &&
//                    !vis[x][y])
//                {
//                    vis[x][y] = true;
//                    q.push({ x, y });
//                }
//            }
//        }
//        // 3. 统计结果
//        int ret = 0;
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (grid[i][j] == 1 && !vis[i][j])
//                    ret++;
//        return ret;
//    }
//};


//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//public:
//    vector<vector<int>> highestPeak(vector<vector<int>>& isWater)
//    {
//        int m = isWater.size(), n = isWater[0].size();
//        vector<vector<int>> dist(m, vector<int>(n, -1));
//        queue<pair<int, int>> q;
//        // 1. 把所有的源点加⼊到队列⾥⾯
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (isWater[i][j])
//                {
//                    dist[i][j] = 0;
//                    q.push({ i, j });
//                }
//        // 2. 多源 bfs
//        while (q.size())
//        {
//            auto [a, b] = q.front(); q.pop();
//            for (int i = 0; i < 4; i++)
//            {
//                int x = a + dx[i], y = b + dy[i];
//                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1)
//                {
//                    dist[x][y] = dist[a][b] + 1;
//                    q.push({ x, y });
//                }
//            }
//        }
//        return dist;
//    }
//};




//class Solution
//{
//    int dx[4] = { 0, 0, 1, -1 };
//    int dy[4] = { 1, -1, 0, 0 };
//public:
//    int maxDistance(vector<vector<int>>& grid)
//    {
//        int m = grid.size(), n = grid[0].size();
//        vector<vector<int>> dist(m, vector<int>(n, -1));
//        queue<pair<int, int>> q;
//        for (int i = 0; i < m; i++)
//            for (int j = 0; j < n; j++)
//                if (grid[i][j])
//                {
//                    dist[i][j] = 0;
//                    q.push({ i, j });
//                }
//
//        int ret = -1;
//        while (q.size())
//        {
//            auto [a, b] = q.front(); q.pop();
//            for (int i = 0; i < 4; i++)
//            {
//                int x = a + dx[i], y = b + dy[i];
//                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1)
//                {
//                    dist[x][y] = dist[a][b] + 1;
//                    q.push({ x, y });
//                    ret = max(ret, dist[x][y]);
//                }
//            }
//        }
//        return ret;
//    }
//};




//class Solution
//{
//public:
//    bool canFinish(int n, vector<vector<int>>& p)
//    {
//        unordered_map<int, vector<int>> edges; // 邻接表
//        vector<int> in(n); // 存储每⼀个结点的⼊度
//
//        // 1. 建图
//        for (auto& e : p)
//        {
//            int a = e[0], b = e[1];
//            edges[b].push_back(a);
//            in[a]++;
//        }
//        // 2. 拓扑排序 - bfs
//        queue<int> q;
//        // 把所有⼊度为 0 的点加⼊到队列中
//        for (int i = 0; i < n; i++)
//        {
//            if (in[i] == 0) q.push(i);
//        }
//        // 层序遍历
//        while (!q.empty())
//        {
//            int t = q.front();
//            q.pop();
//            // 修改相连的边
//            for (int e : edges[t])
//            {
//                in[e]--;
//                if (in[e] == 0) q.push(e);
//            }
//        }
//        // 3. 判断是否有环
//        for (int i : in)
//            if (i)
//                return false;
//
//        return true;
//    }
//};




//class Solution
//{
//public:
//    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites)
//    {
//        // 1. 准备⼯作
//        vector<vector<int>> edges(numCourses); // 邻接表存储图
//        vector<int> in(numCourses); // 存储每⼀个点的⼊度
//        // 2. 建图
//        for (auto& p : prerequisites)
//        {
//            int a = p[0], b = p[1]; // b -> a
//            edges[b].push_back(a);
//            in[a]++;
//        }
//        // 3. 拓扑排序
//        vector<int> ret; // 统计排序后的结果
//        queue<int> q;
//        for (int i = 0; i < numCourses; i++)
//        {
//            if (in[i] == 0) q.push(i);
//        }
//        while (q.size())
//        {
//            int t = q.front(); q.pop();
//            ret.push_back(t);
//            for (int a : edges[t])
//            {
//                in[a]--;
//                if (in[a] == 0) q.push(a);
//            }
//        }
//        if (ret.size() == numCourses) return ret;
//        else return {};
//    }
//};


//class Solution
//{
//    unordered_map<char, unordered_set<char>> edges; // 邻接表来存储图
//    unordered_map<char, int> in; // 统计⼊度
//    bool cheak; // 处理边界情况
//public:
//    string alienOrder(vector<string>& words)
//    {
//        // 1. 建图 + 初始化⼊度哈希表
//        for (auto& s : words)
//        {
//            for (auto ch : s)
//            {
//                in[ch] = 0;
//            }
//        }
//        int n = words.size();
//        for (int i = 0; i < n; i++)
//        {
//            for (int j = i + 1; j < n; j++)
//            {
//                add(words[i], words[j]);
//                if (cheak) return "";
//            }
//        }
//
//        // 2. 拓扑排序
//        queue<char> q;
//        for (auto& [a, b] : in)
//        {
//            if (b == 0) q.push(a);
//        }
//        string ret;
//        while (q.size())
//        {
//            char t = q.front(); q.pop();
//            ret += t;
//            for (char ch : edges[t])
//            {
//                if (--in[ch] == 0) q.push(ch);
//            }
//        }
//        // 3. 判断
//        for (auto& [a, b] : in)
//            if (b != 0) return "";
//
//        return ret;
//    }
//    void add(string& s1, string& s2)
//    {
//        int n = min(s1.size(), s2.size());
//        int i = 0;
//        for (; i < n; i++)
//        {
//            if (s1[i] != s2[i])
//            {
//                char a = s1[i], b = s2[i]; // a -> b
//                if (!edges.count(a) || !edges[a].count(b))
//                {
//                    edges[a].insert(b);
//                    in[b]++;
//                }
//                break;
//            }
//        }
//        if (i == s2.size() && i < s1.size()) cheak = true;
//    }
//};


// 补充内容,其实是差了几道题
//class Solution {
//public:
//    int hammingWeight(int n)
//    {
//        int sum = 0;
//        int i = 0;
//        for (int i = 0; i < 32; i++)
//        {
//            if ((n >> i) & 1 == 1)
//            {
//                sum++;
//            }
//        }
//        return sum;
//    }
//};




//class Solution {
//public:
//    vector<int> countBits(int n)
//    {
//        vector<int> nums(n + 1);
//        for (int i = 0; i <= n; i++)
//        {
//            int sum = 0;
//            for (int j = 0; j < 32; j++)
//            {
//                if ((i >> j) & 1 == 1)
//                    sum++;
//            }
//            nums[i] = sum;
//        }
//        return nums;
//    }
//};




//class Solution {
//public:
//    int hammingDistance(int x, int y)
//    {
//        int sum = 0;
//        for (int i = 0; i < 32; i++)
//        {
//            if (((x >> i) & 1) ^ ((y >> i) & 1) == 1)
//                sum++;
//        }
//        return sum;
//    }
//};



//class Solution {
//public:
//    int singleNumber(vector<int>& nums)
//    {
//        int ret = 0;
//        for (int i = 0; i < nums.size(); i++)
//        {
//            ret ^= nums[i];
//        }
//        return ret;
//    }
//};
#include<stdlib.h>


/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
//int* singleNumber(int* nums, int numsSize, int* returnSize)
//{
//    int* ans = (int*)calloc(2, sizeof(int));
//    int ret = 0;
//    int i = 0;
//    //计算数组中所有数异或起来的结果ret 
//    for (i = 0; i < numsSize; i++) {
//        ret ^= nums[i];
//    }
//
//    //从低位往⾼位遍历计算ret的⼆进制中哪⼀位是1 
//    int pos = 0;
//    for (i = 0; i < 32; i++) {
//        if (((ret >> i) & 1) == 1) {
//            pos = i;
//            break;
//        }
//    }
//    //3. 分组异或 
//    for (i = 0; i < numsSize; i++) {
//        //若当前数pos位为1,作为其中⼀组对ans[0]进⾏异或操作 
//        if (((nums[i] >> pos) & 1) == 1) {
//            ans[0] ^= nums[i];
//        }
//        //否则,作为另⼀组对ans[1]进⾏异或操作。 
//        else {
//            ans[1] ^= nums[i];
//        }
//    }
//    //ans[1]另⼀种计算⽅法 
//    //ans[1]=ret^ans[0];
//    //更新数组⻓度 
//    *returnSize = 2;
//    //返回答案数组 
//    return ans;
//}