手把手带你刷LeetCode-------爱学习的饲养员[c++版](算法篇)
算法1.双指针
LeetCode141.环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL){
return false;
}
ListNode *slow = head;
ListNode *fast = head;
while(fast != NULL && fast -> next != NULL){
slow = slow -> next;
fast = fast -> next -> next;
if(slow == fast)
return true;
}
return false;
}
};
LeetCode881.救生艇
第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。
返回载到每一个人所需的最小船数。(保证每个人都能被船载)。
示例 1:
输入:people = [1,2], limit = 3
输出:1
解释:1 艘船载 (1, 2)。
class Solution {
public:
int numRescueBoats(vector<int>& people, int limit){
if(people.size() == 0)
return 0;
int result = 0;
sort(people.begin(),people.end());
int i = 0,j = people.size() - 1;
while(i <= j){
if(people[i] + people[j] <= limit){
i++;
}
j--;
result++;
}
return result;
}
};
算法2.二分查找法
LeetCode704.二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.size() == 0){
return -1;
}
int l = 0,r = nums.size() - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] > target){
r = mid - 1;
}else{
l = mid + 1;
}
}
return -1;
}
};
LeetCode35.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
if(nums.size() == 0){
return 0;
}
int l = 0,r = nums.size() - 1,result;
while(l < r){
int mid = l + (r - l) / 2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] > target){
r = mid;
}else{
l = mid + 1;
}
}
return nums[l] < target?l + 1:l;
}
};
LeetCode162. 寻找峰值
峰值元素是指其值大于左右相邻值的元素。
给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
class Solution {
public:
int findPeakElement(vector<int>& nums)
{
if(nums.size() == 1)
return 0;
int left = 0,right = nums.size() - 1,mid;
while(left < right)
{
mid = left + (right - left) / 2;
if(nums[mid] > nums[mid + 1])
{
right = mid;
}
else
{
left = mid + 1;
}
}
return left;
}
};
算法3.滑动窗口
LeetCode209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0,j = 0,n = nums.size(),result = n + 1,sum = 0;
while(j < n){
sum += nums[j];
while(sum >= target){
result = min(result,j - i + 1);
sum -= nums[i];
i++;
}
j++;
}
return result == n + 1?0:result;
}
};
LeetCode1456.定长子串中元音的最大数目
给你字符串 s 和整数 k 。
请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。
英文中的 元音字母 为(a, e, i, o, u)。
示例 1:
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。
class Solution {
public:
int maxVowels(string s, int k) {
if(s.size() == 0 || s.size() < k){
return 0;
}
unordered_set<char> st = {'a','e','i','o','u'};
int i,result = 0,count = 0;
for(i = 0;i < k;i++){
if(st.find(s[i]) != st.end()){
count++;
}
}
result = max(result,count);
for(i = k;i < s.size();i++){
if(st.find(s[i - k]) != st.end()){
count--;
}
if(st.find(s[i]) != st.end()){
count++;
}
result = max(result,count);
}
return result;
}
};
算法4.递归
LeetCode509.斐波那契数
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n) 。
示例 1:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
class Solution {
public:
int fib(int n) {
if(n < 2){
return n == 1?1:0;
}
int sum = fib(n - 1) + fib(n - 2);
return sum;
}
};
LeetCode206.反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head -> next == nullptr){
return head;
}
ListNode* p = reverseList(head -> next);
head -> next -> next = head;
head -> next = NULL;
return p;
}
};
LeetCode344.反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
class Solution {
public:
void recursion(vector<char>& s,int left,int right){
if(left >= right){
return;
}
recursion(s,left + 1,right - 1);
int temp = s[left];
s[left] = s[right];
s[right] = temp;
}
void reverseString(vector<char>& s) {
if(s.size() == 0){
return;
}
int left = 0,right = s.size() - 1;
recursion(s,left,right);
}
};
算法5.分治法
LeetCode169.多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
class Solution {
public:
int majorityElement(vector<int>& nums)
{
return getMajority(nums,0,nums.size() - 1);
}
int getMajority(vector<int>&nums,int left,int right)
{
if(left == right)
return nums[left];
int mid = left + (right - left) / 2;
int leftMajority = getMajority(nums,left,mid);
int rightMajority = getMajority(nums,mid + 1,right);
if(leftMajority == rightMajority)
return leftMajority;
int leftcount = 0,rightcount = 0;
for(int i = left;i <= right;i++)
{
if(nums[i] == leftMajority)
{
leftcount++;
}
else if(nums[i] == rightMajority)
{
rightcount++;
}
}
if(leftcount > rightcount)
{
return leftMajority;
}
else
{
return rightMajority;
}
}
};
LeetCode53.最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
class Solution {
private:
int getMax(vector<int>& nums,int left,int right){
if(left == right){
return nums[left];
}
int mid = left + (right - left) / 2;
int leftMax = getMax(nums,left,mid);
int rightMax = getMax(nums,mid + 1,right);
int crossMax = getCrossMax(nums,left,right);
return max(max(leftMax,rightMax),crossMax);
}
int getCrossMax(vector<int>& nums,int left,int right){
int mid = left + (right - left) / 2;
int leftSum = nums[mid];
int leftMax = leftSum;
for(int i = mid - 1;i >= left;i--){
leftSum +=nums[i];
leftMax = max(leftMax,leftSum);
}
int rightSum = nums[mid + 1];
int rightMax = rightSum;
for(int i = mid + 2;i <= right;i++){
rightSum += nums[i];
rightMax = max(rightMax,rightSum);
}
return leftMax + rightMax;
}
public:
int maxSubArray(vector<int>& nums) {
return getMax(nums,0,nums.size() - 1);
}
};
算法6.回溯法
LeetCode22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
class Solution {
public:
vector<string> generateParenthesis(int n)
{
vector<string> result;
string cur;
backtracking(n,result,0,0,cur);
return result;
}
void backtracking(int n,vector<string>& result,int left,int right,string& cur)
{
if(right > left)
return;
if(cur.size() == n * 2)
{
result.push_back(cur);
return;
}
if(left < n)
{
cur.push_back('(');
backtracking(n,result,left + 1,right,cur);
cur.pop_back();
}
if(right < left)
{
cur.push_back(')');
backtracking(n,result,left,right + 1,cur);
cur.pop_back();
}
}
};
LeetCode78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
三种解法:
1.扩展法
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>>result;
result.push_back(*new vector<int>());
for(int num:nums){
vector<vector<int>> subset;
for(auto res:result){
vector<int> temp = *new vector<int>(res);
temp.push_back(num);
subset.push_back(temp);
}
for(auto t:subset){
result.push_back(t);
}
}
return result;
}
};
2.回溯法
class Solution {
public:
vector<vector<int>>result;
void backtracking(vector<int> nums,int length,int index,vector<int> subset){
if(subset.size() == length){
vector<int> temp = *new vector<int>(subset);
result.push_back(temp);
return;
}
for(int i = index;i < nums.size();i++){
subset.push_back(nums[i]);
backtracking(nums,length,i+1,subset);
subset.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
result.push_back(*new vector<int>());
for(int i = 1;i <= nums.size();i++){
backtracking(nums,i,0,*new vector<int>());
}
return result;
}
};
3.dfs
class Solution {
public:
vector<vector<int>>result;
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums,0,*new vector<int>());
return result;
}
void dfs(vector<int>& nums,int index,vector<int> subset){
result.push_back(subset);
if(index == nums.size()){
return;
}
for(int i = index;i < nums.size();i++){
subset.push_back(nums[i]);
dfs(nums,i + 1,subset);
subset.pop_back();
}
}
};
算法7.深度优先搜索算法DFS
LeetCode938.二叉搜索树的范围和
给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。

示例 1:
输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32
1.递归法
class Solution {
public:
int rangeSumBST(TreeNode* root, int low, int high){
if(root == nullptr)
return 0;
int leftsum = rangeSumBST(root -> left,low,high);
int rightsum = rangeSumBST(root -> right,low,high);
int result = leftsum + rightsum;
if(root -> val >= low && root -> val <= high)
result = result + root -> val;
return result;
}
};
2.BFS
class Solution {
public:
int rangeSumBST(TreeNode* root, int low, int high)
{
int result = 0;
queue<TreeNode*>q;
q.push(root);
while(q.size()>0)
{
int size = q.size();
while(size > 0)
{
auto cur = q.front();
q.pop();
if(cur -> val >= low && cur ->val <= high)
{
result += cur -> val;
}
if(cur -> left != NULL)
{
q.push(cur -> left);
}
if(cur -> right != NULL)
{
q.push(cur -> right);
}
size--;
}
}
return result;
}
};
3.DFS
class Solution {
public:
int rangeSumBST(TreeNode* root, int low, int high) {
if(root == nullptr)
return 0;
if(root -> val > high) //比high值大就去左子树,寻找比high小的值
return rangeSumBST(root -> left,low,high);
if(root -> val < low) //比low值小就去右子树,寻找比low大的值
return rangeSumBST(root -> right,low,high);
return root -> val + rangeSumBST(root -> left,low,high) + rangeSumBST(root -> right,low,high); //将符合条件的值相加即得结果
}
};
算法8.广度优先搜索算法BFS
LeetCode102. 二叉树的层序遍历
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层序遍历结果:
[
[3],
[9,20],
[15,7]
]
1.BFS
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> result;
if(root == nullptr)
return result;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
int size = q.size();
result.push_back(vector<int>());
for(int i = 0;i < size;i++)
{
auto temp = q.front();
q.pop();
result.back().push_back(temp -> val);
if(temp -> left != NULL)
{
q.push(temp -> left);
}
if(temp -> right != NULL)
{
q.push(temp -> right);
}
}
}
return result;
}
};
2.DFS
class Solution {
public:
void dfs(TreeNode *node,vector<vector<int>>&result,int level)
{
if(node == nullptr)
return;
if(level >= result.size())
result.push_back(vector<int>());
result[level].push_back(node -> val);
if(node -> left != NULL)
dfs(node -> left,result,level + 1);
if(node -> right != NULL)
dfs(node -> right,result,level + 1);
}
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>>result;
if(root == nullptr)
return result;
dfs(root,result,0);
return result;
}
};
算法9.并查集
LeetCode200. 岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
1.DFS
class Solution {
public:
int numIslands(vector<vector<char>>& grid)
{
if(grid[0][0] == NULL || grid.size() == 0)
{
return 0;
}
int result = 0;
int row = grid.size();
int col = grid[0].size();
for(int i = 0;i < row;i++)
{
for(int j = 0;j < col;j++)
{
if(grid[i][j] == '1')
{
result += 1;
dfs(grid,i,j,row,col);
}
}
}
return result;
}
void dfs(vector<vector<char>>&grid,int i,int j,int row,int col)
{
if(i < 0 || j < 0 || i >= row || j >= col || grid[i][j] == '0')
return;
grid[i][j] = '0';
dfs(grid,i + 1,j,row,col);
dfs(grid,i - 1,j,row,col);
dfs(grid,i,j - 1,row,col);
dfs(grid,i,j + 1,row,col);
}
};
2.BFS
class Solution {
public:
int numIslands(vector<vector<char>>& grid)
{
if(grid[0][0] == NULL || grid.size() == 0)
return 0;
int result = 0;
int row = grid.size();
int col = grid[0].size();
queue<pair<int,int>> q;
for(int i = 0;i < row;i++)
{
for(int j = 0;j < col;j++)
{
if(grid[i][j] == '1')
{
result += 1;
q.push({i,j});
grid[i][j] = '0';
while(!q.empty())
{
auto cur = q.front();
q.pop();
int x = cur.first,y = cur.second;
if(x - 1 >= 0 && grid[x - 1][y] == '1')
{
grid[x - 1][y] = '0';
q.push({x - 1,y});
}
if(y - 1 >= 0 && grid[x][y - 1] == '1')
{
grid[x][y - 1] = '0';
q.push({x,y - 1});
}
if(x + 1 < row && grid[x + 1][y] == '1')
{
grid[x + 1][y] = '0';
q.push({x + 1,y});
}
if(y + 1 < col && grid[x][y + 1] == '1')
{
grid[x][y + 1] = '0';
q.push({x ,y + 1});
}
}
}
}
}
return result;
}
};
3.并查集
class UnionFind{
public:
UnionFind(vector<vector<char>>& grid)
{
int row = grid.size();
int col = grid[0].size();
count = row * col;
root = vector<int>(row * col , 0);
for(int i = 0; i < row*col; i++)
{
root[i] = i;
}
}
int find(int x)
{
if(x == root[x])
{
return x;
}
else
{
root[x] = find(root[x]);
return root[x];
}
}
void Union(int x,int y)
{
int rootx = find(x);
int rooty = find(y);
if(rootx != rooty)
{
root[rootx] = rooty;
count--;
}
}
int getCount()
{
return count;
}
private:
vector<int> root;
int count = 0;
};
class Solution {
public:
int numIslands(vector<vector<char>>& grid)
{
if(!grid.size())
{
return 0;
}
int row = grid.size();
int col = grid[0].size();
int water = 0;
auto uf = UnionFind(grid);
for(int i = 0;i < row;i++)
{
for(int j = 0;j < col;j++)
{
if(grid[i][j] == '0')
{
water++;
}
else
{
vector<vector<int>> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
for(auto d:directions)
{
int x = i + d[0];
int y = j + d[1];
if(x >= 0 && x < row && y >= 0 && y < col && grid[x][y] == '1')
{
uf.Union(x * col + y,i * col + j);
}
}
}
}
}
return uf.getCount() - water;
}
};
算法10.贪心算法
LeetCode322.零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
贪心+动态规划:
class Solution {
public:
int coinChange(vector<int>& coins, int amount)
{
vector<int>dp(amount + 1, -1);
dp[0] = 0;
for(int i = 1;i <= amount;i++)
{
for(int j = 0;j < coins.size();j++)
{
if(coins[j] <= i && dp[i - coins[j]] != -1)
{
if(dp[i] == -1 || dp[i] > dp[i - coins[j]] + 1)
{
dp[i] = dp[i - coins[j]] + 1;
}
}
}
}
return dp[amount];
}
};
算法11.动态规划
LeetCode509.斐波那契数
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n) 。
示例 1:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
class Solution {
public:
int fib(int n) {
if(n < 2){
return n == 1?1:0;
}
int sum = fib(n - 1) + fib(n - 2);
return sum;
}
};
LeetCode62.不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:

输入:m = 3, n = 7
输出:28
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m + 1,vector<int>(n + 1));
dp[0][0] = 1;
dp[0][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];
}
}
return dp[m][n];
}
};
算法12.字典树Trie
LeetCode208.实现Trie(前缀树)
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True
class Trie {
private:
bool isEnd;
Trie *next[26];
public:
/** Initialize your data structure here. */
Trie() {
isEnd = false;
memset(next,0,sizeof(next));
}
/** Inserts a word into the trie. */
void insert(string word) {
Trie *node = this;
for(char c:word){
if(node -> next[c - 'a'] == NULL){
node -> next[c - 'a'] = new Trie();
}
node = node -> next[c - 'a'];
}
node -> isEnd = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Trie *node = this;
for(char c:word){
node = node -> next[c - 'a'];
if(node == NULL){
return false;
}
}
return node -> isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Trie *node = this;
for(char c:prefix){
node = node -> next[c - 'a'];
if(node == NULL){
return false;
}
}
return true;
}
};

浙公网安备 33010602011771号