编程
1、全排列
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
https://leetcode-cn.com/problems/permutations/solution/
class Solution {
public:
void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){
// 所有数都填完了
if (first == len) {
res.emplace_back(output);
return;
}
for (int i = first; i < len; ++i) {
// 动态维护数组
swap(output[i], output[first]);
// 继续递归填下一个数
backtrack(res, output, first + 1, len);
// 撤销操作
swap(output[i], output[first]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int> > res;
backtrack(res, nums, 0, (int)nums.size());
return res;
}
};
2、螺旋矩阵
给你一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
https://leetcode-cn.com/problems/spiral-matrix/
解题思路:
这里的方法不需要记录已经走过的路径,所以执行用时和内存消耗都相对较小
1、首先设定上下左右边界
2、其次向右移动到最右,此时第一行因为已经使用过了,可以将其从图中删去,体现在代码中就是重新定义上边界
3、判断若重新定义后,上下边界交错,表明螺旋矩阵遍历结束,跳出循环,返回答案
4、若上下边界不交错,则遍历还未结束,接着向下向左向上移动,操作过程与第一,二步同理
5、不断循环以上步骤,直到某两条边界交错,跳出循环,返回答案
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> ans;
if(matrix.empty()) return ans; //若数组为空,直接返回答案
int u = 0; //赋值上下左右边界
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while(true)
{
for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
if(-- r < l) break; //重新设定有边界
for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
if(-- d < u) break; //重新设定下边界
for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
if(++ l > r) break; //重新设定左边界
}
return ans;
}
};
3、反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
https://leetcode-cn.com/problems/reverse-linked-list/
1、定义两个指针:pre和cur;pre在前 cur在后。
2、每次让 cur的next指向pre实现一次局部反转
3、局部反转完成之后,pre和 cur同时往前移动一个位置
4、循环上述过程,直至 cur到达链表尾部
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur=head;
ListNode* pre=nullptr;
ListNode* temp=nullptr;
while(cur!=nullptr){
temp=cur->next;//记录当前节点的下一个节点
cur->next=pre;//然后将当前节点指向pre
pre=cur;//pre和cur节点都前进一位
cur=temp;
}
return pre;
}
};
4、反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
https://leetcode-cn.com/problems/reverse-linked-list-ii/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
// 定义一个dummyHead, 方便处理
ListNode* dummy = new ListNode(0);
dummy->next = head;
// 初始化指针
ListNode* g = dummy;
ListNode* p = dummy-> next;
// 将指针移到相应的位置
for(int i = 0; i < left -1; i++){
g = g->next;
p = p->next;
}
// 头插法插入节点
for(int i = 0; i < right - left; i++){
ListNode* remove = p->next;
p->next = p->next->next;
remove->next = g->next;
g->next = remove;
}
return dummy->next;
}
};
5、K 个一组翻转链表
https://leetcode-cn.com/problems/reverse-nodes-in-k-group/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
if (head == nullptr || head->next == nullptr){
return head;
}
//定义一个假的节点。
ListNode* dummy=new ListNode(0);
//假节点的next指向head。
// dummy->1->2->3->4->5
dummy->next=head;
//初始化pre和end都指向dummy。pre指每次要翻转的链表的头结点的上一个节点。end指每次要翻转的链表的尾节点
ListNode* pre=dummy;
ListNode* end=dummy;
while(end->next!=nullptr){
//循环k次,找到需要翻转的链表的结尾,这里每次循环要判断end是否等于空,因为如果为空,end.next会报空指针异常。
//dummy->1->2->3->4->5 若k为2,循环2次,end指向2
for(int i=0;i<k&&end != nullptr;i++){
end=end->next;
}
//如果end==null,即需要翻转的链表的节点数小于k,不执行翻转。
if(end==nullptr){
break;
}
//先记录下end.next,方便后面链接链表
ListNode* next=end->next;
//然后断开链表
end->next=nullptr;
//记录下要翻转链表的头节点
ListNode* start=pre->next;
//翻转链表,pre.next指向翻转后的链表。1->2 变成2->1。 dummy->2->1
pre->next=reverse(start);
//翻转后头节点变到最后。通过.next把断开的链表重新链接。
start->next=next;
//将pre换成下次要翻转的链表的头结点的上一个节点。即start
pre=start;
//翻转结束,将end置为下次要翻转的链表的头结点的上一个节点。即start
end=start;
}
return dummy->next;
}
//链表翻转
// 例子: head: 1->2->3->4
ListNode* reverse(ListNode* head) {
//单链表为空或只有一个节点,直接返回原单链表
if (head == nullptr || head->next == nullptr){
return head;
}
//前一个节点指针
ListNode* pre = nullptr;
//当前节点指针
ListNode* cur = head;
//下一个节点指针
ListNode* next = nullptr;
while (cur != nullptr){
next = cure->next;//next 指向下一个节点,保存当前节点后面的链表。
cur->next=pre;//将当前节点next域指向前一个节点 null<-1<-2<-3<-4
pre = cur;//pre指针向后移动。pre指向当前节点。
cur = next;//cure指针向后移动。下一个节点变成当前节点
}
return preNode;
}
};
6、岛屿数量
https://leetcode-cn.com/problems/number-of-islands/
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。
class Solution {
public:
int numIslands(vector<vector<char>>& nums) {
int res = 0;
int m = nums.size(),
n = nums[0].size();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (nums[i][j] == '1') {
dfs(nums, i, j);
res ++;
}
}
}
return res;
}
void dfs(vector<vector<char>>& nums, int i, int j) {
if (i < 0 || j < 0 || i >= nums.size() || j >= nums[0].size() || nums[i][j] == '0')
return;
nums[i][j] = '0';
dfs(nums, i + 1, j);
dfs(nums, i, j + 1);
dfs(nums, i - 1, j);
dfs(nums, i, j - 1);
}
};
7、最长回文串
给你一个字符串 s
,找到 s
中最长的回文子串。
https://leetcode-cn.com/problems/longest-palindromic-substring/
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
vector<vector<int>> dp(n, vector<int>(n));
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++) {
dp[i][i] = true;
}
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= n; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < n; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= n) {
break;
}
if (s[i] != s[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];//只有 s[i+1:j−1] 是回文串,并且 s 的第 i 和 j 个字母相同时,s[i:j]才会是回文串。
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substr(begin, maxLen);
}
};
8、数组中的第K个最大元素(快排65)
https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
给定整数数组 nums
和整数 k
,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 优先队列可以用一个 for 循环解决哈。就是在 for 循环里面判断小顶堆里面的 size() 是否大于 k 个数,是的话就 poll() 出去;整个 for 循环结束之后剩下来的就是 k 个数的小顶堆。堆顶即第 k 大的数。
// priority_queue<Type, Container, Functional>
// Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。 STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆
priority_queue<int,vector<int>, greater<int> > heap;
for (int num : nums) {
heap.emplace(num);
if (heap.size() > k) {
heap.pop(); //此时k+1个
}
}
return heap.top();
}
};
快排之单边循环法
class Solution {
public :
int findKthLargest(vector<int>&nums, int k) {
int len = nums.size();
int left = 0;
int right = len - 1;
// 转换一下,第 k 大元素的下标是 len - k
int target = len - k;
while (true) {
int index = partition(nums, left, right);
if (index == target) {
return nums[index];
} else if (index < target) {
left = index + 1;
} else {
right = index - 1;
}
}
}
* 对数组 nums 的子区间 [left..right] 执行 partition 操作,返回 nums[left] 排序以后应该在的位置
* 在遍历过程中保持循环不变量的定义:
int partition(vector<int>& nums, int left, int right) {
int pivot = nums[left];
int j = left;
for (int i = left + 1; i <= right; i++) {
if (nums[i] < pivot) {
// j 的初值为 left,先右移,再交换,小于 pivot 的元素都被交换到前面
j++;
swap(nums, j, i);
}
}
// 在之前遍历的过程中,满足 nums[left + 1..j] < pivot,并且 nums(j..i) >= pivot
swap(nums, j, left);
// 交换以后 nums[left..j - 1] < pivot, nums[j] = pivot, nums[j + 1..right] >= pivot
return j;
}
void swap(vector<int>& nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
快排之双边循环法
lass Solution {
vector<int> sortArray(vector<int>& nums) {
quickSort(nums,0,nums.size()-1);
return nums;
}
//快速排序主函数
void quickSort(vector<int>& nums,int low,int high){
//递归终止条件
if(low >= high){
return;
}
int mid = partition(nums,low,high);
quickSort(nums,low,mid-1);
quickSort(nums,mid+1,high);
}
//分割
//每次找到基准元素对应的排序位置
int partition(vector<int>& nums,int start,int end){
int pivot = nums[start];
int left = start;//不能从start+1开始,否则当只有两个数时,无论大小都会交换
int right = end;
while(left != right){
while(left < right && nums[right] > pivot){//也可以写nums[right]>=pivot无影响
right--;
}
while(left < right && nums[left] <= pivot){
left++;
}
//结束两个子循环会有两种情况,只有left小于right时,才进行交换
if(left < right){
swap(nums,left,right);
}
}
//此时跳出while循环,只可能是left等于right,不会有left>right的情况
//接下来的交换和返回,写left或者right都行,因为两者相等
swap(nums,start,left);
return left;
}
void swap(vector<int>& nums,int i,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
9、有效的括号
https://leetcode-cn.com/problems/valid-parentheses/
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。
class Solution {
public:
bool isValid(string s) {
unordered_map<char,int> m{{'(',1},{'[',2},{'{',3},
{')',4},{']',5},{'}',6}};
stack<char> st;
bool istrue=true;
for(char c:s){
int flag=m[c];
if(flag>=1&&flag<=3) st.push(c);
else if(!st.empty()&&m[st.top()]==flag-3) st.pop();
else {istrue=false;break;}
}
if(!st.empty()) istrue=false;
return istrue
}
};
10、无重复字符的最长子串
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// if(s.size()==0 ||s.size()==1) return s.size();哈希的有问题 aab
// if(s.size()==2 && s[0]==s[1]) return 1;
// if(s.size()==2 && s[0]!=s[1]) return 2;
// int a=0;
// for(int i=0;i<s.size();i++){
// unordered_map<char,int> dic;
// for(int j=i;j<s.size();j++){
// if(dic.find(s[j])!=dic.end()){
// if(j-i>a) a=j-i;
// break;
// }
// dic[s[j]]++;
// }
// }
// return a;
if(s.size() == 0) return 0;
unordered_set<char> lookup;
int maxStr = 0;
int left = 0;
for(int i = 0; i < s.size(); i++){
while (lookup.find(s[i]) != lookup.end()){
lookup.erase(s[left]);//不断从左缩小窗口
left ++;
}
maxStr = max(maxStr,i-left+1);
lookup.insert(s[i]);
}
return maxStr;
}
};
11、买卖股票的最佳时机
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
class Solution {
public:
int maxProfit(vector<int>& prices) {
// int len = prices.size();
// int res = 0;
// // 前一天卖出可以获得的最大利润
// int pre = 0;
// for (int i = 1; i < len; i++) {
// // 利润差
// int diff = prices[i] - prices[i - 1];
// // 状态转移方程:第i天卖出可以获得的最大利润 = 第i-1天卖出的最大利润 + 利润差
// pre = max(pre + diff, 0);
// res = max(res, pre);
// }
// return res;
// 动态规划。空间复杂度为O1,时间复杂度为On
// 标记一个买入股票的日子的股价记作left,然后尝试在某一天卖出股票,策略如下:
// 如果这一天的股价高于left,则可以考虑卖出股票,利润暂存到res里。
// 如果这一天的股价小于等于left,则应该选择这一天作为买入股票的日子,此时的暂存的利润要么是0,要么是之前暂存的值。
int left = 10000000;
int res = 0;
for(int i : prices){
if(left >= i){
left = i;
}else if(left < i){
res = max(i-left,res);
}
}
return res;
}
};
12、相交链表
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *node1 = headA;
ListNode *node2 = headB;
while (node1 != node2) {
node1 = (node1 != NULL) ? node1->next : headB;
//巧妙:是判断(node1 != NULL) ?,而不是(node1-next != NULL) ? 这样不会陷入死循环,比如在B的最后一个节点,node1 != NULL,但是node1->next 为Null,然后赋个node1,而下一步同样node2也走到了A的最后一个节点,所以node2也=Null,所以下一次while的判断不成立,跳出循环,相当于指向了同一个相同的节点Null(死亡),两个人都走了L1+L2的长度,所以最后到终点的时间也相同,不会出现死循环。用(node1-next != NULL) ? 会导致node永远不会指向null
node2 = (node2 != NULL) ? node2->next : headA;
}
return node1;
// 两个链表长度分别为L1+C、L2+C, C为公共部分的长度,第一个人走了L1+C步后,回到第二个人起点走L2步;第2个人走了L2+C步后,回到第一个人起点走L1步。 当两个人走的步数都为L1+L2+C时就两个家伙就相遇相爱了
// 你变成我,走过我走过的路。我变成你,走过你走过的路。然后我们便相遇了
}
};
13、两数之和
https://leetcode-cn.com/problems/two-sum/
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> dic;
for(int i=0;i<nums.size();++i){
auto it=dic.find(target-nums[i]);
if(it!=dic.end()) {
return {i,it->second};
}
dic[nums[i]]=i;
}
return {};
}
};
两数相加
https://leetcode-cn.com/problems/add-two-numbers/
给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* head=new ListNode(-1);//存放结果的链表
ListNode* h=head;//移动指针
int sum=0;//每个位的加和结果
bool carry=false;//进位标志
while(l1!=NULL||l2!=NULL)
{
sum=0;
if(l1!=NULL)
{
sum+=l1->val;
l1=l1->next;
}
if(l2!=NULL)
{
sum+=l2->val;
l2=l2->next;
}
if(carry)
sum++;
h->next=new ListNode(sum%10);
h=h->next;
carry=sum>=10?true:false;
}
if(carry) //如果最后还有一位进位
{
h->next=new ListNode(1);
}
return head->next;
}
};
14、三数之和
https://leetcode-cn.com/problems/3sum/
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
class Solution { //排序,双指针
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
if (nums.size() < 3) return ans;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > 0) return ans;
// 去重
if ( i > 0 && nums[i] == nums[i - 1]) {//为什么要大于0?,防止在i=0是判断出错
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (left < right) {
if(nums[left] + nums[right] + nums[i] > 0) {
right--;
} else if(nums[left] + nums[right] + nums[i] < 0){
left++;
} else {
ans.push_back({nums[left], nums[right], nums[i]});
while (right > left && nums[right] == nums[right - 1]) right--; //去掉重复的的
while (right > left && nums[left] == nums[left + 1]) left++;
left++;//前两个循环结束,是指向在重复元素的最后一位
right--;
}
}
}
return ans;
}
};
15、最大子序和
https://leetcode-cn.com/problems/maximum-subarray/
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
class Solution {
public:
// 动态规划
int maxSubArray(vector<int>& nums) {
//dp[i]表示nums中以nums[i]结尾的最大子序和
vector<int> dp(nums.size());
dp[0] = nums[0];
int result = dp[0];
for (int i = 1; i <nums.size(); i++)
{
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
result = max(result, dp[i]);
}
return result;
}
// 贪心
int maxSubArray(vector<int>& nums) {
//类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
int result = INT_MIN;
int numsSize = int(nums.size());
int sum = 0;
for (int i = 0; i < numsSize; i++)
{
sum += nums[i];
result = max(result, sum);
//如果sum < 0,重新开始找子序串
if (sum < 0)
{
sum = 0;
}
}
return result;
}
};
16、环形链表
https://leetcode-cn.com/problems/linked-list-cycle/
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
class Solution {
public:
bool hasCycle(ListNode *head) {
//当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。
ListNode *slow = head;
ListNode *fast = head;
while(fast != nullptr) {
fast = fast->next;
if(fast != nullptr) {
fast = fast->next;
}
if(fast == slow) {
return true;
}
slow = slow->next;
}
return false;
//如果存在环,如何判断环的长度呢?方法是,快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度。
}
};
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow=head,*fast=head;
while(1){
if(fast ==nullptr || fast->next ==nullptr) return nullptr;
fast=fast->next->next;
slow=slow->next;
if(fast==slow){ //第一次相遇,这时fast走了2nb,slow走了nb
fast=head;
break;
}
}
while(fast != slow){ //让fast和slow走a,这时他们在入环的第一个节点相遇,
fast=fast->next;
slow=slow->next;
}
return fast;
}
};
17、合并两个有序链表
https://leetcode-cn.com/problems/merge-two-sorted-lists/
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
这道题可以使用递归实现,新链表也不需要构造新节点,下面列举递归三个要素
终止条件:两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束
返回值:每一层调用都返回排序好的链表头
本级递归内容:如果l1的 val值更小,则将 l1->next 与排序好的链表头相接,l2 同理
O(m+n),m为l1的长度,n为 l2 的长度
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
if (l1->val <= l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
};
18、二叉树的层序遍历
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。(即逐层地,从左到右访问所有节点)
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> queue;
if (root != nullptr) queue.push(root);
while (!queue.empty()) {
int n = queue.size(); // 无法区分队列中的结点来自哪一层。因此,我们需要稍微修改一下代码,在每一层遍历开始前,先记录队列中的结点数量 n(也就是这一层的结点数量),然后一口气处理完这一层的 n 个结点。
vector<int> level;
for (int i = 0; i < n; ++i) {
TreeNode* node = queue.front();
queue.pop();
level.push_back(node->val);
if (node->left != nullptr) queue.push(node->left);
if (node->right != nullptr) queue.push(node->right);
}
res.push_back(level);
}
return res;
}
};
19、二叉树的锯齿形层序遍历
https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
class Solution {
public:
// 解题思路:每层遍历塞到数组里,那每层的一维数组的值是固定的,所以只要往这层塞数据就可以了
// 根据题意,每层从左往右,下层从右往左,所以每偶数层添加在后面,每奇数层插入在前面,递归即可
void Sum(vector<vector<int>>& arr, TreeNode* root, int row)
{
if(root == nullptr)
return;
if(arr.size()<= row)
arr.push_back(vector<int>());
if(row%2 == 0)
arr[row].push_back(root->val);
else
arr[row].insert(arr[row].begin(), root->val);
Sum(arr, root->left, row+1);
Sum(arr, root->right, row+1);
}
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> arr;
Sum(arr, root, 0);
return arr;
}
};
20、合并两个有序数组
https://leetcode-cn.com/problems/merge-sorted-array/
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
- 思路的重点一个是从后往前确定两组中该用哪个数字
- 另一个是结束条件以第二个数组全都插入进去为止
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = nums1.size() - 1;
m--;
n--;
while (n >= 0) {
while (m >= 0 && nums1[m] > nums2[n]) {
swap(nums1[i--], nums1[m--]);
}
swap(nums1[i--], nums2[n--]);
}
// int i = nums1.size() - 1;
// --m;
// --n;
// while (n >= 0)
// {
// if(m >= 0 && nums1[m] > nums2[n])
// {
// nums1[i--] = nums1[m--];
// }
// else
// {
// nums1[i--] = nums2[n--];
// }
// }
}
};
21、二叉树的最近公共祖先
https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//1 当 left 和 right 同时为空 :说明 root 的左 / 右子树中都不包含 p,q ,返回 null ;
//2 当 left 和 right 同时不为空 :说明 p,q 分列在 root 的 异侧 (分别在 左 / 右子树),因此 root为最近公共祖先,返回 root ;
//3 当 left为空 ,right 不为空 :p,q都不在 root的左子树中,直接返回 right。具体可分为两种情况:
// p,q 其中一个在 root 的 右子树 中,此时 right 指向 p(假设为 p);
// p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
//4 当 left 不为空 , right 为空 :与情况 3. 同理;
if(root == nullptr || root == p || root == q) return root;
TreeNode *left = lowestCommonAncestor(root->left, p, q);
TreeNode *right = lowestCommonAncestor(root->right, p, q);
if(left == nullptr && right == nullptr) return nullptr; // 1.
if(left == nullptr) return right;// 3.
if(right == nullptr) return left;// 4.
return root;// 2. if(left != null and right != null)
}
};
22、颜色填充
红,绿,蓝三种颜色,各自n个,相邻不能一样(首位不能一样),求填充方法数
对于我们可以选择红绿蓝三种颜色,我们可以将它们看成 0, 1, 2。这样一来,一种涂色方案就对应着一个长度为 m 的三进制数,其十进制的范围为 [0, 3^m)。
我们可以枚举 [0, 3^m)数,将其转换为长度为 m 的三进制串,再判断其是否满足任意相邻的两个数位均不相同即可。
int main() {
unordered_map<int, vector<int>> valid;
int m = 3;
// 在 [0, 3^m) 范围内枚举满足要求的 mask
int mask_end = pow(3,3);
for (int mask = 0; mask < mask_end; ++mask) {
vector<int> color;
int mm = mask;
for (int i = 0; i < m; ++i) {
color.push_back(mm % 3);
mm /= 3;
}
bool check = true;
for (int i = 0; i < m - 1; ++i) {
if (color[i] == color[i + 1]) {
check = false;
break;
}
}
if (check) {
valid[mask] = move(color);
}
}
cout << valid.size();
return 0;
}
23、搜索旋转排序数组
https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.size()==0){
return -1;
}
int left=0,right=nums.size()-1;
while(left+1<right){
int mid=left+(right-left)/2;
if(nums[mid]==target){
return mid;
}
if(nums[left]<nums[mid]){
if((nums[mid]>=target)&&(target>=nums[left])){
right=mid;
}else {
left=mid;
}
}
if(nums[right]>nums[mid]){
if((nums[mid]<=target)&&(target<=nums[right])){
left=mid;
}else{
right=mid;
}
}
}
if (nums[left] == target ){
return left;
}
if(nums[right] == target) {
return right;
}
return -1;
}
};
24、x的平方根
给你一个非负整数 x
,计算并返回 x
的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3LqCGAbo-1649167843204)(C:\Users\hgx\AppData\Roaming\Typora\typora-user-images\image-20211012143335307.png)]
class Solution {
public:
int mySqrt(int x) {
if (x == 0) {
return 0;
}
int ans = exp(0.5 * log(x));
return ((long long)(ans + 1) * (ans + 1) <= x ? ans + 1 : ans);
}
};
二分查找
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ((long long)mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
};
class Solution {
public:
int s;
int mySqrt(int x) {
s=x;
if(x==0) return 0;
return ((int)(sqrts(x)));
}
double sqrts(double x){
double res = (x + s / x) / 2;
if (res == x) {
return x;
} else {
return sqrts(res);
}
}
};
25、链表中倒数第k个节点
https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode *fast = head, *slow = head;
for(int i = 0; i < k; i++) {
if(fast == nullptr) return nullptr;
fast = fast->next;
}
while(fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
26、删除链表的倒数第 k 个节点
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head; //等同于ListNode* dummy = new ListNode(0, head);
ListNode* p = dummyHead;
ListNode* q = dummyHead;
for( int i = 0 ; i < n + 1 ; i ++ ){
q = q->next;
}
while(q){
p = p->next;
q = q->next;
}
ListNode* delNode = p->next;
p->next = delNode->next;
delete delNode;
ListNode* retNode = dummyHead->next;
delete dummyHead;
return retNode;
}
};