给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104
题解
func Swap(x *int, y *int){
temp := *x
*x = *y
*y = temp
}
func Partition(nums *[]int, i int,j int) int{
idx := rand.Int()%(j-i+1)+i
Swap(&(*nums)[i],&(*nums)[idx])
x := (*nums)[i]
for i<j{
for i<j && (*nums)[j] >= x{
j--
}
(*nums)[i] = (*nums)[j]
for i<j && (*nums)[i]<=x {
i++
}
(*nums)[j] = (*nums)[i]
}
(*nums)[i] = x
return i
}
func quickSort(nums *[]int, s int, e int){
var m int
if (s<e){
m = Partition(nums,s,e)
quickSort(nums,s,m-1)
quickSort(nums,m+1,e)
}
}
func sortArray(nums []int) []int {
if len(nums) <=1 {
return nums
}
rand.New(rand.NewSource(time.Now().UnixNano()))
i,j := 0,len(nums)-1
quickSort(&nums,i,j)
return nums
}
难度:简单
环形公交路线上有 n
个站,按次序从 0
到 n - 1
进行编号。我们已知每一对相邻公交站之间的距离,distance[i]
表示编号为 i
的车站和编号为 (i + 1) % n
的车站之间的距离。
环线上的公交车都可以按顺时针和逆时针的方向行驶。
返回乘客从出发点 start
到目的地 destination
之间的最短距离。
示例 1:
输入:distance = [1,2,3,4], start = 0, destination = 1
输出:1
解释:公交站 0 和 1 之间的距离是 1 或 9,最小值是 1。
示例 2:
输入:distance = [1,2,3,4], start = 0, destination = 2
输出:3
解释:公交站 0 和 2 之间的距离是 3 或 7,最小值是 3。
示例 3:
输入:distance = [1,2,3,4], start = 0, destination = 3
输出:4
解释:公交站 0 和 3 之间的距离是 6 或 4,最小值是 4。
提示:
1 <= n <= 10^4
distance.length == n
0 <= start, destination < n
0 <= distance[i] <= 10^4
题解
计算start-end的加和,计算这个范围外的加和,输出较小者。
class Solution {
public:
int distanceBetweenBusStops(vector& nums, int start, int end) {
int d1 = 0, d2 = 0;
int l = min(start,end);
int r = max(start,end);
for(int i=0;i=l && i
难度:简单
给你一个 严格升序排列 的正整数数组 arr
和一个整数 k
。
请你找到这个数组里第 k
个缺失的正整数。
示例 1:
输入:arr = [2,3,4,7,11], k = 5
输出:9
解释:缺失的正整数包括 [1,5,6,8,9,10,12,13,...] 。第 5 个缺失的正整数为 9 。
示例 2:
输入:arr = [1,2,3,4], k = 2
输出:6
解释:缺失的正整数包括 [5,6,7,...] 。第 2 个缺失的正整数为 6 。
提示:
1 <= arr.length <= 1000
1 <= arr[i] <= 1000
1 <= k <= 1000
1 <= i < j <= arr.length
的 i
和 j
满足 arr[i] < arr[j]
题解
方法一:
相当于找空座位
int findKthPositive(vector& a, int k) {
for(int i=0;i
方法二:二分法(需要找到各个元素与缺失元素个数的关系)
int findKthPositive(vector& arr, int k) {
if (arr[0] > k) {
return k;
}
int l = 0, r = arr.size();
while (l < r) {
int mid = l+((r-l) >> 1);
int x = mid < arr.size() ? arr[mid] : INT_MAX;
if (x - mid - 1 >= k) {
r = mid;
} else {
l = mid + 1;
}
}
return k - (arr[l - 1] - (l - 1) - 1) + arr[l - 1];
}
难度: 困难
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
示例 2:
输入:nums = [3,4,-1,1]
输出:2
示例 3:
输入:nums = [7,8,9,11,12]
输出:1
提示:
1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1
题解
class Solution {
public:
int firstMissingPositive(vector& nums) {
int n=nums.size();
for(int i=0;i0&&nums[i]
难度: 简单
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
题解
int findRepeatNumber(vector& a) {
if(a.size()<2)return -1;
for(int i=0;i
难度: 简单
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
题解
class Solution {
public:
int singleNumber(vector& nums) {
int ans=0;
for(const int&e:nums)ans ^=e;
return ans ;
}
};
难度: 简单
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
题解
一次切分
class Solution {
public:
void moveZeroes(vector& nums) {
if(nums.size()<2)return;
int i=-1;
for(int j=0;j& nums,int start,int end){
int x=nums[end];
int i=start-1;
for(int j=start;j
难度: 简单
给你两个有序整数数组 nums1
和 nums2
,请你将 nums2
合并到 nums1
中*,*使 nums1
成为一个有序数组。
初始化 nums1
和 nums2
的元素数量分别为 m
和 n
。你可以假设 nums1
的空间大小等于 m + n
,这样它就有足够的空间保存来自 nums2
的元素。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[i] <= 109
题解
逆向遍历,如果前一个数组为空,则直接放第二个数组的元素。
特殊示例:
(1)
[0,0,0] [1,2,3] //m:0 n:3
(2)
[1,2,3,0] [1] //m:4 n:1
class Solution {
public:
void merge(vector& nums1, int m, vector& nums2, int n) {
int last = m+n-1; //合并后元素的个数
while(n){ //注意n
if(m==0||nums1[m-1]<=nums2[n-1]){ //注意nums2
nums1[last--] = nums2[--n];
}
else{
nums1[last--] = nums1[--m];
}
}
}
};
难度:中等
给你一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
题解
vector spiralOrder(vector>& matrix) {
if(matrix.empty())return {};
vectorans;
int n=matrix[0].size();
int m=matrix.size();
int z=0,y=n-1,s=0,x=m-1; //左右上下
int i=0;
while(1){
//①从左到右
for(i=z;i<=y ;++i)ans.emplace_back(matrix[s][i]);
//②从上到下
if(++s>x)break; //判断是否越界
for(i=s; i<=x;++i)ans.emplace_back(matrix[i][y]);
//③从右到左
if(--y=z;--i) ans.emplace_back(matrix[x][i]);
//④从下往上
if(--x=s;--i)ans.emplace_back(matrix[i][z]);
if(++z>y)break; /*!!!*/
}
return ans;
}
难度:中等
给定一个 n × n 的二维矩阵 matrix
表示一个图像。请你将图像顺时针旋转 90 度。
你必须在** 原地** 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
示例 3:
输入:matrix = [[1]]
输出:[[1]]
示例 4:
输入:matrix = [[1,2],[3,4]]
输出:[[3,1],[4,2]]
提示:
matrix.length == n
matrix[i].length == n
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
题解
class Solution {
public:
void rotate(vector>& matrix) {
int n=matrix.size();
for(int i=0;i<(n/2);++i)matrix[i].swap(matrix[n-i-1]) ;//先上下反转
for(int i=0;i>& matrix) {
int n = matrix.size();
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < (n + 1) / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
};
难度:简单
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
说明:
题解
//调库函数:time: O(mlogm+nlogn) space: O(logm+logn)
#define all(x) begin(x), end(x) //!!!
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
vector ret;
sort(all(nums1));
sort(all(nums2));
set_intersection(all(nums1), all(nums2), back_inserter(ret));
ret.erase(unique(all(ret)), end(ret));
return ret;
}
};
//hash表法:time: O(n) space: O(n)
class Solution {
public:
std::unordered_map map;
vector ans;
vector intersection(vector& nums1, vector& nums2) {
for(int i = 0;i
难度中等1002
给定一个数组,将数组中的元素向右移动 k
个位置,其中 k
是非负数。
进阶:
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
题解
class Solution {
public:
void rotate(vector& a, int k) {
int n=a.size();
k%=n;
if (n<2||k==0)return;
reverse(a.begin(),a.end());
reverse(a.begin(),a.begin()+k);
reverse(a.begin()+k,a.end());
}
};
难度中等432
给定一个二进制数组 nums
, 找到含有相同数量的 0
和 1
的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
提示:
1 <= nums.length <= 105
nums[i]
不是 0
就是 1
题解
//前缀和问题,基础解法
class Solution {
public:
int findMaxLength(vector& nums) {
int res=0,sum=0;
unordered_mapumap; //key: prefix sum, v: index
umap[0]=-1;
for(int i=0;i& nums) {
int res=0,sum=0;
const int n=nums.size();
vectorhash(2*n+1,-2);//存放下标,初始化为全-2
hash[n]=-1;
for(int i=0;i
难度简单1684收藏分享切换为英文接收动态反馈
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
0 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i]
仅由小写英文字母组成题解
class Solution {
public:
string longestCommonPrefix(vector& strs) {
if(strs.empty()) return string();
sort(strs.begin(), strs.end());
string start = strs.front(), end = strs.back();
int i, num = min(start.size(), end.size());
for(i = 0; i < num && start[i] == end[i]; i++);
return start.substr(0,i);
}
};
难度简单204
在二维平面上,有一个机器人从原点 (0, 0) 开始。给出它的移动顺序,判断这个机器人在完成移动后是否在 (0, 0) 处结束。
移动顺序由字符串表示。字符 move[i] 表示其第 i 次移动。机器人的有效动作有 R
(右),L
(左),U
(上)和 D
(下)。如果机器人在完成所有动作后返回原点,则返回 true。否则,返回 false。
**注意:**机器人“面朝”的方向无关紧要。 “R” 将始终使机器人向右移动一次,“L” 将始终向左移动等。此外,假设每次移动机器人的移动幅度相同。
示例 1:
输入: "UD"
输出: true
解释:机器人向上移动一次,然后向下移动一次。所有动作都具有相同的幅度,因此它最终回到它开始的原点。因此,我们返回 true。
示例 2:
输入: "LL"
输出: false
解释:机器人向左移动两次。它最终位于原点的左侧,距原点有两次 “移动” 的距离。我们返回 false,因为它在移动结束时没有返回原点。
题解
class Solution {
public:
bool judgeCircle(string moves) {
int x = 0, y = 0;
for (int i = 0; i < moves.size(); i++) {
if (moves[i] == 'U') y++;
if (moves[i] == 'D') y--;
if (moves[i] == 'L') x--;
if (moves[i] == 'R') x++;
}
if (x == 0 && y == 0) return true;
return false;
}
};
难度中等3487
给你一个包含 n
个整数的数组 nums
,判断 nums
中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0
且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
题解
class Solution {
public:
vector> threeSum(vector& nums) {
vector> ans;//首先创建一个存放一个符合题意的一个三数组
int n=nums.size();
if(n<3){ //如果这个数组小于3三个元素 不可能满足
return {};
}
sort(nums.begin(),nums.end());//对这个数组进行排序
for(int i=0;i0){return ans;} //这是排序过的 如果第一个数大于0 那么后面的数没有负的 就不可能三个数相加等于0;
if(i>0&&nums[i]==nums[i-1]){continue;}//如果这个数前面用过了 就跳过
int l=i+1;//左指针
int r=n-1;//右指针
// 进入双指针法找到 i后面 两个数之和=-i的;
while(l-nums[i]){
r--;
}
//如果两数之和小 就要增加 左指针向右收缩
else if(nums[l]+nums[r]<-nums[i]){
l++;
}
//如果相等
else{
ans.push_back(vector{nums[i],nums[l],nums[r]});//将符合的 三个坐标插入 我们的答案二维数组
//然后收缩指针 看看 之间还有没有 符合的
l++;
r--;
//在找到相等的情况下,有数字重复就跳过
while(l
难度中等820
给定一个包括 n 个整数的数组 nums
和 一个目标值 target
。找出 nums
中的三个整数,使得它们的和与 target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
题解
class Solution {
public:
int threeSumClosest(vector& nums, int target) {
int n=nums.size();
sort(nums.begin(),nums.end());
int l,r,sum,ans=nums[0]+nums[1]+nums[2];
for(int i=0;i0 &&nums[i-1] == nums[i]) continue;
l = i+1;
r = n-1;
while(ltarget)r--;
else l++;
}
}
return ans;
}
};
难度中等
给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1)
,右下角为 (row2, col2)
。
上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = **(4, 3),**该子矩形内元素的总和为 8。
示例:
给定 matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]
sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12
提示:
sumRegion
方法*。*row1 ≤ row2
且 col1 ≤ col2
。题解
using namespace std;
#include
class NumMatrix {
private:
vector > sums;
public:
NumMatrix(vector >& matrix) {
int n = matrix.size();
int m = matrix[0].size();
if (n == 0 || m == 0) {
return;
}
sums.resize(n + 1, vector(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
sums[i][j] = matrix[i - 1][j- 1] + sums[i - 1][j] + sums[i][j - 1] - sums[i - 1][j - 1];
}
}
return;
}
int sumRegion(int row1, int col1, int row2, int col2) {
return sums[row2 + 1][col2 + 1] - sums[row2 + 1][col1] - sums[row1][col2 + 1] + sums[row1][col1];
}
};
/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix* obj = new NumMatrix(matrix);
* int param_1 = obj->sumRegion(row1,col1,row2,col2);
*/
难度中等250
如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。
例如,以下数列为等差数列:
1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下数列不是等差数列。
1, 1, 2, 5, 7
数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P 如果满足以下条件,则称子数组(P, Q)为等差数组: 元素 A[P], A[p + 1], …, A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。 函数要返回数组 A 中所有为等差数组的子数组个数。 示例: 题解 难度: 中等 实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 必须** 原地 **修改,只允许使用额外常数空间。 示例 1: 示例 2: 示例 3: 示例 4: 提示: 题解 逆向遍历找不符合 nums[i+1]<=nums[i] 的第一个不符合的index,然后逆序遍历找到不符合 nums[i]>=nums[j] 的index,交换 nums[i]和nums[j] ,反转 [begin+i+1,end) 的元素。 难度中等219 给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。 示例: 说明: 题解 fx=1,代表方向为斜上↗; fx=-1,代表方向为斜下↙; 难度中等258 给你一个字符数组 从一个空字符串 压缩后得到的字符串 请在 修改完输入数组后 ,返回该数组的新长度。 你必须设计并实现一个只使用常量额外空间的算法来解决此问题。 示例 1: 示例 2: 示例 3: 提示: 题解 给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 示例 2: 题解 难度中等362收藏分享切换为英文接收动态反馈 给定一个大小为 n 的整数数组,找出其中所有出现超过 **进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。 示例 1: 示例 2: 示例 3: 题解 给你一个整数数组 如果存在则返回 示例 1: 示例 2: 示例 3: 题解 难度中等839收藏分享切换为英文接收动态反馈 给定一个未排序的整数数组 请你设计并实现时间复杂度为 示例 1: 示例 2: 提示: 题解 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 示例 2: 示例 3: 示例 4: 题解 难度: 中等 给你一个二元数组 子数组 是数组的一段连续部分。 示例 1: 示例 2: 提示: 题解 难度:简单 罗马数字包含以下七种字符: 例如, 罗马数字 2 写做 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 题解 难度中等175收藏分享切换为英文接收动态反馈 给你一个整数数组 如果某个 连续 子数组中恰好有 请返回这个数组中「优美子数组」的数目。 示例 1: 示例 2: 示例 3: 提示: 题解 前缀和 难度:简单 给你一个整数数组 数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端,那么左侧数之和视为 如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等153收藏分享切换为英文接收动态反馈 这里有 有一份航班预订表 请你返回一个长度为 示例 1: 示例 2: 提示: 题解 难度中等438 给定一个二进制数组 示例 1: 示例 2: 提示: 题解 前缀和三部曲: 题目描述: 注意: 题解 难度:简单 给定一个整数数组 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等674 罗马数字包含以下七种字符: 例如, 罗马数字 2 写做 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 给你一个整数,将其转为罗马数字。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 题解 链表定义 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。 示例: 限制: 题解 难度中等621 给定一个单链表 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例 1: 示例 2: 提示: 题解 难度困难1257 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。 k 是一个正整数,它的值小于或等于链表的长度。 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。 进阶: 示例 1: 示例 2: 示例 3: 示例 4: 提示: 题解 难度简单245 输入两个链表,找出它们的第一个公共节点。 如下面的两个链表**:** 在节点 c1 开始相交。 示例 1: 示例 2: 示例 3: 注意: 题解 给定一个链表,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 如果链表中存在环,则返回 true 。 否则,返回 false 。 进阶: 你能用 O(1)(即,常量)内存解决此问题吗? 编写一个程序,找到两个单链表相交的起始节点。 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 进阶:你能尝试使用一趟扫描实现吗? 示例 1: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xLuuit29-1630453061437)(C:\Users\bj\AppData\Roaming\Typora\typora-user-images\image-20210529215153342.png)] 示例 2: 示例 3: 题解 难度简单608收藏分享切换为英文接收动态反馈 存在一个按升序排列的链表,给你这个链表的头节点 返回同样按升序排列的结果链表。 示例 1: 示例 2: 提示: 题解 难度中等662 存在一个按升序排列的链表,给你这个链表的头节点 返回同样按升序排列的结果链表。 示例 1: 示例 2: 提示: 题解 给你一个链表的头节点 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1: 示例 2: 题解 编写一个函数,检查输入的链表是否是回文的。 示例 1: 示例 2: 进阶: 难度中等959 给你单链表的头指针 示例 1: 示例 2: 提示: 题解 难度困难1390收藏分享切换为英文接收动态反馈 给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等986 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例 1: 示例 2: 示例 3: 提示: **进阶:**你能在不修改链表节点值的情况下解决这个问题吗?(也就是说,仅修改节点本身。) 题解 难度简单265 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 示例 1: 示例 2: 提示: 题解 给你两个 没有重复元素 的数组 请你找出 示例 1: 示例 2: 提示: 题解 notes 单调递增栈 单调递减栈 单调栈的作用: 求解数组中元素右边第一个比它小的元素的下标,从前往后,构造单调递增栈; 难度中等439收藏分享切换为英文接收动态反馈 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。 示例 1: 注意: 输入数组的长度不会超过 10000。 题解 难度困难272收藏分享切换为英文接收动态反馈 给定一个数组 示例: 题解 难度中等 给你一个整数数组 请你找出符合题意的 最短 子数组,并输出它的长度。 示例 1: 示例 2: 示例 3: 题解 单调栈正向遍历和反向遍历,找左右边界。 根据 逆波兰表示法,求表达式的值。 有效的算符包括 说明: 示例 1: 示例 2: 示例 3: 提示: 给定一个只包括 有效字符串需满足: 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 难度:简单 设计一个支持 示例: 提示: 题解 难度:简单 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作( 实现 说明: 进阶: 示例: 提示: 题解 难度困难1404 给你一个只包含 示例 1: 示例 2: 示例 3: 提示: 题解 法二 难度中等440 给你一个字符串表达式 整数除法仅保留整数部分。 示例 1: 示例 2: 示例 3: 提示: 题解 难度困难612 给你一个字符串表达式 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等854 给定一个经过编码的字符串,返回它解码后的字符串。 编码规则为: 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k,例如不会出现像 示例 1: 示例 2: 示例 3: 示例 4: 题解 给你一个整数数组 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如, 示例 1: 示例 2: 示例 3: 提示: 进阶: 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 要求时间复杂度为O(n)。 示例1: 提示: 题解 给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 示例 1: 示例 2: 题解 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 示例 1: 示例 2: 难度中等802收藏分享切换为英文接收动态反馈 在一个由 示例 1: 示例 2: 示例 3: 提示: 题解 给定一个字符串 示例 1: 输出: 一个可能的最长回文子序列为 “bbbb”。 示例 2: 输出: 一个可能的最长回文子序列为 “bb”。 题解 难度中等601收藏分享切换为英文接收动态反馈 给定两个字符串 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等373 在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 火车票有三种不同的销售方式: 通行证允许数天无限制的旅行。 例如,如果我们在第 2 天获得一张为期 7 天的通行证,那么我们可以连着旅行 7 天:第 2 天、第 3 天、第 4 天、第 5 天、第 6 天、第 7 天和第 8 天。 返回你想要完成在给定的列表 示例 1: 示例 2: 提示: 题解 状态方程:dp[n]=min( dp[n-1] + cost[0] , dp[n-7] + cost[1] , dp[n-30] + cost[2] ) 难度中等1041收藏分享切换为英文接收动态反馈 一个机器人位于一个 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径? 示例 1: 示例 2: 示例 3: 示例 4: 提示: 题解 圆环上有10个点,编号为0~9。从0点出发,每次可以逆时针和顺时针走一步,问走n步回到0点共有多少种走法。 走n步到0的方案数=走n-1步到1的方案数+走n-1步到9的方案数。 因此,若设dp[i][j]为从0点出发走i步到j点的方案数,则递推式为: ps:公式之所以取余是因为j-1或j+1可能会超过圆环0~9的范围. 题解 给定一个 没有重复数字的序列,返回其所有可能的全排列。 示例: 画图 题解: 输入一个字符串,打印出该字符串中字符的所有排列。 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。 示例: 题解 给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。 示例: 给你一个整数数组 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 示例 2: 题解 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明: 输入:candidates = [2,3,6,7], target = 7, 示例 2: 示例 2: 输入:candidates = [2,3,5], target = 8, 题解 给定一个数组 说明: 示例 1: 示例 2: 题解 难度中等740 给你一个字符串 回文串 是正着读和反着读都一样的字符串。 示例 1: 示例 2: 题解 难度中等624 给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “[email protected]” 是 无效 IP 地址。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 题解 难度中等281 给定一个字符串 提示: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NgnRDdIe-1630453061485)(C:\Users\bj\Desktop\微信图片_20210720195929.jpg)] 题解 难度中等1464 给定一个仅包含数字 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 示例 2: 示例 3: 提示: 题解 给一非空的单词列表,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。 示例 1: 示例 2: 题解 对unordered_map的排序的另一种方法 输出前K大的数据,不要求按顺序排列 给你一个整数数组 示例 1: 示例 2: 题解 难度:中等 给定一个包含红色、白色和蓝色,一共 此题中,我们使用整数 示例 1: 示例 2: 示例 3: 示例 4: 提示: 进阶: 题解 难度简单246 给你一个按 非递减顺序 排序的整数数组 示例 1: 示例 2: 提示: 题解 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxNOuqbQ-1630453061494)(C:\Users\bj\AppData\Roaming\Typora\typora-user-images\image-20210519223748862.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KPcjJLF7-1630453061496)(C:\Users\bj\AppData\Roaming\Typora\typora-user-images\image-20210610154919507.png)] 背包问题解题模板(别人总结) 1、最值问题: dp[i] = max/min(dp[i], dp[i-nums]+1)或dp[i] = max/min(dp[i], dp[i-num]+nums); //说明:属于是最值问题 给定不同面额的硬币 你可以认为每种硬币的数量是无限的。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 题解 //说明:最值问题 给定正整数 n,找到若干个完全平方数(比如 给你一个整数 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如, 示例 1: 示例 2: 题解 这里有 我们约定:掷骰子的得到总点数为各骰子面朝上的数字的总和。 如果需要掷出的总点数为 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 难度:中等 夏日炎炎,小男孩 Tony 想买一些雪糕消消暑。 商店中新到 给你价格数组 **注意:**Tony 可以按任意顺序购买雪糕。 示例 1: 示例 2: 示例 3: 提示: 题解 难度:中等 给你 **说明:**你不能倾斜容器。 示例 1: 示例 2: 示例 3: 示例 4: 提示: 题解 难度:困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 示例 2: 提示: 题解 局部最优解为:当前元素的接的雨水为左边最大值(leftmax)和右边最大值(rightmax)的较小值,以及当前的高度决定。由局部最优解得到全局解。 难度中等844 请根据每日 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等681 在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。 说明: 示例 1: 示例 2: 题解 第 每艘船最多可同时载两人,但条件是这些人的重量之和最多为 返回载到每一个人所需的最小船数。(保证每个人都能被船载)。 示例 1: 示例 2: 示例 3: 提示: 题解 n个人要过河,但是只有一艘船;船每次只能做两个人,每个人有一个单独坐船的过河时间a[i],如果两个人(x和y)一起坐船,那过河时间为a[x]和a[y]的较大值。问最短需要多少时间,才能把所有人送过河?(每趟必须有人乘船回来) 题解 大于5人时,状态转移方程dp[i] = min(dp[i - 1] + a[i] + a[1], dp[i - 2] + a[2] + a[1] + a[i] + a[2]); 再根据我们考虑的1、2、3、4个人的情况,我们分别可以算出dp[i]的初始化值: 难度中等1259 给定一个非负整数数组 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 示例 1: 示例 2: 提示: 题解 难度中等1051 给定一个非负整数数组,你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 假设你总是可以到达数组的最后一个位置。 示例 1: 示例 2: 提示: 题解 难度简单350 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 示例 1: 示例 2: 提示: 题解 因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这个孩子。 难度困难590 老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。 你需要按照以下要求,帮助老师给这些孩子分发糖果: 那么这样下来,老师至少需要准备多少颗糖果呢? 示例 1: 示例 2: 题解 把所有孩子的糖果数初始化为 1;先从左往右遍历一遍,如果右边孩子的评分比左边的高,则右边孩子的糖果数更新为左边孩子的糖果数加 1;再从右往左遍历一遍,如果左边孩子的评分比右边的高,且左边孩子当前的糖果数不大于右边孩子的糖果数,则左边孩子的糖果数更新为右边孩子的糖果数加 1。 难度中等189 给定一个非负整数 (当且仅当每个相邻位数上的数字 示例 1: 示例 2: 示例 3: 说明: 题解 难度中等452 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。 注意: 示例 1: 示例 2: 示例 3: 题解 选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间。因此,我们采取的贪心策略为,优先保留结尾小且不相交的区间。 难度中等1016 以数组 示例 1: 示例 2: 提示: 题解 难度中等431 在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。 一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 给你一个数组 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 题解 难度中等526收藏分享切换为英文接收动态反馈 字符串 示例: 提示: 题解 难度简单365 假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。 给你一个整数数组 示例 1: 示例 2: 提示: 题解 难度简单27 给定 N 个无限容量且初始均空的水缸,每个水缸配有一个水桶用来打水,第 每个水缸对应最低蓄水量记作 注意:实际蓄水量 达到或超过 最低蓄水量,即完成蓄水要求。 示例 1: 输入: 输出: 解释: 示例 2: 输入: 输出: 解释: 提示: 题解 难度中等174 给定一个由整数数组 在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当 此外,子数组最多只能包含固定缓冲区 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 题解 难度中等620 给你一个以字符串表示的非负整数 示例 1 : 示例 2 : 示例 3 : 提示: 题解 思考 n个节点的二叉树或二叉搜索树有多少种形态! 答:符合卡特兰数,即 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LMnbdprs-1630453061508)(C:\Users\bj\AppData\Roaming\Typora\typora-user-images\image-20210703083859289.png)] 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] 示例 1: 示例 2: 题解 难度中等281 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 为了让您更好地理解问题,以下面的二叉搜索树为例: 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。 下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。 特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。 题解 难度:中等 输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 例如,给出 返回如下的二叉树: 限制: 题解 题解 请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 1 示例 1: 示例 2: 限制: 题解 难度简单143收藏分享切换为英文接收动态反馈 请完成一个函数,输入一个二叉树,该函数输出它的镜像。 例如输入: 4 4 示例 1: 限制: 题解 难度中等303 输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构, 即 A中有出现和B相同的结构和节点值。 例如: 示例 1: 示例 2: 限制: 题解 输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。 例如: 给定二叉树 返回它的最大深度 3 。 难度简单61收藏分享切换为英文接收动态反馈 实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两棵子树的高度差不超过 1。 示例 1: 示例 2: 题解 自底向上的方法可以有效减少重复访问 难度简单731收藏分享切换为英文接收动态反馈 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。 示例 : 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 题解 路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 示例 1: 示例 2: 提示: 题解 难度中等473 给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。 注意:两个节点之间的路径长度由它们之间的边数表示。 示例 1: 输入: 输出: 示例 2: 输入: 输出: 注意: 给定的二叉树不超过10000个结点。 树的高度不超过1000。 题解 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 **说明:**叶子节点是指没有子节点的节点。 示例 1: 示例 2: 提示: 题解 在二叉树中,根节点位于深度 如果二叉树的两个节点深度相同,但 父节点不同 ,则它们是一对堂兄弟节点。 我们给出了具有唯一值的二叉树的根节点 只有与值 示例 3: 提示: 题解 在二叉树中查找值为x的节点,找出该节点的所有祖先节点,假设x节点只有一个。 题解 给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与**满二叉树(full binary tree)**结构相同,但一些节点为空。 每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的 示例 1: 示例 2: 示例 3: 示例 4: 注意: 答案在32位有符号整数的表示范围内。 题解 记忆化搜索,每一层的记忆元素如下 难度简单526 给定一个二叉树,返回所有从根节点到叶子节点的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 题解 难度简单611 给你二叉树的根节点 叶子节点 是指没有子节点的节点。 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等520 给你二叉树的根节点 叶子节点 是指没有子节点的节点。 示例 1: 示例 2: 示例 3: 提示: 题解 给定一个二叉树,它的每个结点都存放着一个整数值。 找出路径和等于给定数值的路径总数。 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。 示例: 题解 难度中等375收藏分享切换为英文接收动态反馈 给你一个二叉树的根节点 每条从根节点到叶节点的路径都代表一个数字: 计算从根节点到叶节点生成的 所有数字之和 。 叶节点 是指没有子节点的节点。 示例 1: 示例 2: 提示: 题解 难度:中等 给你二叉树的根结点 示例 1: 示例 2: 示例 3: 提示: 题解 难度:简单 二叉树数据结构 返回转换后的单向链表的头节点。 **注意:**本题相对原题稍作改动 示例: 提示: 题解 难度简单714 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。 示例 1: 注意: 合并必须从两个树的根节点开始。 题解 难度中等480 给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下: 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 初始状态下,所有 next 指针都被设置为 进阶: 示例: 提示: 题解 难度中等21 小扣有一个根结点为 示例 1: 输入: 输出: 示例 2: 输入: 输出: 提示: 题解 难度中等487 给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 示例: 题解 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” 例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5] 示例 1: 示例 2: 题解 难度中等1213收藏分享切换为英文接收动态反馈 给你一个整数 示例 1: 示例 2: 提示: 题解 难度简单138收藏分享切换为英文接收动态反馈 给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。 例如, 你应该返回如下子树: 在上述示例中,如果要找的值是 题解 难度中等67收藏分享切换为英文接收动态反馈 设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。 如果指定节点没有对应的“下一个”节点,则返回 示例 1: 示例 2: 题解 难度中等191 给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。 注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。 示例 1: 示例 2: 示例 3: 提示: 题解 难度简单791 给你一个整数数组 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1: 示例 2: 题解 难度中等548 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。 示例: 题解 给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。 一般来说,删除节点可分为两个步骤: 说明: 要求算法时间复杂度为 O(h),h 为树的高度。 示例: 题解 难度简单193 给你一个二叉搜索树的根节点 示例 1: 示例 2: 提示: 题解 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 假设一个二叉搜索树具有如下特征: 示例 1: 示例 2: 题解 难度中等409收藏分享切换为英文接收动态反馈 给定一个二叉搜索树的根节点 示例 1: 示例 2: 提示: 题解 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 参考以下这颗二叉搜索树: 示例 1: 示例 2: 提示: 题解 给你二叉搜索树的根节点 所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 难度中等67 给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。 如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的 。 如果有多种构造方法,请你返回任意一种。 示例: 提示: 题解 难度中等276 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 为了让您更好地理解问题,以下面的二叉搜索树为例: 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。 下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。 特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。 题解 给定一个 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。 例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。 示例 1: 示例 2: 提示: 难度中等1954 数字 示例 1: 示例 2: 提示: 题解 难度中等561 给你一个 示例 1: 示例 2: 提示: 题解 如果矩阵的四周都是X,那么矩阵中只要有O,肯定是被X包围的。否则,将边界的与O连通的块标记为A。 难度中等315收藏分享切换为英文接收动态反馈 地上有一个m行n列的方格,从坐标 示例 1: 示例 2: 提示: 题解 难度中等1223 给你一个由 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。 示例 1: 示例 2: 提示: 题解 直接在原数组进行标记,将0标记为已经访问。 题目描述 有一个 n \times mn×m 的棋盘,在某个点 (x, y)(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。 输入格式 输入只有一行四个整数,分别为 n, m, x, yn,m,x,y。 输出格式 一个 n \times mn×m 的矩阵,代表马到达某个点最少要走几步(左对齐,宽 55 格,不能到达则输出 -1−1)。 输入输出样例 **输入 ** 输出 题解 难度: 中等 给你两个 如果 请你返回 示例 1: 示例 2: 提示: 题解 难度中等504收藏分享切换为英文接收动态反馈 给定一个包含了一些 一个 岛屿 是由一些相邻的 找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 示例 1: 对于上面这个给定矩阵应返回 示例 2: 对于上面这个给定的矩阵, 返回 题解 难度: 简单 小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下: 给定总玩家数 示例 1: 输入: 输出: 解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。 示例 2: 输入: 输出: 解释:信息不能从小 A 处经过 2 轮传递到编号 2 限制: 题解 难度中等359收藏分享切换为英文接收动态反馈 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: 锁的初始数字为 列表 字符串 示例 1: 示例 2: 示例 3: 示例 4: 提示: 题解 难度困难794收藏分享切换为英文接收动态反馈 字典 给你两个单词 示例 1: 示例 2: 提示: 题解 难度中等788 给定一个三角形 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 示例 1: 示例 2: 提示: 进阶: 题解 难度中等77 这里有一个非负整数数组 请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。 注意,不管是什么情况下,你都无法跳到数组之外。 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等446 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 示例 1: 示例 2: 提示: 题解 q;
int m = matrix.size(), n = matrix[0].size();
vector 难度中等379收藏分享切换为英文接收动态反馈 在给定的网格中,每个单元格可以有以下三个值之一: 每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。 返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 示例 1: 示例 2: 示例 3: 提示: 题解 1.istringstream 2.strtok 难度中等1044 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 说明: 示例 1: 示例 2: 难度困难794收藏分享切换为英文接收动态反馈 字典 给你两个单词 示例 1: 示例 2: 提示: 题解 难度中等788 给定一个三角形 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 示例 1: 示例 2: 提示: 进阶: 题解 难度中等77 这里有一个非负整数数组 请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。 注意,不管是什么情况下,你都无法跳到数组之外。 示例 1: 示例 2: 示例 3: 提示: 题解 难度中等446 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 示例 1: 示例 2: 提示: 题解 q;
int m = matrix.size(), n = matrix[0].size();
vector 难度中等379收藏分享切换为英文接收动态反馈 在给定的网格中,每个单元格可以有以下三个值之一: 每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。 返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 示例 1: [外链图片转存中…(img-xxrW1W6P-1630453061673)] 示例 2: 示例 3: 提示: 题解
A = [1, 2, 3, 4]
返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。
//dp
class Solution {
public:
int numberOfArithmeticSlices(vector
31. 下一个排列
输入:nums = [1,2,3]
输出:[1,3,2]
输入:nums = [3,2,1]
输出:[1,2,3]
输入:nums = [1,1,5]
输出:[1,5,1]
输入:nums = [1]
输出:[1]
1 <= nums.length <= 100
0 <= nums[i] <= 100
class Solution {
public:
void nextPermutation(vector
498. 对角线遍历
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,4,7,5,3,6,8,9]
解释:
class Solution {
public:
vector
443. 压缩字符串
chars
,请使用下述算法压缩:s
开始。对于 chars
中的每组 连续重复字符 :
1
,则将字符追加到 s
中。s
追加字符,后跟这一组的长度。s
不应该直接返回 ,需要转储到字符数组 chars
中。需要注意的是,如果组长度为 10
或 10
以上,则在 chars
数组中会被拆分为多个字符。输入:chars = ["a","a","b","b","c","c","c"]
输出:返回 6 ,输入数组的前 6 个字符应该是:["a","2","b","2","c","3"]
解释:
"aa" 被 "a2" 替代。"bb" 被 "b2" 替代。"ccc" 被 "c3" 替代。
输入:chars = ["a"]
输出:返回 1 ,输入数组的前 1 个字符应该是:["a"]
解释:
没有任何字符串被替代。
输入:chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
输出:返回 4 ,输入数组的前 4 个字符应该是:["a","b","1","2"]。
解释:
由于字符 "a" 不重复,所以不会被压缩。"bbbbbbbbbbbb" 被 “b12” 替代。
注意每个数字在数组中都有它自己的位置。
1 <= chars.length <= 2000
chars[i]
可以是小写英文字母、大写英文字母、数字或符号class Solution {
public:
int compress(vector
◉ 二、摩尔投票法
1. 多数元素
⌊ n/2 ⌋
的元素。输入:[3,2,3]
输出:3
输入:[2,2,1,1,1,2,2]
输出:2
class Solution {
public:
int majorityElement(vector
2. 求众数 II
⌊ n/3 ⌋
次的元素。输入:[3,2,3]
输出:[3]
输入:nums = [1]
输出:[1]
输入:[1,1,1,3,3,2,2,2]
输出:[1,2]
//摩尔投票法
class Solution {
public:
vector
◉ 三、哈希表
220. 存在重复元素 III
nums
和两个整数 k
和 t
。请你判断是否存在 两个不同下标 i
和 j
,使得 abs(nums[i] - nums[j]) <= t
,同时又满足 abs(i - j) <= k
。true
,不存在返回 false
。输入:nums = [1,2,3,1], k = 3, t = 0
输出:true
输入:nums = [1,0,1,1], k = 1, t = 2
输出:true
输入:nums = [1,5,9,1,5,9], k = 2, t = 3
输出:false
/*在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。*/
static int x=[](){
ios::sync_with_stdio(false);
cin.tie(NULL);
return 0;
}();
class Solution {
public:
long getID(long x, long t){
//如果x元素大于等于零,直接分桶
if(x>=0){
return x / ( t + 1 );
}else{
//如果x元素小于零,偏移后再分桶
return ( x + 1 )/( t + 1 ) - 1 ;
}
return 0;
}
bool containsNearbyAlmostDuplicate(vector
*128. 最长连续序列
nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。O(n)
的算法解决此问题。输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
0 <= nums.length <= 105
-109 <= nums[i] <= 109
class Solution {
public:
int longestConsecutive(vector
3. 无重复字符的最长子串
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
输入: s = ""
输出: 0
int lengthOfLongestSubstring(const string& s) {
if(s.size()==0)return 0;
if(s.size()==1) return 1;
bitset<128>map;
int ans=1;
map[s[0]]=1;
int i=0,j=0;
while(j
930. 和相同的二元子数组
nums
,和一个整数 goal
,请你统计并返回有多少个和为 goal
的 非空 子数组。输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
输入:nums = [0,0,0,0,0], goal = 0
输出:15
1 <= nums.length <= 3 * 104
nums[i]
不是 0
就是 1
0 <= goal <= nums.length
//前缀和
class Solution {
public:
int numSubarraysWithSum(vector
13. 罗马数字转整数
I
, V
, X
, L
,C
,D
和 M
。字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
II
,即为两个并列的 1。12 写做 XII
,即为 X
+ II
。 27 写做 XXVII
, 即为 XX
+ V
+ II
。IIII
,而是 IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在 V
(5) 和 X
(10) 的左边,来表示 4 和 9。X
可以放在 L
(50) 和 C
(100) 的左边,来表示 40 和 90。C
可以放在 D
(500) 和 M
(1000) 的左边,来表示 400 和 900。输入: "III"
输出: 3
输入: "IV"
输出: 4
输入: "IX"
输出: 9
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
1 <= s.length <= 15
s
仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
s
是一个有效的罗马数字,且表示整数在范围 [1, 3999]
内class Solution {
public:
int romanToInt(string s) {
int i = 0, n = 0;
int len = s.size();
int hash[26] = {0};
hash['I'-'A'] = 1;
hash['V'-'A'] = 5;
hash['X'-'A'] = 10;
hash['L'-'A'] = 50;
hash['C'-'A'] = 100;
hash['D'-'A'] = 500;
hash['M'-'A'] = 1000;
while (i < len)
{
if (i + 1 < len && hash[s[i]-'A'] < hash[s[i + 1]-'A'])
{
n = n + hash[s[i + 1]-'A'] - hash[s[i]-'A'];
i = i + 2;
}
else
{
n = n + hash[s[i]-'A'];
i++;
}
}
return n;
}
};
1248. 统计「优美子数组」
nums
和一个整数 k
。k
个奇数数字,我们就认为这个子数组是「优美子数组」。输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。
输入:nums = [2,4,6], k = 1
输出:0
解释:数列中不包含任何奇数,所以不存在优美子数组。
输入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出:16
1 <= nums.length <= 50000
1 <= nums[i] <= 10^5
1 <= k <= nums.length
static int x=[](){
ios::sync_with_stdio(0);
cin.tie(0);
return 0;
}();
class Solution {
public:
int numberOfSubarrays(vector
724. 寻找数组的中心下标
nums
,请计算数组的 中心下标 。0
,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。-1
。输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。
输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。
输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。
1 <= nums.length <= 104
-1000 <= nums[i] <= 1000
//前缀和
class Solution {
public:
int pivotIndex(vector
1109. 航班预订统计
n
个航班,它们分别从 1
到 n
进行编号。bookings
,表中第 i
条预订记录 bookings[i] = [firsti, lasti, seatsi]
意味着在从 firsti
到 lasti
(包含 firsti
和 lasti
)的 每个航班 上预订了 seatsi
个座位。n
的数组 answer
,其中 answer[i]
是航班 i
上预订的座位总数。输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号 1 2 3 4 5
预订记录 1 : 10 10
预订记录 2 : 20 20
预订记录 3 : 25 25 25 25
总座位数: 10 55 45 25 25
因此,answer = [10,55,45,25,25]
输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号 1 2
预订记录 1 : 10 10
预订记录 2 : 15
总座位数: 10 25
因此,answer = [10,25]
1 <= n <= 2 * 104
1 <= bookings.length <= 2 * 104
bookings[i].length == 3
1 <= firsti <= lasti <= n
1 <= seatsi <= 104
class Solution {
public:
vector
525. 连续数组
nums
, 找到含有相同数量的 0
和 1
的最长连续子数组,并返回该子数组的长度。输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
1 <= nums.length <= 105
nums[i]
不是 0
就是 1
class Solution {
public:
int findMaxLength(vector
325.和等于 k 的最长子数组长度
给定一个数组 nums 和一个目标值 k,找到和等于 k 的最长子数组长度。如果不存在任意一个符合要求的子数组,则返回 0。
nums 数组的总和是一定在 32 位有符号整数范围之内的。示例 1:
输入: nums = [1, -1, 5, -2, 3], k = 3
输出: 4
解释: 子数组 [1, -1, 5, -2] 和等于 3,且长度最长。
示例 2:
输入: nums = [-2, -1, 2, 1], k = 1
输出: 2
解释: 子数组 [-1, 2] 和等于 1,且长度最长。
//
class Solution {
public:
int maxSubArrayLen(vector
1. 两数之和
nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
输入:nums = [3,2,4], target = 6
输出:[1,2]
输入:nums = [3,3], target = 6
输出:[0,1]
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
class Solution {
public:
vector
12. 整数转罗马数字
I
, V
, X
, L
,C
,D
和 M
。字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
II
,即为两个并列的 1。12 写做 XII
,即为 X
+ II
。 27 写做 XXVII
, 即为 XX
+ V
+ II
。IIII
,而是 IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在 V
(5) 和 X
(10) 的左边,来表示 4 和 9。X
可以放在 L
(50) 和 C
(100) 的左边,来表示 40 和 90。C
可以放在 D
(500) 和 M
(1000) 的左边,来表示 400 和 900。输入: num = 3
输出: "III"
输入: num = 4
输出: "IV"
输入: num = 9
输出: "IX"
输入: num = 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
输入: num = 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
1 <= num <= 3999
class Solution {
public:
string intToRoman(int num) {
int values[]={1000,900,500,400,100,90,50,40,10,9,5,4,1};
string reps[]={"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
string res;
for(int i=0; i<13; i++){
while(num>=values[i]){
num -= values[i];
res += reps[i];
}
}
return res;
}
};
◉ 四、链表
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
剑指 Offer 24. 反转链表
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
0 <= 节点个数 <= 5000
//迭代法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head||!head->next)return head;
ListNode * newh=nullptr;
ListNode * p=head;
while(head){
p= head->next;
head->next = newh;
newh = head;
head =p;
}
return newh;
}
};
//递归法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == NULL || head->next == NULL) {
return head;
}
ListNode* ret = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return ret;
}
};
//头插法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head||!head->next)return head;
ListNode newh(0),*p=&newh;
ListNode * temp = nullptr;
while(head->next){
temp = head->next;
head->next = temp->next;
temp->next = p->next;
p->next = temp;
}
return newh.next;
}
};
143. 重排链表
L
的头节点 head
,单链表 L
表示为:L0 → L1 → … → Ln-1 → Ln
请将其重新排列后变为:L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
输入: head = [1,2,3,4]
输出: [1,4,2,3]
输入: head = [1,2,3,4,5]
输出: [1,5,2,4,3]
[1, 5 * 104]
1 <= node.val <= 1000
class Solution {
public:
void reorderList(ListNode *head) {
if (head == nullptr) {
return;
}
vector
25. K 个一组翻转链表
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
输入:head = [1], k = 1
输出:[1]
sz
内1 <= sz <= 5000
0 <= Node.val <= 1000
1 <= k <= sz
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if(!head||!head->next)return head;
ListNode *p =head;
int n=0;
while(p)n++,p=p->next;
p=head;
ListNode newh(0,head),*pre=&newh;
for(int i=0;i
剑指 Offer 52. 两个链表的第一个公共节点
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
null
.class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr||headB==nullptr)return nullptr;
ListNode* p1=headA;
ListNode* p2 =headB;
while(p1!=p2){
p1=(p1==nullptr)?headB:p1->next;
p2=(p2==nullptr)?headA:p2->next;
}
return p1;
}
};
环形链表
struct ListNode{
int val;
ListNode * next;
ListNode(int oval):val(oval),next(nullptr){}
};
class solution{
public:
bool hasLoop(ListNode* head){
if(!head||!head->next)return false;
ListNode * fast = head;
ListNode * slow = head;
while(fast &&fast->next){
fast = fast ->next->next;
slow=slow->next;
if(fast==slow)return true;
}
return false;
}
}
//找到这个环形链表的入口
ListNode *detectCycle(ListNode *head) {
if(!head||!head->next)return nullptr;
ListNode * fast=head;
ListNode * slow=head;
while(fast&&fast->next){
fast = fast->next->next;
slow = slow->next;
}
if(!fast||!fast->next)return nullptr;
fast =head;
while(slow!=fast){
slow=slow->next;
fast=fast->next;
}
return slow;
}
链表相交
ListNode *getIntersectionNode(ListNode *l1, ListNode *l2) {
if(!l1 || !l2) return nullptr;
ListNode *p1=l1;
ListNode* p2=l2;
while(p1!=p2){
p1=p1?p1->next:l2;
p2=p2?p2->next:l1;
}
return p1;
}
删除链表的倒数第N个节点
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
ListNode* removeNthFromEnd(ListNode* head, int k) {
if(head==nullptr||head->next==nullptr)return nullptr;
ListNode *slow = head;
ListNode *fast = head;
while(fast->next!=nullptr){
if(k>-1)k--;
if(k==-1)slow=slow->next;//在找到倒数k+1个节点后,快慢指针同步更新
fast=fast->next;//快指针
}
ListNode * temp=nullptr;
if(k>0) {//快指针已经到末尾,删除头节点
temp=head;
head=head->next;
delete temp;
}
else{//快指针没有到末尾
temp=slow->next;
slow->next = slow->next->next;
delete temp;
}
return head;
}
83. 删除排序链表中的重复元素
head
,请你删除所有重复的元素,使每个元素 只出现一次 。输入:head = [1,1,2]
输出:[1,2]
输入:head = [1,1,2,3,3]
输出:[1,2,3]
[0, 300]
内-100 <= Node.val <= 100
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head||!head->next)return head;
ListNode* p = head;
while(p&&p->next){
if(p->val==p->next->val){
ListNode* temp=p->next;
p->next = p->next->next;
delete temp;
}
else p = p->next;
}
return head;
}
};
82. 删除排序链表中的重复元素 II
head
,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
输入:head = [1,1,1,2,3]
输出:[2,3]
[0, 300]
内-100 <= Node.val <= 100
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (!head) return head;
ListNode* dummy = new ListNode(0, head);
ListNode* cur = dummy;
while (cur->next && cur->next->next) {
if (cur->next->val == cur->next->next->val) {
int x = cur->next->val;
while (cur->next && cur->next->val == x) {
ListNode *temp = cur->next; //用于删除
cur->next = cur->next->next;
delete temp;
}
}
else {
cur = cur->next;
}
}
return dummy->next;
}
};
86. 分隔链表
head
和一个特定值 x
,请你对链表进行分隔,使得所有 小于 x
的节点都出现在 大于或等于 x
的节点之前。输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
输入:head = [2,1], x = 2
输出:[1,2]
ListNode* partition(ListNode* head, int x) {//四指针法,分割为两条链
ListNode* small = new ListNode(0);
ListNode* smallHead = small;
ListNode* large = new ListNode(0);
ListNode* largeHead = large;
while (head != nullptr) {
if (head->val < x) {
small->next = head;
small = small->next;
} else {
large->next = head;
large = large->next;
}
head = head->next;
}
large->next = nullptr;
small->next = largeHead->next;
return smallHead->next;
}
面试题 02.06. 回文链表
输入: 1->2
输出: false
输入: 1->2->2->1
输出: true
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?class Solution {
public:
bool isPalindrome(ListNode* head) {
if (head == nullptr) {
return true;
}
// 找到前半部分链表的尾节点并反转后半部分链表
ListNode* firstHalfEnd = endOfFirstHalf(head);
ListNode* secondHalfStart = reverseList(firstHalfEnd->next);
// 判断是否回文
ListNode* p1 = head;
ListNode* p2 = secondHalfStart;
bool result = true;
while (result && p2 != nullptr) {
if (p1->val != p2->val) {
result = false;
}
p1 = p1->next;
p2 = p2->next;
}
// 还原链表并返回结果
firstHalfEnd->next = reverseList(secondHalfStart);
return result;
}
ListNode* reverseList(ListNode* head) {//反转链表
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr != nullptr) {
ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
ListNode* endOfFirstHalf(ListNode* head) {//找前半部分链尾
ListNode* fast = head;
ListNode* slow = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
};
92. 反转链表 II
head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
输入:head = [5], left = 1, right = 1
输出:[5]
n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
class Solution {
public:
ListNode* reverseBetween(ListNode* head,int m, int n) {
if(head==nullptr||head->next==nullptr)return head;
ListNode* newhead= new ListNode(0);
newhead->next = head;
ListNode* pre=newhead; //work pointer
for(int i=1;i
23. 合并K个升序链表
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
输入:lists = []
输出:[]
输入:lists = [[]]
输出:[]
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i]
按 升序 排列lists[i].length
的总和不超过 10^4
class Solution {
public:
// 小根堆的回调函数
struct cmp{
bool operator()(ListNode *a,ListNode *b){
return a->val > b->val;
};
ListNode* mergeKLists(vector
24. 两两交换链表中的节点
输入:head = [1,2,3,4]
输出:[2,1,4,3]
输入:head = []
输出:[]
输入:head = [1]
输出:[1]
[0, 100]
内0 <= Node.val <= 100
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(!head ||!head->next) return head;
ListNode newh,*p=&newh;
p->next = head;
ListNode*temp = p;
ListNode*node1 = head;
ListNode*node2 = head->next;
while(temp->next && temp->next->next ){
node1 = temp->next;
node2 = temp->next->next;
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
return newh.next;
}
};
◉ 五、栈和队列
剑指 Offer 09. 用两个栈实现队列
appendTail
和 deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead
操作返回 -1 )输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用
static int xx = []()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
return 0;
}();
class CQueue {
public:
stack
496. 单调栈:下一个更大元素 I
nums1
和 nums2
,其中nums1
是 nums2
的子集。nums1
中每个元素在 nums2
中的下一个比其大的值。nums1
中数字 x
的下一个更大元素是指 x
在 nums2
中对应位置的右边的第一个比 x
大的元素。如果不存在,对应位置输出 -1
。输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
1 <= nums1.length <= nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 104
nums1
和nums2
中所有整数 互不相同nums1
中的所有整数同样出现在 nums2
中vector
for(int i = 0; i < T.size(); i++){
while(! stk.empty() && stk.top() > T[i]){
//hash[stk.top()]=T[i];//使用哈希表记录右边第一个比它小的数
stk.pop();
}
stk.push(T[i]);
}
/* T[]={1,2,3,45,3,4,7};
stk[]最终内容:(从栈底至栈顶)1,2,3,3,4,7
for(int i = T.size() - 1; i >= 0; i--){
while(! stk.empty() && T[i] >= stk.top()){
stk.pop();
}
stk.push(i);
}
可以以 O(1) 的时间复杂度得知某个位置左右两侧比他大(或小)的数的位置,当你需要高效率获取某个位置左右两侧比他大(或小)的数的位置的的时候就可以用到单调栈。
求解数组中元素右边第一个比它大的元素的下标,从前往后,构造单调递减栈;
求解数组中元素左边第一个比它小的元素的下标,从后往前,构造单调递减栈;
求解数组中元素左边第一个比它小的元素的下标,从后往前,构造单调递增栈。503. 下一个更大元素 II
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
vector
剑指 Offer 59 - I. 滑动窗口的最大值
nums
和滑动窗口的大小 k
,请找出所有滑动窗口里的最大值。输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
vector
581. 最短无序连续子数组
nums
,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
输入:nums = [1,2,3,4]
输出:0
输入:nums = [1]
输出:0
class Solution {
public:
int findUnsortedSubarray(vector
循环队列
class MyCircularQueue {
private:
vector
150. 逆波兰表达式求值
+
、-
、*
、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:
该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
1 <= tokens.length <= 104
tokens[i]
要么是一个算符("+"
、"-"
、"*"
或 "/"
),要么是一个在范围 [-200, 200]
内的整数 int evalRPN(vector
20. 有效的括号
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
输入:s = "()"
输出:true
输入:s = "()[]{}"
输出:true
输入:s = "(]"
输出:false
输入:s = "([)]"
输出:false
输入:s = "{[]}"
输出:true
1 <= s.length <= 104
s
仅由括号 '()[]{}'
组成 bool isValid(const string &s) {
int n = s.size();
if(0==n)return true;
if(n&1)return false;//为奇数个,不能匹配
int top = -1,i=0;
vector
155. 最小栈
push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
push(x)
—— 将元素 x 推入栈中。pop()
—— 删除栈顶的元素。top()
—— 获取栈顶元素。getMin()
—— 检索栈中的最小元素。输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
pop
、top
和 getMin
操作总是在 非空栈 上调用。class MinStack {
private:
stack
232. 用栈实现队列
push
、pop
、peek
、empty
):MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回 true
;否则,返回 false
push to top
, peek/pop from top
, size
, 和 is empty
操作是合法的。
O(1)
的队列?换句话说,执行 n
个操作的总时间复杂度为 O(n)
,即使其中一个操作可能花费较长时间。输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
1 <= x <= 9
100
次 push
、pop
、peek
和 empty
pop
或者 peek
操作)class MyQueue {
public:
stack
32. 最长有效括号
'('
和 ')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
输入:s = ""
输出:0
0 <= s.length <= 3 * 104
s[i]
为 '('
或 ')'
class Solution {
public:
int longestValidParentheses(string s) {
if(s.size()<1)return 0;
vector
class Solution {
public:
int longestValidParentheses(string s) {
if(s.size()<2)return 0;
int left=0,right=0,ans=0;
for(int i=0;i
227. 基本计算器 II
s
,请你实现一个基本计算器来计算并返回它的值。输入:s = "3+2*2"
输出:7
输入:s = " 3/2 "
输出:1
输入:s = " 3+5 / 2 "
输出:5
1 <= s.length <= 3 * 105
s
由整数和算符 ('+', '-', '*', '/')
组成,中间由一些空格隔开s
表示一个 有效表达式[0, 231 - 1]
内class Solution {
public:
int calculate(string s) {
int ans=0,d=0;
int n=s.size();
char sign='+';
vector
224. 基本计算器
s
,请你实现一个基本计算器来计算并返回它的值。输入:s = "1 + 1"
输出:2
输入:s = " 2-1 + 2 "
输出:3
输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23
1 <= s.length <= 3 * 105
s
由数字、'+'
、'-'
、'('
、')'
、和 ' '
组成s
表示一个有效的表达式class Solution {
public:
int calculate(string s) {
int sign=1;
vector
394. 字符串解码
k[encoded_string]
,表示其中方括号内部的 encoded_string正好重复 k 次。注意 k 保证为正整数。3a
或 2[4]
的输入。输入:s = "3[a]2[bc]"
输出:"aaabcbc"
输入:s = "3[a2[c]]"
输出:"accaccacc"
输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"
输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
class Solution {
public:
string decodeString(string s) {
stack
◉ 六、动态规划DP
最长递增子序列
nums
,找到其中最长严格递增子序列的长度。[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
输入:nums = [0,1,0,3,2,3]
输出:4
输入:nums = [7,7,7,7,7,7,7]
输出:1
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
O(n2)
的解决方案吗?O(n log(n))
吗? int lengthOfLIS(vector
剑指 Offer 42. 连续子数组的最大和
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
//第一种方法
class Solution {
public:
int maxSubArray(vector
买卖股票的最佳时机 II
输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出,
这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,
这笔交易所能获得利润 = 6-3 = 3 。
输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出,
这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,
你必须在再次购买前出售掉之前的股票。
int maxProfit(vector
回文子串
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
//中心扩展法
class Solution {
public:
int num=0;
void countNum(const string&s,int i,int j){
while(i>=0&&j
221. 最大正方形
'0'
和 '1'
组成的二维矩阵内,找到只包含 '1'
的最大正方形,并返回其面积。输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4
输入:matrix = [["0","1"],["1","0"]]
输出:1
输入:matrix = [["0"]]
输出:0
m == matrix.length
n == matrix[i].length
1 <= m, n <= 300
matrix[i][j]
为 '0'
或 '1'
class Solution {
public:
int maximalSquare(vector
516. 最长回文子序列
s
,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s
的最大长度为 1000
。
输入:"bbbab"
4
输入:"cbbd"
2
int longestPalindromeSubseq(const string& s) {
const int n=s.size();
vector
1143. 最长公共子序列
text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列,返回 0
。
"ace"
是 "abcde"
的子序列,但 "aec"
不是 "abcde"
的子序列。输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。
1 <= text1.length, text2.length <= 1000
text1
和 text2
仅由小写英文字符组成。//dp
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m=text1.size(),n=text2.size();
vector
983. 最低票价
days
的数组给出。每一项是一个从 1
到 365
的整数。
costs[0]
美元;costs[1]
美元;costs[2]
美元。days
中列出的每一天的旅行所需要的最低消费。输入:days = [1,4,6,7,8,20], costs = [2,7,15]
输出:11
解释:
例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划:
在第 1 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 1 天生效。
在第 3 天,你花了 costs[1] = $7 买了一张为期 7 天的通行证,它将在第 3, 4, ..., 9 天生效。
在第 20 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 20 天生效。
你总共花了 $11,并完成了你计划的每一天旅行。
输入:days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15]
输出:17
解释:
例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划:
在第 1 天,你花了 costs[2] = $15 买了一张为期 30 天的通行证,它将在第 1, 2, ..., 30 天生效。
在第 31 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 31 天生效。
你总共花了 $17,并完成了你计划的每一天旅行。
1 <= days.length <= 365
1 <= days[i] <= 365
days
按顺序严格递增costs.length == 3
1 <= costs[i] <= 1000
class Solution {
public:
int mincostTickets(vector
62. 不同路径
m x n
网格的左上角 (起始点在下图中标记为 “Start” )。输入:m = 3, n = 7
输出:28
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
输入:m = 7, n = 3
输出:28
输入:m = 3, n = 3
输出:6
1 <= m, n <= 100
2 * 109
class Solution {
public:
int uniquePaths(const int& m,const int& n) {
vector
圆环回原点问题
输入: 2
输出: 2
解释:有2种方案。分别是0->1->0和0->9->0
int backToOrigin(int start,int end ,int n)//start=0, end=9, n:steps
{ int len=end-start+1;
vector
◉ 七、回溯算法
res = []
path = []
def backtrack(未探索区域, res, path):
if path 满足条件:
res.add(path) # 深度拷贝
# return # 如果不用继续搜索需要 return
for 选择 in 未探索区域当前可能的选择:
if 当前选择符合要求:
path.add(当前选择)
backtrack(新的未探索区域, res, path)
path.pop()
1.全排列问题
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
//经典回溯
vector
剑指 Offer 38. 字符串的排列
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
vector
2.组合问题
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
//题解:
class Solution {
public:
vector
3.子集问题
nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
输入:nums = [0]
输出:[[],[0]]
//方法一
vector
4. 组合总和
示例 1:
所求解集为:[
[7],
[2,2,3]
]
所求解集为:[
[2,2,2,2],
[2,3,3],
[3,5]
]
//采用完全背包问题解决
vector
5. 组合总和 II
candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。candidates
中的每个数字在每个组合中只能使用一次。
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
//回溯法,注意需要排序
vector
6. 分割回文串
s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]
输入:s = "a"
输出:[["a"]]
class Solution {
public:
vector
93. 复原 IP 地址
s
获得的 有效 IP 地址 。你可以按任何顺序返回答案。0
),整数之间用 '.'
分隔。输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]
输入:s = "0000"
输出:["0.0.0.0"]
输入:s = "1111"
输出:["1.1.1.1"]
输入:s = "010010"
输出:["0.10.0.10","0.100.1.0"]
输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
0 <= s.length <= 3000
s
仅由数字组成class Solution {
private:
vector
784. 字母大小写全排列
S
,通过将字符串S
中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。示例:
输入:S = "a1b2"
输出:["a1b2", "a1B2", "A1b2", "A1B2"]
输入:S = "3z4"
输出:["3z4", "3Z4"]
输入:S = "12345"
输出:["12345"]
S
的长度不超过12
。S
仅由数字和字母组成。class Solution {
public:
vector
7. 电话号码的字母组合
2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
输入:digits = ""
输出:[]
输入:digits = "2"
输出:["a","b","c"]
0 <= digits.length <= 4
digits[i]
是范围 ['2', '9']
的一个数字。class Solution {
public:
vector
◉ 八、排序算法
1. 前K个高频单词
输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 "i" 在 "love" 之前。
输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,
出现次数依次为 4, 3, 2 和 1 次。
//自定义排序方法,对原来数组进行排序,再去重
vector
unordered_map
int partition(vector
2. 前 K 个高频元素
nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
输入: nums = [1], k = 1
输出: [1]
//基于随机选择排序,随即切分
int partition(vector
75. 颜色分类
n
个元素的数组,**原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。0
、 1
和 2
分别表示红色、白色和蓝色。输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
输入:nums = [2,0,1]
输出:[0,1,2]
输入:nums = [0]
输出:[0]
输入:nums = [1]
输出:[1]
n == nums.length
1 <= n <= 300
nums[i]
为 0
、1
或 2
class Solution {
public:
void sortColors(vector
977. 有序数组的平方
nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序class Solution {
public:
vector
排序算法总结
//快排
/*数组的划分1:伪码---该划分方法保证了相对顺序的不变
partition(nums,i,j)
x=nums[j] //中枢值为末尾值
p=i-1
for k=i to j-1 (range:[i,j-1])
if nums[k]<=x //不满足大于中枢值,则进行交换
p=p+1
swap nums[p] with nums[k]
swap nums[p+1] with nums[j];
return i+1
*/
int partition(vector
◉ 九、背包问题
01背包问题模板
//01背包的最大价值问题
vector
完全背包问题模板
//完全背包最大价值问题
for (int i = 0; i < n; i++) {
for (int j = V[i]; j <= w; j++) {//顺序遍历
f[j] = max(f[j], f[j-V[i]] + W[i]);
}
}
//选择的方法数
vector
问题分类的模板:
2、存在问题(bool):dp[i]=dp[i]||dp[i-num];
3、组合问题:dp[i]+=dp[i-num];322. 零钱兑换
coins
和一个总金额 amount
。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
。输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
输入:coins = [2], amount = 3
输出:-1
输入:coins = [1], amount = 0
输出:0
输入:coins = [1], amount = 1
输出:1
输入:coins = [1], amount = 2
输出:2
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
int coinChange(vector
279. 完全平方数
1, 4, 9, 16, ...
)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。n
,返回和为 n
的完全平方数的 最少数量 。1
、4
、9
和 16
都是完全平方数,而 3
和 11
不是。输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
输入:n = 13
输出:2
解释:13 = 4 + 9
//package question
int numSquares(int n) {
vector
1155. 掷骰子的N种方法
d
个一样的骰子,每个骰子上都有 f
个面,分别标号为 1, 2, ..., f
。target
,请你计算出有多少种不同的组合情况(所有的组合情况总共有 f^d
种),模 10^9 + 7
后返回。输入:d = 1, f = 6, target = 3
输出:1
输入:d = 2, f = 6, target = 7
输出:6
输入:d = 2, f = 5, target = 10
输出:1
输入:d = 1, f = 2, target = 3
输出:0
输入:d = 30, f = 30, target = 500
输出:222616187
1 <= d, f <= 30
1 <= target <= 1000
int numRollsToTarget(int d, int f, int target)
{
vector
◉ 十、贪心
1833. 雪糕的最大数量
n
支雪糕,用长度为 n
的数组 costs
表示雪糕的定价,其中 costs[i]
表示第 i
支雪糕的现金价格。Tony 一共有 coins
现金可以用于消费,他想要买尽可能多的雪糕。costs
和现金量 coins
,请你计算并返回 Tony 用 coins
现金能够买到的雪糕的 最大数量 。输入:costs = [1,3,2,4,1], coins = 7
输出:4
解释:Tony 可以买下标为 0、1、2、4 的雪糕,总价为 1 + 3 + 2 + 1 = 7
输入:costs = [10,6,8,7,7,8], coins = 5
输出:0
解释:Tony 没有足够的钱买任何一支雪糕。
输入:costs = [1,6,3,1,2,5], coins = 20
输出:6
解释:Tony 可以买下所有的雪糕,总价为 1 + 6 + 3 + 1 + 2 + 5 = 18 。
costs.length == n
1 <= n <= 105
1 <= costs[i] <= 105
1 <= coins <= 108
class Solution {
public:
int maxIceCream(vector
11. 盛最多水的容器
n
个非负整数 a1,a2,...,a``n
,每个数代表坐标中的一个点 (i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为 (i, ai)
和 (i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
输入:height = [1,1]
输出:1
输入:height = [4,3,2,1,4]
输出:16
输入:height = [1,2,1]
输出:2
n = height.length
2 <= n <= 3 * 104
0 <= height[i] <= 3 * 104
class Solution {
public:
int maxArea(vector
42. 接雨水
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
输入:height = [4,2,0,3,2,5]
输出:9
n == height.length
0 <= n <= 3 * 104
0 <= height[i] <= 105
class Solution {
public:
int trap(vector
739. 每日温度
气温
列表 temperatures
,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0
来代替。输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
输入: temperatures = [30,60,90]
输出: [1,1,0]
1 <= temperatures.length <= 105
30 <= temperatures[i] <= 100
class Solution {
public:
vector
134. 加油站
gas[i]
升。cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
输入:
gas = [2,3,4]
cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
class Solution {
public:
int canCompleteCircuit(vector
881. 救生艇
i
个人的体重为 people[i]
,每艘船可以承载的最大重量为 limit
。limit
。输入:people = [1,2], limit = 3
输出:1
解释:1 艘船载 (1, 2)
输入:people = [3,2,2,1], limit = 3
输出:3
解释:3 艘船分别载 (1, 2), (2) 和 (3)
输入:people = [3,5,3,4], limit = 5
输出:4
解释:4 艘船分别载 (3), (3), (4), (5)
1 <= people.length <= 50000
1 <= people[i] <= limit <= 30000
class Solution {
public:
int numRescueBoats(vector
小船过河问题
dp[1] = a[1];
dp[2] = a[2];
dp[3] = a[2]+a[1]+a[3];
dp[4] = min(dp[3] + a[4] + a[1], dp[2]+a[2]+a[1]+a[4]+a[2]);class Solution{
int minTime(vector
55. 跳跃游戏
nums
,你最初位于数组的 第一个下标 。输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
class Solution {
public:
bool canJump(vector
45. 跳跃游戏 II
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
输入: [2,3,0,1,4]
输出: 2
1 <= nums.length <= 1000
0 <= nums[i] <= 105
class Solution {
public:
int jump(vector
455. 分发饼干
i
,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j
,都有一个尺寸 s[j]
。如果 s[j] >= g[i]
,我们可以将这个饼干 j
分配给孩子 i
,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
1 <= g.length <= 3 * 104
0 <= s.length <= 3 * 104
1 <= g[i], s[j] <= 231 - 1
class Solution {
public:
int findContentChildren(vector
135. 分发糖果
输入:[1,0,2]
输出:5
解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。
输入:[1,2,2]
输出:4
解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
class Solution {
public:
int candy(vector
738. 单调递增的数字
N
,找出小于或等于 N
的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。x
和 y
满足 x <= y
时,我们称这个整数是单调递增的。)输入: N = 10
输出: 9
输入: N = 1234
输出: 1234
输入: N = 332
输出: 299
N
是在 [0, 10^9]
范围内的一个整数。class Solution {
public:
int monotoneIncreasingDigits(int n) {
string temp=to_string(n);
int flag;
for(int i=temp.size()-1;i>0;--i){
if(temp[i-1]>temp[i]){
flag=i;
temp[i-1]--;
}
}
for(int i=flag;i
435. 无重叠区间
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
输入: [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
输入: [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
class Solution {
public:
int eraseOverlapIntervals(vector
56. 合并区间
intervals
表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi]
。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
class Solution{
vector
};
452. 用最少数量的箭引爆气球
x``start
,x``end
, 且满足 xstart ≤ x ≤ x``end
,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。points
,其中 points [i] = [xstart,xend]
,返回引爆所有气球所必须射出的最小弓箭数。输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球
输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
输入:points = [[1,2]]
输出:1
输入:points = [[2,3],[2,3]]
输出:1
1 <= points.length <= 104
points[i].length == 2
-231 <= xstart < xend <= 231 - 1
class Solution {
public:
int findMinArrowShots(vector
763. 划分字母区间
S
由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
S
的长度在[1, 500]
之间。S
只包含小写字母 'a'
到 'z'
。class Solution {
public:
vector
605. 种花问题
flowerbed
表示花坛,由若干 0
和 1
组成,其中 0
表示没种植花,1
表示种植了花。另有一个数 n
,能否在不打破种植规则的情况下种入 n
朵花?能则返回 true
,不能则返回 false
。输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
输入:flowerbed = [1,0,0,0,1], n = 2
输出:false
1 <= flowerbed.length <= 2 * 104
flowerbed[i]
为 0
或 1
flowerbed
中不存在相邻的两朵花0 <= n <= flowerbed.length
class Solution {
public:
bool canPlaceFlowers(vector
LCP 33. 蓄水
i
个水缸配备的水桶容量记作 bucket[i]
。小扣有以下两种操作:
bucket[i]+1
vat[i]
,返回小扣至少需要多少次操作可以完成所有水缸蓄水要求。
bucket = [1,3], vat = [6,8]
4
第 1 次操作升级 bucket[0];
第 2 ~ 4 次操作均选择蓄水,即可完成蓄水要求。
bucket = [9,0,1], vat = [0,2,2]
3
第 1 次操作均选择升级 bucket[1]
第 2~3 次操作选择蓄水,即可完成蓄水要求。
1 <= bucket.length == vat.length <= 100
0 <= bucket[i], vat[i] <= 10^4
class Solution {
public:
int storeWater(vector
918. 环形子数组的最大和
A
表示的环形数组 C
,求 **C**
的非空子数组的最大可能和。0 <= i < A.length
时 C[i] = A[i]
,且当 i >= 0
时 C[i+A.length] = C[i]
)A
中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], ..., C[j]
,不存在 i <= k1, k2 <= j
其中 k1 % A.length = k2 % A.length
)输入:[1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3
输入:[5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
输入:[3,-1,2,-1]
输出:4
解释:从子数组 [2,-1,3] 得到最大和 2 + (-1) + 3 = 4
输入:[3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
输入:[-2,-3,-1]
输出:-1
解释:从子数组 [-1] 得到最大和 -1
-30000 <= A[i] <= 30000
1 <= A.length <= 30000
class Solution {
public:
int maxSubarraySumCircular(vector
402. 移掉 K 位数字
num
和一个整数 k
,移除这个数中的 k
位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。输入:num = "1432219", k = 3
输出:"1219"
解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。
输入:num = "10200", k = 1
输出:"200"
解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
输入:num = "10", k = 2
输出:"0"
解释:从原数字移除所有的数字,剩余为空就是 0 。
1 <= k <= num.length <= 105
num
仅由若干位数字(0 - 9)组成num
不含任何前导零class Solution {
public:
string removeKdigits(string num, int k) {
while (k--) {
int n = num.size(), i = 0;
while (i + 1 < n && num[i] <= num[i + 1]) ++i;//每次剔除向右大的
num.erase(i, 1); // 每次 erase 的时间复杂度为O(n)
}
auto i = num.find_first_not_of("0");
return i == string::npos ? "0" : num.substr(i);
}
};
◉ 十一、二叉树
class TreeNode {
public:
int val;
TreeNode *left;
TreeNode *right;
TreeNode(const int& x):val(x),left(nullptr),right(nullptr){}
};
//销毁二叉树
void deleteTree(TreeNode* root) {
if (root == nullptr)return;
else if (root) {
deleteTree(root->left);
deleteTree(root->right);
delete root;
root = nullptr;
}
}
▼二叉树的遍历
//前序
vector
剑指 Offer 68 - II. 二叉树的最近公共祖先
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr) return root;
if(root==p||root==q) return root; //如果根节点就是p或者q,返回根节点
//分别去左右子树里面找
TreeNode* l=lowestCommonAncestor(root->left,p,q);
TreeNode* r=lowestCommonAncestor(root->right,p,q);
if(l&&r) return root;//p,q各在一边,说明当前的根就是最近共同祖先
else if(l)return l;//说明p,q都在左子树
else if(r)return r;//说明p,q都在右子树
return nullptr;
}
//或
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root || root == p || root == q) return root; //注意看条件
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(!left) return right;
if(!right) return left;
return root;
}
剑指 Offer 36. 二叉搜索树与双向链表
class Solution {
public:
void biuld(Node* root,Node* &head,Node* &tail){
if(!root) return ;
biuld(root->left,head,tail);
if(!head) head = tail = root;
else{
tail->right = root;
root->left = tail;
tail = root;
}
biuld(root->right,head,tail);
}
Node* treeToDoublyList(Node* root) {
if (!root) return nullptr;
Node* head = nullptr,*tail = nullptr;
biuld(root,head,tail);
tail->right = head;
head->left = tail;
return head;
}
};
剑指 Offer 07. 重建二叉树
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
3
/ \
9 20
/ \
15 7
0 <= 节点个数 <= 5000
class Solution {
public:
unordered_map
判断是否是完全二叉树
bool isComplete(TreeNode* root){
queue
剑指 Offer 28. 对称的二叉树
1 / \ 2 2 / \ / \ 3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
/
2 2
\
3 3输入:root = [1,2,2,3,4,4,3]
输出:true
输入:root = [1,2,2,null,3,null,3]
输出:false
0 <= 节点个数 <= 1000
class Solution {
public:
bool helper(TreeNode* l,TreeNode *r){
if(l==nullptr&& r==nullptr)return true;
if(l==nullptr||r==nullptr||l->val!=r->val)return false;
return helper(l->right,r->left)&&helper(l->left,r->right);
}
bool isSymmetric(TreeNode* root) {
if(root==nullptr||(root->left==nullptr&&root->right==nullptr))return true;
return helper(root->left,root->right);
}
};
//迭代
class Solution {
public:
bool bthelp(TreeNode*l,TreeNode*r){
queue
剑指 Offer 27. 二叉树的镜像
/
2 7
/ \ /
1 3 6 9
镜像输出:
/
7 2
/ \ /
9 6 3 1输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
0 <= 节点个数 <= 1000
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(!root)return root;
TreeNode* t=root->left;
root->left=root->right;
root->right=t;
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
};
//迭代
class Solution {
public:
stack
剑指 Offer 26. 树的子结构
给定的树 A: 3 / \ 4 5 / \ 1 2
给定的树 B: 4 / 1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。输入:A = [1,2,3], B = [3,1]
输出:false
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
0 <= 节点个数 <= 10000
class Solution {
public:
bool helper(TreeNode* A, TreeNode* B){
if(A==NULL||B==NULL)
return B==NULL?true:false;
return (A->val==B->val)&&helper(A->left,B->left)&&helper(A->right,B->right);
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A==NULL||B==NULL)
return false;
return helper(A,B)||isSubStructure(A->left,B)||isSubStructure(A->right,B);
}
};
剑指 Offer 55 - I. 二叉树的深度
[3,9,20,null,null,15,7]
, 3
/ \
9 20
/ \
15 7
//迭代
class Solution {
private:
queue
面试题 04.04. 检查平衡性
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
//后序遍历
class Solution {
public:
bool f=true;
int depth(TreeNode*root){
if(!root)return 0;
int ldepth=depth(root->left);
int rdepth=depth(root->right);
if(abs(ldepth-rdepth)>1) f=false;
return max(ldepth,rdepth)+1;
}
bool isBalanced(TreeNode* root) {
if(!root) return true;
depth(root);
return f;
}
};
543. 二叉树的直径
给定二叉树 1
/ \
2 3
/ \
4 5
class Solution {
public:
int ans=0;
int depth(TreeNode* root){
if(!root)return 0;
int l=depth(root->left),r=depth(root->right);
ans = max(ans,l+r);
return max(l,r)+1;
}
int diameterOfBinaryTree(TreeNode* root) {
depth(root);
return ans;
}
};
124. 二叉树中的最大路径和
root
,返回其 最大路径和 。输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
[1, 3 * 104]
-1000 <= Node.val <= 1000
class Solution {
public:
int ans=INT_MIN;
int fun(TreeNode* root) {
if(root==nullptr){
return 0;
}
// 如果子树路径和为负则应当置0表示最大路径不包含子树
int left=max(0,fun(root->left));
int right=max(0,fun(root->right));
// 判断在该节点包含左右子树的路径和是否大于当前最大路径和
ans=max(ans,left+right+root->val);
// 选择左子树/右子树+当前节点
return max(left,right)+root->val;
}
int maxPathSum(TreeNode* root) {
fun(root);
return ans;
}
};
687. 最长同值路径
5
/ \
4 5
/ \ \
1 1 5
2
1
/ \
4 5
/ \ \
4 4 5
2
class Solution {
public:
int ans=0;
int dfs(TreeNode*root){
if(!root)return 0;
const int l=dfs(root->left),r=dfs(root->right);
const int lp=(root->left&&root->left->val==root->val)?l:0;
const int rp=(root->right&&root->right->val==root->val)?r:0;
ans=max(ans,lp+rp);
return max(lp,rp)+1;
}
int longestUnivaluePath(TreeNode* root) {
dfs(root);
return ans;
}
};
111. 二叉树的最小深度
输入:root = [3,9,20,null,null,15,7]
输出:2
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
[0, 105]
内-1000 <= Node.val <= 1000
//迭代
class Solution {
private:
queue
993. 二叉树的堂兄弟节点
0
处,每个深度为 k
的节点的子节点位于深度 k+1
处。root
,以及树中两个不同节点的值 x
和 y
。x
和 y
对应的节点是堂兄弟节点时,才返回 true
。否则,返回 false
。输入:root = [1,2,3,4], x = 4, y = 3
输出:false
输入:root = [1,2,3,null,4,null,5], x = 5, y = 4
输出:true
输入:root = [1,2,3,null,4], x = 2, y = 3
输出:false
2
到 100
之间。1
到 100
的整数。//DFS
class Solution {
public:
TreeNode* p1=NULL; //father1: x
TreeNode* p2=NULL; //father2: y
int h1=-1,h2=-1;
void dfs(TreeNode* root,int x,int y,int h){
if(!root) return;
if(root->left&&root->left->val==x){ //father1: x
p1=root;h1=h;
}
if(root->right&&root->right->val==x){ //father1: x
p1=root;h1=h;
}
if(root->left&&root->left->val==y){ //father2: y
p2=root;h2=h;
}
if(root->right&&root->right->val==y){ //father2: y
p2=root;h2=h;
}
dfs(root->left,x,y,h+1);
dfs(root->right,x,y,h+1);
}
bool isCousins(TreeNode* root, int x, int y) {
dfs(root,x,y,0);
return (h1==h2)&&(p1!=p2); //同层不同父
}
};
//BFS
class Solution {
private:
queue
二叉树节点的所有祖先节点
bool PrintAncestors1(TreeNode *T, const int& x, vector
662. 二叉树最大宽度
null
节点也计入长度)之间的长度。输入:
1
/ \
3 2
/ \ \
5 3 9
输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。
输入:
1
/
3
/ \
5 3
输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。
输入:
1
/ \
3 2
/
5
输出: 2
解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。
输入:
1
/ \
3 2
/ \
5 9
/ \
6 7
输出: 8
解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。
1
/ \
0 1
/ \ / \
0 1 2 3
class Solution {
public:
int widthOfBinaryTree(TreeNode* root) {
if(root == NULL) return 0;
queue
257. 二叉树的所有路径
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
class Solution{
public:
vector
112. 路径总和
root
和一个表示目标和的整数 targetSum
,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
输入:root = [1,2,3], targetSum = 5
输出:false
输入:root = [1,2], targetSum = 0
输出:false
[0, 5000]
内-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
class Solution {
public:
int sum=0;
bool ans;
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root)return false;
sum+=root->val;//选择
if(sum==targetSum&&!root->left&&!root->right)return true;
if(root->left) ans = hasPathSum(root->left,targetSum);
if(root->right)ans= hasPathSum(root->right,targetSum);
sum-=root->val;//取消
return ans;
}
};
113. 路径总和 II
root
和一个整数目标和 targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
输入:root = [1,2,3], targetSum = 5
输出:[]
输入:root = [1,2], targetSum = 0
输出:[]
[0, 5000]
内-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
class Solution {
public:
vector
437. 路径总和 III
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等于 8 的路径有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
class Solution {
public:
unordered_map
129. 求根节点到叶节点数字之和
root
,树中每个节点都存放有一个 0
到 9
之间的数字。
1 -> 2 -> 3
表示数字 123
。输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25
输入:root = [4,9,0,5,1]
输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026
[1, 1000]
内0 <= Node.val <= 9
10
class Solution {
public:
int helper(TreeNode* root, int sum){
if(!root)
return 0;
else if (!root->left && !root->right)
return 10*sum + root->val;
return helper(root->left, 10*sum + root->val) +
helper(root->right, 10*sum + root->val);
}
int sumNumbers(TreeNode* root) {
return helper(root, 0);
}
};
114. 二叉树展开为链表
root
,请你将它展开为一个单链表:
TreeNode
,其中 right
子指针指向链表中下一个结点,而左子指针始终为 null
。输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
输入:root = []
输出:[]
输入:root = [0]
输出:[0]
[0, 2000]
内-100 <= Node.val <= 100
class Solution {
public:
TreeNode* after = nullptr;
void flatten(TreeNode* root) {
if(!root)return;
flatten(root->right);
flatten(root->left);
root->right=after;
root->left=nullptr;
after=root;
}
};
//time: O(n) space:O(1)
class Solution {
public:
void flatten(TreeNode* root) {
if(!root||(!root->left&&!root->right))return ;
TreeNode*cur=root;
while(cur){
if(cur->left){
//record the next node, it is equal to the cur->right
TreeNode* next = cur->left;
TreeNode* temp = next; //find the rightmost node of next
while(temp->right){
temp=temp->right;
}
//the next node of the rightmost node of next is cur->right
temp->right = cur->right;
cur->left = nullptr; //set the left node to nullptr
cur->right = next;
}
cur=cur->right; //move the pointer
}
return ;
}
面试题 17.12. BiNode
TreeNode
可用来表示单向链表(其中left
置空,right
为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。输入: [4,2,5,1,3,null,6,0]
输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]
class Solution {
public:
TreeNode* res=new TreeNode(-1);
TreeNode* tmp=res;
TreeNode* convertBiNode(TreeNode* root) {
if(!root)return root;
convertBiNode(root->left);
tmp->right=root;
root->left=nullptr;
tmp=tmp->right;
convertBiNode(root->right);
return res->right;
}
};
617. 合并二叉树
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if(!t1)
return t2;
if(!t2)
return t1;
TreeNode *root=new TreeNode(0);
if(t1)
root->val+=t1->val;
if(t2)
root->val+=t2->val;
root->left=mergeTrees(t1?t1->left:nullptr,t2?t2->left:nullptr);
root->right=mergeTrees(t1?t1->right:nullptr,t2?t2->right:nullptr);
return root;
}
};
116. 填充每个节点的下一个右侧节点指针
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
NULL
。NULL
。
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
4096
-1000 <= node.val <= 1000
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr || root->left == nullptr)
return root;
root->left->next = root->right;
if (root->next) //例如节点5->6
root->right->next = root->next->left;
connect(root->left);
connect(root->right);
return root;
}
};
//通用BFS!!!
class Solution {
public:
Node* connect(Node* root) {
if (!root) return root;
Node* cur = root;
while (cur) {
Node* head = new Node(), *tail = head;
for (Node* p = cur; p; p = p->next) {
if (p->left) tail = tail->next = p->left;//{ tail->next = p->left;tail = tail->next;}
if (p->right) tail = tail->next = p->right;
}
cur = head->next;
}
return root;
}
};
LCP 34. 二叉树染色
root
的二叉树模型,初始所有结点均为白色,可以用蓝色染料给模型结点染色,模型的每个结点有一个 val
价值。小扣出于美观考虑,希望最后二叉树上每个蓝色相连部分的结点个数不能超过 k
个,求所有染成蓝色的结点价值总和最大是多少?
root = [5,2,3,4], k = 2
12
root = [4,1,3,9,null,null,2], k = 2
16
1 <= k <= 10
1 <= val <= 10000
1 <= 结点数量 <= 10000
class Solution {
public:
array
199. 二叉树的右视图
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <---
/ \
2 3 <---
\ \
5 4 <---
//dfs
class Solution {
public:
vector
▼二叉搜索树
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==nullptr) return nullptr;
if(root->val > p->val&&root->val > q->val){//说明p,q都在左子树
return lowestCommonAncestor(root->left,p,q);}
else if(root->val < p->val && root->val < q->val)//说明p,q都在右子树
return lowestCommonAncestor(root->right,p,q);
return root;//p,q各在一边,说明当前的根就是最近共同祖先
}
96. 不同的二叉搜索树
n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。输入:n = 3
输出:5
输入:n = 1
输出:1
1 <= n <= 19
class Solution { //cantalan数
public:
int numTrees(int n) {
vector
700. 二叉搜索树中的搜索
给定二叉搜索树:
4
/ \
2 7
/ \
1 3
和值: 2
2
/ \
1 3
5
,但因为没有节点值为 5
,我们应该返回 NULL
。//迭代
class Solution {
public:
TreeNode* searchBST(TreeNode* root, const int& val) {
while(root&&val!=root->val){
if(val
面试题 04.06. 后继者
null
。输入: root = [2,1,3], p = 1
2
/ \
1 3
输出: 2
输入: root = [5,3,6,2,4,null,null,1], p = 6
5
/ \
3 6
/ \
2 4
/
1
输出: null
class Solution {
public:
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
TreeNode* res = nullptr;
while(root) {
if(root->val > p->val) { //找到刚好大于p的节点
res = root;
root = root->left;
}
else root = root->right;
}
return res;
}
};
701. 二叉搜索树中的插入操作
输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:
输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]
输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]
0
和 10^4
之间0
到 10^8
-10^8 <= val <= 10^8
//递归
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(!root){
return root=new TreeNode(val);
}
else if(root->val>val) root->left=insertIntoBST(root->left,val);
else root->right=insertIntoBST(root->right,val);
return root;
}
};
//迭代
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int k) {
if(!root){
return root=new TreeNode(k);
}
TreeNode*parent=root,*p=root;
while(p){
parent=p;
p=p->val < k?p->right:p->left;
}
if(parent->val < k)parent->right = new TreeNode(k);
else parent->left = new TreeNode(k);
return root;
}
};
108. 将有序数组转换为二叉搜索树
nums
,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
输入:nums = [1,3]
输出:[3,1]
解释:[1,3] 和 [3,1] 都是高度平衡二叉搜索树。
class Solution {
public:
TreeNode* helper(vector
109. 有序链表转换二叉搜索树
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
class Solution{
public:
ListNode* h;
TreeNode* dfs(int s,int e){ //中序遍建立树
if(s>e)return nullptr;
int m=(s+e+1)>>1;
TreeNode* root=new TreeNode();
root->left=dfs(s,m-1);
root->val=h->val;
h=h->next;
root->right = dfs(m+1,e);
return root;
}
TreeNode* sortedListToBST(ListNode* head) {
if(!head)return nullptr;
if(!head->next)return new TreeNode(head->val);
h=head;
ListNode *temp=head; int n=0;
while(temp){ //统计链表节点数
n++;
temp=temp->next;
}
return dfs(0,n-1);
}
};
//方法二:前序遍历
class Solution {
public:
TreeNode* sortedListToBST(ListNode* head) {
return buildtree(head, nullptr);
}
ListNode* getmid(ListNode* left, ListNode* right) {//找中
ListNode* slow = left;
ListNode* fast = left;
while (fast != right && fast->next != right) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
TreeNode* buildtree(ListNode* left, ListNode* right) {
if (left == right)
return nullptr;
ListNode* mid = getmid(left, right);
TreeNode* root = new TreeNode(mid->val);
root->left = buildtree(left, mid);
root->right = buildtree(mid->next, right);
return root;
}
};
450. 删除二叉搜索树中的节点
root = [5,3,6,2,4,null,7]
key = 3
5
/ \
3 6
/ \ \
2 4 7
给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
5
/ \
4 6
/ \
2 7
另一个正确答案是 [5,2,6,null,4,null,7]。
5
/ \
2 6
\ \
4 7
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key)
{
if (root == nullptr) return root; // 第一种情况:没找到删除的节点,遍历到空节点直接返回了
if (root->val == key) {
// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点
if (root->left == nullptr) return root->right;
// 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
else if (root->right == nullptr) return root->left;
// 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置
// 并返回删除节点右孩子为新的根节点。
else {
TreeNode* cur = root->right; // 找右子树最左面的节点
while(cur->left != nullptr) {
cur = cur->left;
}
cur->left = root->left; // 把要删除的节点(root)左子树放在cur的左孩子的位置
TreeNode* tmp = root; // 把root节点保存一下,下面来删除
root = root->right; // 返回旧root的右孩子作为新root
delete tmp; // 释放节点内存(这里不写也可以,但C++最好手动释放一下吧)
return root;
}
}
if (root->val > key) root->left = deleteNode(root->left, key);
if (root->val < key) root->right = deleteNode(root->right, key);
return root;
}
};
//另一种方法
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key)
{
if (root == nullptr) return root;
if (root->val == key) {
if (root->right == nullptr) { // 这里第二次操作目标值:最终删除的作用
return root->left;
}
TreeNode *cur = root->right;
while (cur->left) {
cur = cur->left;
}
swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左节点。
}
root->left = deleteNode(root->left, key);
root->right = deleteNode(root->right, key);
return root;
}
};
783. 二叉搜索树节点最小距离
root
,返回 树中任意两不同节点值之间的最小差值 。输入:root = [4,2,6,1,3]
输出:1
输入:root = [1,0,48,null,null,12,49]
输出:1
[2, 100]
内0 <= Node.val <= 105
//递归
class Solution {
public:
TreeNode *pre=nullptr;
int ans=INT_MAX;
void dfs(TreeNode* root){
if(!root)return ;
dfs(root->left);
if(pre) ans=min(root->val-pre->val,ans);
pre=root;
dfs(root->right);
}
int minDiffInBST(TreeNode* root) {
dfs(root);
return ans;
}
};
//迭代
class Solution {
public:
TreeNode *pre=nullptr;
int ans=INT_MAX;
stack
98. 验证二叉搜索树
输入:
2
/ \
1 3
输出: true
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
//迭代
class Solution {
public:
//TreeNode *pre=nullptr;
stack
230. 二叉搜索树中第K小的元素
root
,和一个整数 k
,请你设计一个算法查找其中第 k
个最小元素(从 1 开始计数)。输入:root = [3,1,4,null,2], k = 1
输出:1
输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
n
。1 <= k <= n <= 104
0 <= Node.val <= 104
class Solution {
public:
stack
剑指 Offer 33. 二叉搜索树的后序遍历序列
true
,否则返回 false
。假设输入的数组的任意两个数字都互不相同。 5
/ \
2 6
/ \
1 3
输入: [1,6,3,2,5]
输出: false
输入: [1,3,2,6,5]
输出: true
数组长度 <= 1000
class Solution {
public:
bool verifyPostorder(vector
669. 修剪二叉搜索树
root
,同时给定最小边界low
和最大边界 high
。通过修剪二叉搜索树,使得所有节点的值在[low, high]
中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]
输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]
输入:root = [1], low = 1, high = 2
输出:[1]
输入:root = [1,null,2], low = 1, high = 3
输出:[1,null,2]
输入:root = [1,null,2], low = 2, high = 4
输出:[2]
[1, 104]
内0 <= Node.val <= 104
0 <= low <= high <= 104
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root)return root;
if(root->val
1382. 将二叉搜索树变平衡
输入:root = [1,null,2,null,3,null,4,null,null]
输出:[2,1,3,null,null,null,4]
解释:这不是唯一的正确答案,[3,1,4,null,2,null,null] 也是一个可行的构造方案。
1
到 10^4
之间。1
到 10^5
之间。class Solution {
public:
void inorder(TreeNode* root, vector
剑指 Offer 36. 二叉搜索树与双向链表
class Solution {
public:
void build(Node* root, Node* &head, Node *&tail) {//指针类型,你可以访问head 指针指向的区域,不能更改指针指向的地址 指针引用类型,除了可以访问指针指向的区域,还可以更改指针指向的地址, 因为后续代码中有做 head = root 这样的操作,所以更改了指针地址,所以用指针引用
if (root) {
build(root->left, head, tail);
if (head == nullptr) {//头节点为空
head = tail = root;
} else {
tail->right = root;
root->left = tail;
tail = root;
}
build(root->right, head, tail);
}
}
Node* treeToDoublyList(Node* root) {
if (root == nullptr) return nullptr;
Node *head = nullptr, *tail = nullptr;
build(root, head, tail);
head->left = tail;
tail->right = head;
return head;
}
};
◉ 十二、DFS和BFS
剑指 Offer 12. 矩阵中的路径
m x n
二维字符网格 board
和一个字符串单词 word
。如果 word
存在于网格中,返回 true
;否则,返回 false
。输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
1 <= board.length <= 200
1 <= board[i].length <= 200
board
和 word
仅由大小写英文字母组成 int m;
int n;
bool dfs(const string &s,vector
22. 括号生成
n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
输入:n = 1
输出:["()"]
1 <= n <= 8
//dfs
void dfs(string path,int l,int r){
if(l==0&&r==0){
ans.emplace_back(path);
return ;
}
if(l>0)dfs(path+"(",l-1,r);
if(r>l)dfs(path+")",l,r-1);
}
//bfs:记忆化搜索
class Solution {
public:
vector
130. 被围绕的区域
m x n
的矩阵 board
,由若干字符 'X'
和 'O'
,找到所有被 'X'
围绕的区域,并将这些区域里所有的 'O'
用 'X'
填充。输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
输入:board = [["X"]]
输出:[["X"]]
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j]
为 'X'
或 'O'
//dfs
class Solution {
public:
void dfs(vector
剑指 Offer 13. 机器人的运动范围
[0,0]
到坐标 [m-1,n-1]
。一个机器人从坐标 [0, 0]
的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?输入:m = 2, n = 3, k = 1
输出:3
输入:m = 3, n = 1, k = 0
输出:1
1 <= n,m <= 100
0 <= k <= 20
//dfs
class Solution {
public:
int ans=0;
bool isValid(int i, int j,int k){
int sum=0;
while(i){
sum+=(i%10);
i/=10;
}
while(j){
sum+=(j%10);
j/=10;
}
return sum>k;
}
void dfs(int i, int j,int k,vector
200. 岛屿数量
'1'
(陆地)和 '0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j]
的值为 '0'
或 '1'
//dfs
class Solution {
public:
void dfs(vector
马的遍历
3 3 1 1
0 3 2
3 -1 1
2 1 4
#include
1905. 统计子岛屿
m x n
的二进制矩阵 grid1
和 grid2
,它们只包含 0
(表示水域)和 1
(表示陆地)。一个 岛屿 是由 四个方向 (水平或者竖直)上相邻的 1
组成的区域。任何矩阵以外的区域都视为水域。grid2
的一个岛屿,被 grid1
的一个岛屿 完全 包含,也就是说 grid2
中该岛屿的每一个格子都被 grid1
中同一个岛屿完全包含,那么我们称 grid2
中的这个岛屿为 子岛屿 。grid2
中 子岛屿 的 数目 。输入:grid1 = [[1,1,1,0,0],[0,1,1,1,1],[0,0,0,0,0],[1,0,0,0,0],[1,1,0,1,1]], grid2 = [[1,1,1,0,0],[0,0,1,1,1],[0,1,0,0,0],[1,0,1,1,0],[0,1,0,1,0]]
输出:3
解释:如上图所示,左边为 grid1 ,右边为 grid2 。
grid2 中标红的 1 区域是子岛屿,总共有 3 个子岛屿。
输入:grid1 = [[1,0,1,0,1],[1,1,1,1,1],[0,0,0,0,0],[1,1,1,1,1],[1,0,1,0,1]], grid2 = [[0,0,0,0,0],[1,1,1,1,1],[0,1,0,1,0],[0,1,0,1,0],[1,0,0,0,1]]
输出:2
解释:如上图所示,左边为 grid1 ,右边为 grid2 。
grid2 中标红的 1 区域是子岛屿,总共有 2 个子岛屿。
m == grid1.length == grid2.length
n == grid1[i].length == grid2[i].length
1 <= m, n <= 500
grid1[i][j]
和 grid2[i][j]
都要么是 0
要么是 1
。//dfs
class Solution {
public:
bool f;
void dfs(int i,int j,vector
695. 岛屿的最大面积
0
和 1
的非空二维数组 grid
。1
(代表土地) 构成的组合,这里的「相邻」要求两个 1
必须在水平或者竖直方向上相邻。你可以假设 grid
的四个边缘都被 0
(代表水)包围着。0
。)[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
6
。注意答案不应该是 11
,因为岛屿只能包含水平或垂直的四个方向的 1
。[[0,0,0,0,0,0,0,0]]
0
。//dfs
class Solution {
public:
int dfs(vector
LCP 07. 传递信息
n
,以及按 [玩家编号,对应可传递玩家编号]
关系组成的二维数组 relation
。返回信息从小 A (编号 0 ) 经过 k
轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。
n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3
3
n = 3, relation = [[0,2],[2,1]], k = 2
0
2 <= n <= 10
1 <= k <= 5
1 <= relation.length <= 90, 且 relation[i].length == 2
0 <= relation[i][0],relation[i][1] < n 且 relation[i][0] != relation[i][1]
//DFS
class Solution {
public:
int ans = 0;
int numWays(int n, vector
752. 打开转盘锁
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
。每个拨轮可以自由旋转:例如把 '9'
变为 '0'
,'0'
变为 '9'
。每次旋转都只能旋转一个拨轮的一位数字。'0000'
,一个代表四个拨轮的数字的字符串。deadends
包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。target
代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1
。输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,
因为当拨动到 "0102" 时这个锁就会被锁定。
输入: deadends = ["8888"], target = "0009"
输出:1
解释:
把最后一位反向旋转一次即可 "0000" -> "0009"。
输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
输出:-1
解释:
无法旋转到目标数字且不被锁定。
输入: deadends = ["0000"], target = "8888"
输出:-1
1 <= deadends.length <= 500
deadends[i].length == 4
target.length == 4
target
不在 deadends
之中target
和 deadends[i]
仅由若干位数字组成static int x=[](){
ios::sync_with_stdio(false);
cin.tie(NULL);
return 0;
}();
class Solution {
public:
int openLock(vector
127. 单词接龙
wordList
中从单词 beginWord
和 endWord
的 转换序列 是一个按下述规格形成的序列:
beginWord
。endWord
。wordList
中的单词。beginWord
和 endWord
和一个字典 wordList
,找到从 beginWord
到 endWord
的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:5
解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:0
解释:endWord "cog" 不在字典中,所以无法进行转换。
1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord
、endWord
和 wordList[i]
由小写英文字母组成beginWord != endWord
wordList
中的所有字符串 互不相同class Solution {
public:
queue
120. 三角形最小路径和
triangle
,找出自顶向下的最小路径和。i
,那么下一步可以移动到下一行的下标 i
或 i + 1
。输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
输入:triangle = [[-10]]
输出:-10
1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104
O(n)
的额外空间(n
为三角形的总行数)来解决这个问题吗?//DFS
class Solution {
public:
int dfs(vector
1306. 跳跃游戏 III
arr
,你最开始位于该数组的起始下标 start
处。当你位于下标 i
处时,你可以跳到 i + arr[i]
或者 i - arr[i]
。输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
输入:arr = [4,2,3,0,3,1,2], start = 0
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 0 -> 下标 4 -> 下标 1 -> 下标 3
输入:arr = [3,0,2,1,2], start = 2
输出:false
解释:无法到达值为 0 的下标 1 处。
1 <= arr.length <= 5 * 10^4
0 <= arr[i] < arr.length
0 <= start < arr.length
//dfs
class Solution {
public:
bool dfs(vector
542. 01 矩阵
输入:
[[0,0,0],
[0,1,0],
[0,0,0]]
输出:
[[0,0,0],
[0,1,0],
[0,0,0]]
输入:
[[0,0,0],
[0,1,0],
[1,1,1]]
输出:
[[0,0,0],
[0,1,0],
[1,2,1]]
//bfs
struct P{
int x, y, dis;
};
class Solution {
public:
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
vector
994. 腐烂的橘子
0
代表空单元格;1
代表新鲜橘子;2
代表腐烂的橘子。-1
。输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j]
仅为 0
、1
或 2
class Solution {
public:
int orangesRotting(vector
◉ 十三、字符串匹配和分割
void split1(vector
void split2(vector
139. 单词拆分
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
et
127. 单词接龙
wordList
中从单词 beginWord
和 endWord
的 转换序列 是一个按下述规格形成的序列:
beginWord
。endWord
。wordList
中的单词。beginWord
和 endWord
和一个字典 wordList
,找到从 beginWord
到 endWord
的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:5
解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:0
解释:endWord "cog" 不在字典中,所以无法进行转换。
1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord
、endWord
和 wordList[i]
由小写英文字母组成beginWord != endWord
wordList
中的所有字符串 互不相同class Solution {
public:
queue
120. 三角形最小路径和
triangle
,找出自顶向下的最小路径和。i
,那么下一步可以移动到下一行的下标 i
或 i + 1
。输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
输入:triangle = [[-10]]
输出:-10
1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104
O(n)
的额外空间(n
为三角形的总行数)来解决这个问题吗?//DFS
class Solution {
public:
int dfs(vector
1306. 跳跃游戏 III
arr
,你最开始位于该数组的起始下标 start
处。当你位于下标 i
处时,你可以跳到 i + arr[i]
或者 i - arr[i]
。输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
输入:arr = [4,2,3,0,3,1,2], start = 0
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案:
下标 0 -> 下标 4 -> 下标 1 -> 下标 3
输入:arr = [3,0,2,1,2], start = 2
输出:false
解释:无法到达值为 0 的下标 1 处。
1 <= arr.length <= 5 * 10^4
0 <= arr[i] < arr.length
0 <= start < arr.length
//dfs
class Solution {
public:
bool dfs(vector
542. 01 矩阵
输入:
[[0,0,0],
[0,1,0],
[0,0,0]]
输出:
[[0,0,0],
[0,1,0],
[0,0,0]]
输入:
[[0,0,0],
[0,1,0],
[1,1,1]]
输出:
[[0,0,0],
[0,1,0],
[1,2,1]]
//bfs
struct P{
int x, y, dis;
};
class Solution {
public:
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
vector
994. 腐烂的橘子
0
代表空单元格;1
代表新鲜橘子;2
代表腐烂的橘子。-1
。输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j]
仅为 0
、1
或 2
class Solution {
public:
int orangesRotting(vector