LeetCode 笔记六
前言:主要是记录一些算法题的解题思路与技巧,纯当笔记用。
图片与部分代码来源:leetcode
图片与部分代码来源:leetcode
图片与部分代码来源:leetcode
一.乘积小于 K 的子数组
力扣链接:乘积小于 K 的子数组
这题就一个关键点,懂了这点,这题就会了。首先利用双指针扩展窗口直至当前窗口的乘积位于K的临界值,即再乘上下一个数,乘积会大于或等于K 。然后缩小窗口,即增加左指针的值缩小窗口,因为所有的数都是正数,所以相当于除了一个正数。如果之前的乘积是小于 K 的,那么除了一个正数之后也一定是小于K的。换一种表述,如果一个数组是符合要求的,那么它的一个子数组也一定是符合要求的, 所以计数可以直接加 right-left 。具体实现看代码。
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
int left=0,right=0; //左右指针
int sum=1;
int ret=0; //计数
while(right<nums.size()){
sum*=nums[right]; //类乘
if(sum<k){ //如果乘积小于K则继续类乘,并且计数加一
++right;
++ret;
}
else{ //如果乘积大于等于K则缩小窗口并且利用之前的计算结果避免重复计算
sum/=nums[right];
if(left!=right)
sum/=nums[left];
++left;
if(right<left)
right=left;
ret+=right-left;
}
}
while(left<nums.size()){ //计算剩余的满足条件的子数组
++left;
ret+=right-left;
}
return ret;
}
};
二.重排链表
力扣链接:重排链表
很经典的一题,考察了链表的很多知识点。这题的最优解是是先用快慢指针分割链表,把一个链表分成相对平均的两半。反转其中的一半。然后再把这两个链表合并就行了。
class Solution {
public:
ListNode* divis(ListNode* head){ //利用快慢指针分割链表
ListNode* fast=head,*slow=head;
while(fast){
fast=fast->next;
if(!fast)
return slow;
fast=fast->next;
if(!fast || !fast->next){
if(fast && !fast->next)
slow=slow->next;
ListNode* tmp=slow->next;
slow->next=nullptr;
return tmp;
}
else
slow=slow->next;
}
return slow;
}
ListNode* rever(ListNode* head){ //反转链表
if(!head)
return nullptr;
ListNode* first=head,*second=first->next;
first->next=nullptr;
while(second){
ListNode* third=second->next;
second->next=first;
first=second;
second=third;
}
return first;
}
void reorderList(ListNode* head) { //合并两个链表
ListNode* first=head;
ListNode* second=divis(head);
second=rever(second);
while(first && second){
ListNode* tmp=first->next;
ListNode* tmp1=second->next;
first->next=second;
first=tmp;
second->next=first;
second=tmp1;
}
return;
}
};
三.二叉树的右视图
力扣链接:二叉树的右视图
方法一:
可以用递归做这题,具体的做法为先遍历二叉树的右子树,再遍历左子树,同时记录当前遍历到的二叉树层数。边遍历边把符合条件的值加入到结果数组中,当前值是否加入结果数组的条件为当前递归层数是否大于或等于结果数组中元素个数。如果大于等于,则把当前节点的值加入结果数组中。
class Solution {
public:
int dfs(vector<int>& ret,TreeNode* root,int count){
if(!root)
return 1;
if(count>=ret.size()) //如果当前递归层数大于或等于结果数组中元素个数,则把当前节点的值加入到结果集中
ret.push_back(root->val);
dfs(ret,root->right,count+1); //遍历右子树
dfs(ret,root->left,count+1); //遍历左子树
return 1;
}
vector<int> rightSideView(TreeNode* root) {
vector<int> ret;
dfs(ret,root,0);
return ret;
}
};
方法二
也可以利用二叉树的层序遍历,只需要把二叉树每一层所有节点中的最右边的节点的值加入结果数组中就行了。
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> ret;
if(!root)
return ret;
queue<TreeNode*> que;
que.push(root);
while(que.size()){
ret.push_back(que.back()->val); //把二叉树当前层所有节点中的最右边的节点的值加入结果数组中
int length=que.size();
while(length){ //把当前层中所有节点加入队列
TreeNode* tmp=que.front();
que.pop();
if(tmp->left)
que.push(tmp->left);
if(tmp->right)
que.push(tmp->right);
--length;
}
}
return ret;
}
};
四.下一个排列
力扣链接:下一个排列
三步走:
- 找到一个较小数,使得他尽量往右靠;找到一个较大数,使得他尽量往右。(较小数应小于较大数)
- 交换两数。
- 把交换前较小数所在位置往后的所有数按升序排列。
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int left=nums.size()-2;
while(left>=0 && nums[left]>=nums[left+1]) //找到较小数
--left;
if(left>=0){
int right=nums.size()-1;
while(right>=0 && nums[right]<=nums[left]) //找到较大数
--right;
swap(nums[left],nums[right]); //交换两数
}
sort(nums.begin() + left + 1, nums.end()); //升序排列
}
};
五.平衡二叉树
力扣链接:平衡二叉树
很基础的一题,但有一个优化的小技巧,就是在下层二叉树已经不满足的条件的情况下可以加一个判断,避免递归函数进行多余的递归,以提高效率。
class Solution {
public:
bool ret=true;
int dfs(TreeNode* root){
if(!root)
return 0;
if(!ret) //如果子树已经不满足条件了,就没必要继续递归下去
return 1;
int left=dfs(root->left)+1;
int right=dfs(root->right)+1;
if(abs(left-right)>=2) //如果左右子树差超过1,则不满足条件
ret=false;
return max(left,right);
}
bool isBalanced(TreeNode* root) {
dfs(root);
return ret;
}
};