Leetcode 算法入门

Leetcode 算法入门(C++)

每题第一个代码均为本人所写代码,并非最优解,部分解法参考题解。

第 1 天 二分查找

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size()-1;
while (l <= r)
{
int m = (l + r) / 2;
if (target == nums[m])
{
return m;
}
else if (target < nums[m])
{
r = m - 1;
}
else
{
l = m + 1;
}
}
return -1;
}
};
  • 时间复杂度O(logN)O(logN)
  • 数组,二分查找

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);

class Solution {
public:
int firstBadVersion(int n) {
long long int l=1,r=n,ans=0;
while(l<=r)
{
long long int m=(l+r)/2;
if(isBadVersion(m))
{
ans=m;
r=m-1;
}
else
{
l=m+1;
}
}
return ans;
}
};
  • 时间复杂度O(logN)O(logN)
  • 二分查找,交互

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l=0,r=nums.size()-1,m;
while(l<=r)
{
m=(l+r)/2;
if(nums[m]==target)
{
return m;
}
else if(nums[m]>target)
{
r=m-1;
}
else
{
l=m+1;
}
}
if(target>nums[m])
return m+1;
else
return m;
}
};
  • 时间复杂度O(logN)O(logN)
  • 数组,二分查找

第 2 天 双指针

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int r=nums.size()-1;
vector<int> result(nums.size(),0);
for(int i=0,j=r;i<=j; )
{
if(nums[i]*nums[i]<nums[j]*nums[j])
{
result[r] = nums[j]*nums[j];
j--;
}
else
{
result[r] = nums[i]*nums[i];
i++;
}
r--;
}
return result;
}
};
  • 时间复杂度O(n)O(n)
  • 数组,双指针,排序

189. 轮转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
void reverse(vector<int>& A,int i,int j)
{
while(i<j)
{
int temp = A[j];
A[j]=A[i];
A[i]=temp;
i++;
j--;
}
}
//整体翻转一次,按分割线两边翻转一次
void rotate(vector<int>& nums, int k) {
//防循环
k=k%nums.size();
int r=nums.size()-1;
reverse(nums,0,r);
reverse(nums,0,k-1);
reverse(nums,k,r);
}
};
  • 时间复杂度O(n)O(n)
  • 空间复杂度O(1)O(1)
  • 数组,数学,双指针

第 3 天 双指针

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int len=nums.size();
int j=0;
for(int i=0;i<len;i++)
{
if(nums[i]!=0)
nums[j++]=nums[i];
}
while(j<len)
{
nums[j++]=0;
}
}
};
  • 时间复杂度O(n)O(n)
  • 空间复杂度O(1)O(1)
  • 数组

167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int i=0,j=numbers.size()-1;
bool k=0;
while(i<j)
{
if(target==numbers[i]+numbers[j])
{
break;
}
else if(target<numbers[i]+numbers[j])
{
j--;
}
else
{
i++;
}
}
return {i+1,j+1};
}
};
  • 时间复杂度O(n)O(n)
  • 空间复杂度O(1)O(1)
  • 数组,指针,二分查找

第 4 天 双指针

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public:
void reverseString(vector<char>& s) {
int i=0,j=s.size()-1;
for(;i<j;i++,j--)
{
char temp=s[i];
s[i]=s[j];
s[j]=temp;
}
}
};
  • 时间复杂度O(n)O(n)
  • 空间复杂度O(1)O(1)
  • 双指针,字符串

557. 反转字符串中的单词 III

给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
void reverseString(string& s,int l,int r) {
int i=l,j=r;
for(;i<j;i++,j--)
{
char temp=s[i];
s[i]=s[j];
s[j]=temp;
}
}
string reverseWords(string s) {
int l=0,r=0;
int len=s.length();
for(int k=0;k<=len;k++)
{
if(s[k]==' '||k==len)
{
r=k-1;
reverseString(s,l,r);
l=k+1;
}
}
return s;
}
};
  • 时间复杂度O(n)O(n)
  • 空间复杂度O(1)O(1)
  • 双指针,字符串

第 5 天 双指针

876. 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
int i=1;
ListNode *now=head;
while(now->next)
{
now=now->next;
ListNode *temp=now;
bool flag=0;
for(int j=0;j<=i;j++)
{
temp=temp->next;
if(temp==nullptr)
{
flag=1;
break;
}
}
if(flag==1)
{
break;
}
i++;
}
return now;
}
};
  • 时间复杂度O(n2)O(n^2)
  • 链表,双指针

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//快慢指针
ListNode *slow=head,*fast=head;
for(int i=0;i<n;i++)
{
fast=fast->next;
}
if(fast==nullptr)
{
head=head->next;
}
while(fast!=nullptr&&fast->next!=nullptr)
{
slow=slow->next;
fast=fast->next;
}
if(slow->next!=nullptr)
slow->next=slow->next->next;
else
slow->next=nullptr;
return head;
}
};
  • 时间复杂度:O(n)O(n)
  • 链表,双指针

第 6 天 滑动窗口

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len=s.length();
map<char,int> h;
int ans=0,l=0,r=0;
for(int i=0;i<len;i++)
{

if(h.find(s[i])==h.end())
{
//字符s[i]最近出现的位置
h[s[i]]=i;
}
else
{
l=max(h[s[i]]+1,l);
h[s[i]]=i;
}
r=i;
if(r-l+1>ans)
ans=r-l+1;
}
return ans;
}
};
  • 时间复杂度O(n)O(n)
  • 哈希表,字符串,滑动窗口

567. 字符串的排列

  • 参考了题解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
//s1 和 s2 仅包含小写字母
bool checkInclusion(string s1, string s2) {
int len1=s1.length(),len2=s2.length();
//异常情况
if(len1>len2)
return false;
int windowSize=len1;
//s1的频率分布字典
vector<int> vec1(26,0);
//动态窗口的频率分布字典
vector<int> vecWin(26,0);
//构建字典
for(int i=0;i<windowSize;i++)
{
vec1[s1[i]-'a']++;
vecWin[s2[i]-'a']++;
}
//滑动窗口
for(int i=windowSize-1;i<len2;i++)
{
if(vec1==vecWin)
return true;
vecWin[s2[i+1-windowSize]-'a']--;
if(i<len2-1)
vecWin[s2[i+1]-'a']++;;
}
return false;
}
};
  • 时间复杂度O(n)O(n)
  • 哈希表,字符串

第 7 天 广度优先搜索 / 深度优先搜索

733. 图像渲染

有一幅以 m x n 的二维整数数组表示的图画 image ,其中 image[i][j] 表示该图画的像素值大小。

你也被给予三个整数 sr , sc 和 newColor 。你应该从像素 image[sr][sc] 开始对图像进行 上色填充 。

为了完成 上色工作 ,从初始像素开始,记录初始坐标的 上下左右四个方向上 像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应 四个方向上 像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为 newColor 。

最后返回 经过上色渲染后的图像 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
void dfs(vector<vector<int>>& image, int sr, int sc, int newColor,unordered_map<int,int> vis)
{
vis[sr*50+sc]=1;
int mx[4]={1,-1,0,0},my[4]={0,0,1,-1};
for(int i=0;i<4;i++)
{
int x1=sr+mx[i],y1=sc+my[i];
if(x1>=0&&x1<image.size()&&y1>=0&&y1<image[sr].size()&&vis[x1*50+y1]==0&&image[sr][sc]==image[x1][y1])
{
dfs(image,sr+mx[i],sc+my[i],newColor,vis);
}
}
if(image[sr][sc]!=newColor)
image[sr][sc]=newColor;
}
//x上下,y左右
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {
unordered_map<int,int> vis;
dfs(image,sr,sc,newColor,vis);
return image;
}
};
  • 深度优先搜索,数组

695. 岛屿的最大面积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
int dfs(vector<vector<int>>& grid,int x,int y,int count)
{
if(x<0||x>=grid.size()||y<0||y>=grid[0].size()||grid[x][y]==0)
return count;
else
{
grid[x][y]=0;
count++;
count=max(count,dfs(grid,x+1,y,count));
count=max(count,dfs(grid,x-1,y,count));
count=max(count,dfs(grid,x,y-1,count));
count=max(count,dfs(grid,x,y+1,count));
return count;
}
}
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans=0;
for(int i=0;i<grid.size();i++)
for(int j=0;j<grid[0].size();j++)
{
if(grid[i][j])
ans=max(ans,dfs(grid,i,j,0));
}
return ans;
}
};
  • 深度优先搜索

第 8 天 广度优先搜索 / 深度优先搜索

617. 合并二叉树

给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 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:
//前序遍历
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1==nullptr) return root2;
if(root2==nullptr) return root1;
root1->val+=root2->val;
root1->left=mergeTrees(root1->left,root2->left);
root1->right=mergeTrees(root1->right,root2->right);
return root1;
}
};
  • 时间复杂度: O(max(n1,n2))n1n2分别为树的结点个数O(max(n1,n2)),n1、n2分别为树的结点个数

116. 填充每个节点的下一个右侧节点指针

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;

Node() : val(0), left(NULL), right(NULL), next(NULL) {}

Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/

class Solution {
public:
Node* connect(Node* root) {
//当前层
queue<Node*> now;
now.push(root);
Node* p=NULL;
while(!now.empty())
{
//下一层
queue<Node*> nextLay;
Node* temp=NULL;
while(!now.empty())
{
p=now.front();
now.pop();
if(p)
{
if(p->left)
nextLay.push(p->left);
if(p->right)
nextLay.push(p->right);
}
if(temp)
{
temp->next=p;
}
temp=p;
}
if(p)
p->next=NULL;
now=nextLay;
}
return root;
}
};

第 9 天 广度优先搜索 / 深度优先搜索

542. 01 矩阵

给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。

两个相邻元素间的距离为 1 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
//以0为第一层进行广度优先搜索
int row_size=mat.size();
int col_size=mat[0].size();
queue<pair<int,int>> q;
for(int i=0;i<row_size;i++)
{
for(int j=0;j<col_size;j++)
{
if(mat[i][j]==0)
{
//emplace 可以直接传入构造对象需要的元素, 然后自己调用其构造函数
q.emplace(i,j);
}
else
{
//未访问标记
mat[i][j]=-1;
}
}
}
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
while(!q.empty())
{
auto [x,y]=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<row_size&&newy>=0&&newy<col_size&&mat[newx][newy]==-1)
{
mat[newx][newy]=mat[x][y]+1;
q.emplace(newx,newy);
}
}
}
return mat;
}
};
  • 广度优先搜索,数组

994. 腐烂的橘子

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
//以腐烂橘子第一层进行广度优先搜索
int row_size=grid.size();
int col_size=grid[0].size();
queue<pair<int,int>> q;
//腐烂时间
int t[row_size][col_size];
memset(t,0,sizeof(t));
for(int i=0;i<row_size;i++)
{
for(int j=0;j<col_size;j++)
{
if(grid[i][j]==2)
{
//emplace 可以直接传入构造对象需要的元素, 然后自己调用其构造函数
q.emplace(i,j);
t[i][j]=0;
}
}
}
int ans=0;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
while(!q.empty())
{
auto [x,y]=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int newx=x+dx[i];
int newy=y+dy[i];
if(newx>=0&&newx<row_size&&newy>=0&&newy<col_size&&grid[newx][newy]==1)
{
//腐烂并更新时间
grid[newx][newy]=2;
t[newx][newy]=t[x][y]+1;
ans=max(ans,t[newx][newy]);
q.emplace(newx,newy);
}
}
}
for(int i=0;i<row_size;i++)
{
for(int j=0;j<col_size;j++)
{
if(grid[i][j]==1)
return -1;
}
}
return ans;
}
};

第 10 天 递归 / 回溯

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
if(list1->val<list2->val) //留下小的(返回头节点即可)
{
list1->next=mergeTwoLists(list1->next,list2);
return list1;
}
else
{
list2->next=mergeTwoLists(list1,list2->next);
return list2;
}
}
};
  • 时间复杂度:O(n+m),其中n、m分别为两链表长度
  • 递归,链表

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* res=NULL;
ListNode* cur=head;
while(cur!=NULL)
{
ListNode* temp=cur;
cur=cur->next;
temp->next=res;
res=temp;
}
return res;
}
};

第 11 天 递归 / 回溯

77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
vector<vector<int>> ans;
int num_max,len_max;
void addNumber(vector<int> vec,int num)
{
vec.push_back(num);
if(vec.size()==len_max)
ans.push_back(vec);
//回溯+减枝
else if(num<num_max&&num_max-num>=len_max-vec.size())
{
for(int i=num+1;i<=num_max;i++)
{
addNumber(vec,i);
}
}
}
vector<vector<int>> combine(int n, int k) {
num_max=n;
len_max=k;
vector<int> vec;
for(int i=1;i<=num_max-len_max+1;i++)
addNumber(vec,i);
return ans;
}
};
  • 回溯
  • 时间复杂度O(Cnk)O(C_n^k)

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
int len_max;
vector<vector<int>> ans;
unordered_map<int,int> used;
void backtrack(vector<int> vec,vector<int> nums,unordered_map<int,int> used,int len_max)
{
if(vec.size()==len_max)
{
ans.push_back(vec);
return;
}
else
{
for(int i=0;i<len_max;i++)
{
if(used[i]==0)
{
vec.push_back(nums[i]);
used[i]=1;
backtrack(vec,nums,used,len_max);
used[i]=0;
vec.pop_back();
}
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
len_max=nums.size();
vector<int> vec;
backtrack(vec,nums,used,len_max);
return ans;
}
};
  • 数组,回溯

  • 时间复杂度O(nn!)O(n*n!)

784. 字母大小写全排列

给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。

返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
vector<string> ans;
void backTrack(string S,int i)
{
if(i==S.size())
{
ans.push_back(S);
return;
}
if(!isalpha(S[i]))
backTrack(S,i+1);
else
{
S[i]=tolower(S[i]);
backTrack(S,i+1);
S[i]=toupper(S[i]);
backTrack(S,i+1);
}
}
vector<string> letterCasePermutation(string s) {
backTrack(s,0);
return ans;
}
};
  • 时间复杂度O(2k)O(2^k),k为字母个数

第 12 天 动态规划

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
//递推公式 dp[i]=dp[i-1]+dp[i-2]
int climbStairs(int n) {
if(n<=2)
return n;
int dp[2];
dp[0]=1;
dp[1]=2;
for(int i=3;i<=n;i++)
{
int sum=dp[0]+dp[1];
dp[0]=dp[1];
dp[1]=sum;
}
return dp[1];
}
};
  • 时间复杂度O(n),空间复杂度O(1)
  • 记忆化搜索,数学,动态规划

198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
int rob(vector<int>& nums) {
int ans=0;
int len=nums.size();
if(len==1)
{
return nums[0];
}
if(len==2)
{
return max(nums[0],nums[1]);
}
int dp[3];
dp[0]=nums[0];
dp[1]=nums[1];
dp[2]=nums[0]+nums[2];
for(int i=3;i<len;i++)
{
int temp0=dp[1];
int temp1=dp[2];
dp[2]=max(max(dp[0],dp[1])+nums[i],dp[2]);
dp[0]=temp0;
dp[1]=temp1;
}
ans=max(max(dp[0],dp[1]),dp[2]);
return ans;
}
};
  • 时间复杂度O(n),空间复杂度O(1)
  • 数组,动态规划

120. 三角形最小路径和

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
//自底向上计算最短路径和
int minimumTotal(vector<vector<int>>& triangle) {
for(int i=triangle.size()-2;i>=0;i--)
{
for(int j=0;j<triangle[i].size();j++)
{
triangle[i][j]+=min(triangle[i+1][j],triangle[i+1][j+1]);
}
}
return triangle[0][0];
}
};
  • 时间复杂度O(n),n=triangle.size()
  • 数组,动态规划

第 13 天 位运算

231. 2 的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。

如果存在一个整数 x 使得 n == 2^x ,则认为 n 是 2 的幂次方。

1
2
3
4
5
6
class Solution {
public:
bool isPowerOfTwo(int n) {
return n>0 && !(n&(n-1));
}
};
  • 时间复杂度O(1)
  • 位运算,递归,数学

191. 位1的个数

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public:
int hammingWeight(uint32_t n) {
int cnt=0;
while(n)
{
cnt+=n&1;
n>>=1;
}
return cnt;
}
};
  • 时间复杂度O(1)
  • 位运算

第 14 天 位运算

190. 颠倒二进制位

颠倒给定的 32 位无符号整数的二进制位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t ans=0;
int t=32;
while(t--)
{
ans<<=1;
ans+=n&1;
n>>=1;
}
return ans;
}
};
  • 时间复杂度O(1)
  • 位运算

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public:
//相同的数异或为0
int singleNumber(vector<int>& nums) {
int ans=0;
for(int i=0;i<nums.size();i++)
{
ans^=nums[i];
}
return ans;
}
};
  • 位运算,数组

本博客所有文章除特别声明外,均为博客作者本人编写整理,转载请联系作者!