总结
:刷题的时间安排的不是很好,每天安排的时间不定,需要定时定量完成任务。题解思路都放在的代码中,为了方便后面复习。
文章没有解题思路和代码,主要记录自己的刷题过程。题解在网站都很容易找到,而且有多种的解法。自己在代码中更多记录每次做题的思想。
力扣英文版链接
力扣中文版链接
英文版网站题解更加全面,解题方式也更多。
// C语言写法
// 双指针写法 移动零改为 非零的放在前面 后面的全部置为0
void moveZeroes(int* nums, int numsSize){
int i=0,j=0;
for(i=0;i<numsSize;i++)
{
if(nums[i]!=0)
{
nums[j++]=nums[i];
}
}
while(j<numsSize)
{
nums[j++]=0;
}
}
// C++ 写法
//双指针写法 一个指向头 一个指向尾部 移动规则是移动指针指向值较低的向前(后移动)因为求面积是要找更小的边,如果移动更长的 下一次的值一定比上一次小 没有意义
//还需要一个保存当前最优值的
//0716思考的问题有 循环条件 以及先计算面积还是先比较 记得要有返回值
//时间复杂度:O(N)O(N),双指针总计最多遍历整个数组一次。空间复杂度:O(1)O(1),只需要额外的常数级别的空间。
class Solution {
public:
int maxArea(vector<int>& height)
{
//两个指针
int i=0,j=height.size()-1,sum=0;
while(i<j)
{
int area=min(height[i],height[j])*(j-i);
sum=max(area,sum);
if(height[i]<height[j])
{
i++;
}
else
{
j--;
}
}
return sum;
}
};
//如果是递归的话 可以发现规律 第3阶就是爬2阶和1阶的和
/*
if (n==1)
{return 1}
if (n==2)
{return 2}
return climbStairs(n-1)+climbStairs(n-2)
*/
/*简单的说 下面的方式就是移动数组的方式 需要注意的就是迭代条件 n阶 那就是n次 从1开始*/
/*f(1)=1 f(2)=2 f(3)=3 f(4)=5*/
/*推出 f(0)=1 f(-1)=0*/
/*时间复杂度o(n) 空间复杂度1*/
/*0716 修改写法 这样更好理解
class Solution {
public:
int climbStairs(int n) {
int i=0,j=1,sum=0;
for(int q=1;q<=n;q++)
{
sum=i+j;//第一次f(-1)+f(0)
i=j;
j=sum;
}
return sum;
}
};
*/
class Solution {
public:
int climbStairs(int n) {
int i=0,j=0,sum=1;
for(int q=1;q<=n;q++)
{
i=j;
j=sum;
sum=i+j;
}
return sum;
}
};
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
方法一:三重循环 时间复杂度0(n^3)
方法二:双指针法 如下 时间复杂度O(n^2)
三个指针 我自己称第一个为绝对的位置从1~n 第二个指针是第一个指针的下一个 第三个指针是最后一个位置的。
在开始循环
之前我们做了四个事情
第一层循环 做了四件事情
第二层循环
不要重复项 要特别判断
)首先就是push_back,然后指针收缩 两个while判断当前值是否和前一个一样出现一些细节错误
当前值和上一个值比较的时候 要注意 i>0 易错 难找
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
int size = nums.size();
if (size < 3) return {}; // 特判
vector<vector<int> >res; // 保存结果(所有不重复的三元组)
sort(nums.begin(), nums.end());// 排序(默认递增)
for (int i = 0; i < size; i++) // 固定第一个数,转化为求两数之和
{
if (nums[i] > 0) return res; // 第一个数大于 0,后面都是递增正数,不可能相加为零了
// 去重:如果此数已经选取过,跳过
if (i > 0 && nums[i] == nums[i-1]) continue;//太精髓了
// 双指针在nums[i]后面的区间中寻找和为0-nums[i]的另外两个数
int left = i + 1;//第二个指针
int right = size - 1;//第三个指针 经典长度-1
while (left < right) //经典两个指针 while前面指针小于后面指针
{
if (nums[left] + nums[right] +nums[i]>0)
right--; // 三数之和太大,右指针左移
else if (nums[left] + nums[right] + nums[i]<0)
left++; // 两数之和太小,左指针右移
else
{
// 找到一个和为零的三元组,添加到结果中,左右指针内缩,继续寻找
res.push_back(vector<int>{nums[i], nums[left], nums[right]});
left++;//两边都收缩
right--;
// 去重:第二个数和第三个数也不重复选取
// 例如:[-4,1,1,1,2,3,3,3], i=0, left=1, right=5
//后面两个指针的的相同移动 一样的比较的是和之前的
while (left < right && nums[left] == nums[left-1]) left++;
while (left < right && nums[right] == nums[right+1]) right--;
}
}
}
return res;
}
};
注意 两个地方一直弄错
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;//初始的值 第一个为空 就好像第一个变成最后一个了。
ListNode* cur =head;
while(cur!=nullptr)//如果后面的值不为空 那就循环继续
{
ListNode* next= cur->next;//第一步 保存下一个 但是这边特殊 下一个是后面的变量的下一个 也可以理解吧 第一根线断掉
cur->next=pre;//第二步 更改指向
pre=cur;//第三部 更改前面的值
cur=next;//第四部 更新后面的值
}
return pre;
}
};
//
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==nullptr||head->next==nullptr)
{
return false;
}
ListNode* slow=head;
ListNode* quick=head->next;
while(quick!=nullptr&&quick->next!=nullptr)
{
if(quick==slow)
{
return true;
}
else
{
slow=slow->next;
quick=quick->next->next;
}
}
return false;
}
};
非常重要第一条:
我们常说第一个指向第三个 就是 1->next=3
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
简单的说就是四个一个整体 修改的是第二个和第三个值的位置交换(时间复杂度O(n) 空间复杂度为1)
循环前
换一个解释就是 这个变量对应的地址会变化 但是 我们需要知道创建的这个原始地址)
循环(循环条件就是temp的下一个值 和 下下个值不能为空 因为在循环体内需要操作这两个值
)(想着空 1 2 3)
想着空指向2)
//得到 空->2->1->3循环结束 返回temp1的next 特别注意
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* temp=new ListNode(0);//在我看来temp更像是枢纽 不是固定的值 他表示的是重复单元的第一个值 但是我需要开辟空间
temp->next=head;//
ListNode* temp1 = temp;//但是我们需要保存第一个值 因为这个值会改变 这个值才能等到 head
//这个的意思就是 4个重复单元 temp后面的两个是修改的量 如果temp后面没有了结束 不是偶数个 不需要修改
while(temp->next!=nullptr&&temp->next->next!=nullptr)
{
//第一步取出第二个值 1
ListNode* node1 = temp->next;//这边是地址 所以没有给指针开辟空间
//第二步取出第三个值 2
ListNode* node2 = temp->next->next;
//第三步 第一个指向第三个 空->2
temp->next=node2;
//第四部 第二个指向第四个 1->3
node1->next=node2->next;
//第五步 第三个指向第二个 2->1
node2->next=node1; //得到 空->2->1->3
//第六部
// node1 是第第一个值 但是他的位置就是后移2个的
temp=node1;
}
return temp1->next;//注意看
}
};
//0716修正写法 对比之前的快慢指针
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==nullptr||head->next==nullptr)
{
return nullptr;
}
ListNode* slow=head;
ListNode* quick=head;
while(quick!=nullptr&&quick->next!=nullptr)
{
slow=slow->next;
quick=quick->next->next;
if(quick==slow)
{
ListNode *ptr = head;
while (ptr != slow)
{
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr;
}
};
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while (fast != nullptr) {
slow = slow->next;
if (fast->next == nullptr) {
return nullptr;
}
fast = fast->next->next;
if (fast == slow) {
ListNode *ptr = head;
while (ptr != slow) {
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr;
}
};
快指针表示遍历数组到达的下标位置
,慢指针表示下一个不同元素要填入的下标位置
,初始时两个指针都指向下标 11。因为慢指针为放入的位置0不是
)注意我代码的第二种写法是不是更简单理解
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] != nums[fast - 1]) {
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
}
};
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()<2)
{
return nums.size();
}
int slow=1;
int fast=1;
while(fast<nums.size())
{
if (nums[fast]==nums[fast-1])
{
fast++;
}
else
{
nums[slow]=nums[fast];
slow++;
fast++;
}
}
return slow;
}
};
//第一种方法就是 创建一个新的动态数组 然后用 (i+k)%n 的方式确定下标 最后拷贝得旧的数组
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
vector<int> new_array(n);
for(int i=0;i<n;i++)
{
new_array[(i+k)%n]=nums[i];
}
nums.assign(new_array.begin(),new_array.end());
}
};
注意的是数组的翻转方式
class Solution {
public:
void reverse(vector<int>& nums, int start, int end) {
while (start < end) {
swap(nums[start], nums[end]);
start += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k) {
k %= nums.size();
reverse(nums, 0, nums.size() - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.size() - 1);
}
};
三个移动节点,其中一个是经典的枢纽
(开始需要拷贝一份初始地址需要返回)如何确定迭代条件呢 虽然在循环体内有->Next操作 但是在next没有新的操作了 所以那就本身不为空
)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* pre =new ListNode(-1);
ListNode* head=pre;
while(l1!=nullptr&&l2!=nullptr)
{
if(l1->val<l2->val)//每次取头节点进行比较
{
pre->next=l1;//头结点挂上
l1=l1->next;//更换头结点位置
pre=pre->next;//可以理解成新链表的最后一个节点 时刻保持我们操作的是最后一个节点
}
else
{
pre->next=l2;
l2=l2->next;//同理
pre=pre->next;//可以理解成新链表的最后一个节点 时刻保持我们操作的是最后一个节点
}
}
//这一步其实就是把剩余的东西拼凑上去
if(l2!=nullptr)
{
pre->next=l2;
}
else
{
pre->next=l1;
}
return head->next;
}
};
异常情况一定要放在前面
如果第一个下标为-1 那就一直放第二个注意这边 不要下标了 是值
)class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p1 = m-1;//数组一的下标
int p2 = n-1;//数组二的下标
int tail=m+n-1;//new的下标
//int cur;//可有可无
while(p1>=0||p2>=0)
{
if(p1==-1)
{
nums1[tail--]=nums2[p2--];
}
else if(p2==-1)
{
nums1[tail--] =nums1[p1--];
}
else if(nums1[p1]<nums2[p2])
{
nums1[tail--]=nums2[p2--];
}
else
{
nums1[tail--] =nums1[p1--];
}
}
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return {i, j};
}
}
}
return {};
}
};
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
for (int i=digits.size()-1;i>=0;i--)
{
digits[i]++;
if(digits[i]!=10)
{
return digits;
}
else
{
digits[i]=0;
}
}
vector<int> res(digits.size()+1);
res[0]=1;
return res;
}
};
欢迎大家相互学习指教。
心体:我可太难了
但是我们代码的思路是
:不断去便利这个字符串,不断地想把它放入新创建的栈中,但是在放入之前,我们总是做一件事情就是与栈顶的进行匹配 【】 {} 这种同时出现那就不放入并且删除栈顶元素 并且continue
。放入栈中是一直做的事情,所以不放在else 或者if里面,当if中1if满足就不做很巧妙用了continue
)class Solution {
public:
bool isValid(string s) {
stack<char> stack;
for(int i=0; i<s.length(); ++i)
{
char c = s[i];
if(!stack.empty())
{
char t = stack.top();
if(t=='(' && c==')' || t=='[' && c==']'|| t=='{' && c=='}')
{
stack.pop();
continue;
}
}
stack.push(c);
}
return stack.empty();
}
};
class Solution {
public:
bool isValid(string s) {
stack<char> stack;
for(int i=0;i<s.length();i++)
{
char c =s[i];
if (!stack.empty())
{
char t = stack.top();
if(t=='(' && c==')' || t=='[' && c==']'|| t=='{' && c=='}')
{
stack.pop();
continue;
}
else
{
stack.push(c);
}
}
else
{
stack.push(c);
}
}
return stack.empty();
}
};
class MinStack {
public:
stack<int> stack_1;
stack<int> stack_min;
/** initialize your data structure here. */
MinStack() {
stack_min.push(INT_MAX);
}
void push(int val) {
stack_1.push(val);
stack_min.push(min(stack_min.top(),val));//易错 这边 是stack_min
}
void pop() {
stack_1.pop();
stack_min.pop();
}
int top() {
return stack_1.top();
}
int getMin() {
return stack_min.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
这边左边界b和右边界a都是实际矩阵的左边和右边那个 计算也是a-b-1,当我们栈中遇到比前一个小的就说明该计算了,计算后酒吧这个去掉,因为我要保持时刻单调递增,这样子相邻的两个值(就说明左边的一直比我小 左边界就一直是确定了了,因为我是最大)右边界第一个比我小的就确定了
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();//求了下数量
vector<int> left(n), right(n, n);//定义了两个动态数组
stack<int> mono_stack;//定义了一个单调递增的栈 栈里面放的是下标
for (int i = 0; i < n; ++i)
{
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i])//如果栈不为空,或者栈顶元素最为下标比即将要放入的值大
{
right[mono_stack.top()] = i;//那我们就知道对应元素的右边界了
mono_stack.pop();//并且去掉栈中元素
}
left[i] = (mono_stack.empty() ? -1 : mono_stack.top());//正常情况每个元素的左边界都清楚
mono_stack.push(i);//栈中放入坐标
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
暴力求解:for循环 n*k的时间复杂度 大循环控制移动 小循环用于内部比较
简单的说就是双向栈解决问题,不断将新的数据从尾部添加(小的留下,大的也留但是要把栈比自己小的去掉),头部用来去除不在当前滑动窗口的数据(利用数据递增排序来实现)
代码中做了两个并列的for循环
为什么是递减呢,因为如果同一个窗口内你坐标比我小,值还比我小递增的那肯定不是取你,不存在递增的关系
)我想明白了一个点就是加入的这个值肯定是当前滑动窗口的值,如果它真的比之前的大把之前的都删掉也无所谓
。。)注意:就算走进了while循环也要也要进行这一步 大的值也要加进去
),举个例子i=3 k=3 i-k=0 这不就是边界么 小于等于这个删除 刚好符合第二个滑动窗口
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()])//如果队列不为空 加入的值比最后一个大 那就把队列最后一个去掉(这里实际上是存储下标)
{
q.pop_back();
}
q.push_back(i);//比他小就直接加进去 比他大也直接加进去
}
vector<int> ans = {nums[q.front()]};//创建一个存储返回值的动态数组
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()])
{
q.pop_back();
}
q.push_back(i);
while (q.front() <= i - k)//坐标在滑动窗口左侧 那就直接去掉
{
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};
class MyCircularDeque {
private:
vector<int> q;
public:
int K;
/** Initialize your data structure here. Set the size of the deque to be k. */
MyCircularDeque(int k) {
q.reserve(k);
K=k;
}
/** Adds an item at the front of Deque. Return true if the operation is successful. */
bool insertFront(int value) {
if(!isFull()){
q.insert(q.begin(),value);
return true;
}
return false;
}
/** Adds an item at the rear of Deque. Return true if the operation is successful. */
bool insertLast(int value) {
if(!isFull()){
q.push_back(value);
return true;
}
return false;
}
/** Deletes an item from the front of Deque. Return true if the operation is successful. */
bool deleteFront() {
if(!isEmpty()){
q.erase(q.begin());
return true;
}
return false;
}
/** Deletes an item from the rear of Deque. Return true if the operation is successful. */
bool deleteLast() {
if(!isEmpty()){
q.pop_back();
return true;
}
return false;
}
/** Get the front item from the deque. */
int getFront() {
if(isEmpty())return -1;
return q.front();
}
/** Get the last item from the deque. */
int getRear() {
if(isEmpty())return -1;
return q.back();
}
/** Checks whether the circular deque is empty or not. */
bool isEmpty() {
return q.size()==0;
}
/** Checks whether the circular deque is full or not. */
bool isFull() {
return q.size()>=K;
}
};
/**
* Your MyCircularDeque object will be instantiated and called as such:
* MyCircularDeque* obj = new MyCircularDeque(k);
* bool param_1 = obj->insertFront(value);
* bool param_2 = obj->insertLast(value);
* bool param_3 = obj->deleteFront();
* bool param_4 = obj->deleteLast();
* int param_5 = obj->getFront();
* int param_6 = obj->getRear();
* bool param_7 = obj->isEmpty();
* bool param_8 = obj->isFull();
*/
两个for循环 一个加一个减(减的时候还不断判断 ),还有一个就是在开始特判 特别注意
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.length()!=t.length())
{
return false;
}
vector<int> table(26,0);
for (int i=0;i<s.length();i++)
{
table[s[i]-'a']++;
}
for (int i=0;i<t.length();i++)
{
table[t[i]-'a']--;
if(table[t[i]-'a']<0)
{
return false;
}
}
return true;
}
};
我们这边排序后的字符串作为键,未排序的作为值
。以及这个存放类型unordered_map> mp;
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> mp;
for (int i=0;i<strs.size();i++)
{
string sort_new=strs[i];
sort(sort_new.begin(),sort_new.end());
mp[sort_new].emplace_back(strs[i]);
}
//这边用来构建返回值的问题
vector<vector<string>> ans;
//构建迭代器
unordered_map <string,vector<string>>::iterator it;
for (it = mp.begin(); it != mp.end(); ++it)
{
ans.emplace_back(it->second);
}
return ans;
}
};
目标减去现在放的值是不是已经在里面了
,如果在就准备返回了),map键值对的键是具体的数,值是对应的下标,因为下标是我们要返回的东西
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int,int> map_a;
vector<int> vector_b(2,-1);
for(int i=0;i<nums.size();i++)
{
if(map_a.find(target-nums[i])!=map_a.end())//这个键已经存在
{
vector_b[0]=map_a[target-nums[i]];//那我们就或许他的下标也就是值
vector_b[1]=i;
break;
}
else
{
map_a[nums[i]]=i;//这个键不存在 我同时插入键和值
}
}
return vector_b;
}
};
class Solution {
public:
void inorder(TreeNode* root, vector<int>& res) {
if (!root) {
return;
}
inorder(root->left, res);
res.push_back(root->val);
inorder(root->right, res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
};
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
while (root != nullptr || !stk.empty()) {
while (root != nullptr) {
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
res.push_back(root->val);
root = root->right;
}
return res;
}
};
方法三 颜色标记法,本质上也是维护栈来实现
它的思想是
我们需要理解的几个点
首先开始永远是把根放进去的,只要第一次放入栈都是白色的,第二个需要注意的是存在取出白色 放入红色 取出红色 输出的。看看注释和自己花的图
区别pair 和make_pair的区别(自动确定类型),类型是结构体类型
auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型
:
栈加入数据是push
在开始循环前
开始循环(栈不为空)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
**/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<pair<TreeNode*, int> > stk; //放入的这个类型 第一个是节点用来取值 第二个用来标记颜色
stk.push((make_pair(root, 0)));//一开始先把根放入 颜色是白色
while(!stk.empty())//如果栈不是空的就一直变量 知道全部处理好变灰色 然后取出来了
{
auto [node, type] = stk.top();//每次都取出看看
stk.pop();//取出记得删除
if(node == nullptr) continue;
if(type == 0)//如果是白色的 根据中序 先序后续放入
{
stk.push(make_pair(node->right, 0));
stk.push(make_pair(node, 1));
stk.push(make_pair(node->left, 0));
}
else result.emplace_back(node->val);//否则灰色的我就输出
}
return result;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
**/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<pair<TreeNode*, int> > stk; //放入的这个类型 第一个是节点用来取值 第二个用来标记颜色
stk.push((make_pair(root, 0)));//一开始先把根放入 颜色是白色
while(!stk.empty())//如果栈不是空的就一直变量 知道全部处理好变灰色 然后取出来了
{
auto [node, type] = stk.top();//每次都取出看看
stk.pop();//取出记得删除
if(node == nullptr) continue;
if(type == 0)//如果是白色的 根据中序 先序后续放入
{
stk.push(make_pair(node->right, 0));
stk.push(make_pair(node->left, 0));
stk.push(make_pair(node, 1));//当前这个节点已经是取出放入的 而且一直都是中间那一个
}
else result.emplace_back(node->val);//否则灰色的我就输出
}
return result;
}
};
//递归写法
class Solution {
public:
void preorder(TreeNode *root, vector<int> &res) {
if (root == nullptr) {
return;
}
res.push_back(root->val);
preorder(root->left, res);
preorder(root->right, res);
}
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
preorder(root, res);
return res;
}
};
自己尝试用颜色标记法 非常的快 一下子就好了 就是需要注意取出儿子放入的顺序 倒这来的
class Solution {
public:
// 后序遍历,首先最简单的做法是 记住前序 翻转就实现 (直接利用 1.前序遍历 2.翻转)
// 后插入
// 第一种:递归
vector<int> postorder1(Node* root) {
vector<int> ve;
if (!root) return ve;
recursivePreorder(root, ve);
return ve;
}
void recursivePreorder(Node *node, vector<int>& ve) {
if (!node) return;
for (int i=0; i < node->children.size(); i ++) {
Node *n = node->children[i];
if (n) recursivePreorder(n,ve);
}
ve.emplace_back(node->val);
}
// 第二种:迭代
vector<int> postorder(Node* root) {
vector<int> ve;
if (!root) return ve;
stack<Node*> st;
st.push(root);
while (!st.empty()) {
Node *node = st.top();
st.pop();
if (node) {
ve.emplace_back(node->val);
vector<Node*> chs = node->children;
if (!chs.empty()) {
int size = chs.size();
for (int i =0; i< size; i++) {
Node *n = chs[i];
if (n) st.push(n);
}
}
}
}
reverse(ve.begin(),ve.end());
return ve;
}
};
/*
// Definition for a Node.
class Node {
public:
int val;
vector children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<int> postorder(Node* root) {
vector<int> result;//用于接收返回结果
stack<pair<Node*,int>> stk;//
stk.push(make_pair(root, 0));//老规矩吧跟放入栈中
while(!stk.empty())
{
auto [node,type] =stk.top();
stk.pop();
if(node == nullptr) continue;
if(type == 0)//不管什么情况 就是改中间这边
{
stk.push(make_pair(node, 1));
vector<Node*> childrens=node->children;
for (int i=childrens.size()-1;i!=-1;i--)//注意这边的顺序
{
stk.push(make_pair(childrens[i], 0));
}
}
else
{
result.emplace_back(node->val);
}
}
return result;
}
};
/*
// Definition for a Node.
class Node {
public:
int val;
vector children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<int> preorder(Node* root) {
vector<int> result;//用于接收返回结果
stack<pair<Node*,int>> stk;//
stk.push(make_pair(root, 0));//老规矩吧跟放入栈中
while(!stk.empty())
{
auto [node,type] =stk.top();
stk.pop();
if(node == nullptr) continue;
if(type == 0)//不管什么情况 就是改中间这边
{
//vector childrens=node->children;
for (int i=node->children.size()-1;i!=-1;i--)//注意这边的顺序
{
stk.push(make_pair(node->children[i], 0));
}
stk.push(make_pair(node, 1));
}
else
{
result.emplace_back(node->val);
}
}
return result;
}
};
因为我们从根节点开始遍历树,然后向下搜索最接近根节点的节点,这是广度优先搜索。我们使用队列来进行广度优先搜索,队列具有先进先出的特性。
队列确实是解决这个问题的好方法,你想一个队列 模拟放入的场景 不就是按照顺序的 7-6-5-4-3-2-1,它这个代码巧妙的地方就是
在循环前
/*
// Definition for a Node.
class Node {
public:
int val;
vector children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<int> subans;//这个是用来存放放一层的数据 会随时清空
vector<vector<int>> ans;//这个是用来存放汇总的数据
queue<Node*> que;//广度优先 使用队列
vector<vector<int>> levelOrder(Node* root)
{
if(root ==NULL) return {};//特判 如果为空 直接返回
Node* p = root;//在循环开始前 总是把根节点加进去队列
que.push(p);
int flag =1;//初始标记 1 表示当前队列放入了一个
while(!que.empty())//如果队列不为空
{
p = que.front();//那我就每次从队列头部取出一个(最旧的元素 )
que.pop();//老规矩 取出 就要删除
subans.push_back(p->val);//取出就要放入数组内
/**不常见写法
for(auto c:p->children)//遍历他的孩子 孩子放入队列
{
que.push(c);
}
**/
for(int i=0;i<p->children.size();i++)
{
que.push(p->children[i]);
}
--flag; //用于记录当前层次的节点个数
if(flag == 0)
{
flag = que.size();//这边就是统计队列剩余的数量 也就是下一层的数目了
ans.push_back(subans);//把当前层的数组加入总结果
subans.clear();//一定要记得及时清空
}
}
return ans;
}
};
回朔实现
每次数量满足了,就执行一次去掉一个字母,如果函数结束了(三个if都执行完了也去掉一个 就存在一次性去掉很多的情况比如n=3自己看看第二个是怎么生成的)
注意第二种写法 不是引用 是拷贝。原因就好像 你进去函数前是(( 进去后是(()你退出函数 之前那个声明周期都结束了。所以你用的还是((
注意模板(四步走:正常情况 这样写全部输出 特殊点就是去除哪些不符合条件的加了if)
第一步就是结束条件:==2n
第二步就是当前层的逻辑:
第三部:进入下一层
第四部: 清理当前层 (第一种方法有 第二种方法没有
)
核心思想:=2n终止 左括号小于n就可以一直添加 如果右括号小于左括号就可以添加右括号
class Solution {
void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
if (cur.size() == n * 2) //结束条件
{
ans.push_back(cur);
return;
}
if (open < n) {
cur.push_back('(');//步二
backtrack(ans, cur, open + 1, close, n);//步三
cur.pop_back();//步四
}
if (close < open) {
cur.push_back(')');
backtrack(ans, cur, open, close + 1, n);
cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};
、
class Solution {
void backtrack(vector<string>& ans, string cur, int open, int close, int n) {
if (cur.size() == n * 2) //步一
{
ans.push_back(cur);
return;
}
if (open < n)
{
cur.push_back('(');//步二
backtrack(ans, cur, open + 1, close, n);//步三
//cur.pop_back();
}
if (close < open) {
cur.push_back(')');
backtrack(ans, cur, open, close + 1, n);
//cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};
结束条件(因为我们要做的是不断走进跟节点去交换他的左右节点)
当前层的逻辑操作(就是做了一个左右节点的交换)
就是进入下一层,把左右子树节点作为根节点传入
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr) {
return nullptr;
}
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root)
{
//终止条件
if (root == nullptr)
{
return nullptr;
}
//当前层的逻辑处理
TreeNode* temp=root->left;
root->left=root->right;
root->right=temp;
//进入下一层 分别进入左子树和右子树
invertTree(root->right);
invertTree(root->left);
return root;
}
};
/**
* 递归方式遍历反转
*/
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
/**
* 层序遍历方式反转
*/
public TreeNode invertTreeByQueue(TreeNode root) {
if (root == null) {
return null;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return root;
}
/**
* 深度优先遍历的方式反转
*/
private TreeNode invertTreeByStack(TreeNode root) {
if (root == null) {
return null;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
int size = stack.size();
for (int i = 0; i < size; i++) {
TreeNode cur = stack.pop();
TreeNode temp = cur.left;
cur.left = cur.right;
cur.right = temp;
if (cur.right != null) {
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
}
return root;
}
其实这个题一看到就想到了中序遍历,如果是递增的不就好了,看了题解 算是其中一种方法。开始我是把结果进行遍历比较,但是时间超时,后来修改成x
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
vector<int> result;
stack<pair<TreeNode*, int> > stk; //放入的这个类型 第一个是节点用来取值 第二个用来标记颜色
stk.push((make_pair(root, 0)));//一开始先把根放入 颜色是白色
while(!stk.empty())//如果栈不是空的就一直变量 知道全部处理好变灰色 然后取出来了
{
auto [node, type] = stk.top();//每次都取出看看
stk.pop();//取出记得删除
if(node == nullptr) continue;
if(type == 0)//如果是白色的 根据中序 先序后续放入
{
stk.push(make_pair(node->right, 0));
stk.push(make_pair(node, 1));
stk.push(make_pair(node->left, 0));
}
else
{
result.emplace_back(node->val);//否则灰色的我就输出
if(result.size()>=2)
{
if( result[result.size()-1]<=result[result.size()-2])
return false;
}
}
}
return true;
}
};
:他这个跟我想的不是很一样,我一直想把一个三角形作为一个部分进行一次比较,但是它不是,他就是把当前节点的值作为一个传入参数,传入左子树是放在大值那边,传入右子树是作为小值那边,对于另外一个参数用极大LONG_MAX和极小来表示LONG_MIN来表示 (非常巧妙)。
不过你想想也合理,我们对树进行递归的时候,不都是传入当前节点到(值或者数据到下个节点,也只能一边的比较也算合理)
另外一个需要注意的是,我们这个结果一个返回值,和之前的不一样,不再是把递归的方程放在return前面而是放在return上面,return调用了两次的函数非常的巧妙,值得注意。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
bool ret = helper(root,LONG_MIN,LONG_MAX);
return ret;
}
bool helper(TreeNode* root,long long lower, long long upper)
{
if (root==nullptr)//迭代的结束条件
{
return true;
}
//当前层的逻辑事件
if (root -> val <= lower || root -> val >= upper)
{
return false;
}
//进入到下一层 这个不像之前每一层都有获得一些东西 把函数调用放在return前面 这边只要一个return 对或者错 所以把函数调用放在return
return helper(root -> left, lower, root -> val) && helper(root -> right, root -> val, upper);
}
};
我怎么觉的这个更像是从最底层往上算的,函数一层一层往下铺开,但是计算结果从下网上一层一层传上来
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
class Solution {
public:
int maxDepth(TreeNode* root)
{
if (root == nullptr)
{
return 0; //迭代结束条件
}
//当前层的逻辑事件 是不是可以理解成 把下一层的高度中大的+1(刚好把进入下一层给做完了)
int ret = max(maxDepth(root->left), maxDepth(root->right))+1;
//因为我就要一个返回值,所以函数调用放在最下面
//返回结果是把当前层的高度返回给上一层
return ret;
}
};
//其他人的写法
class Solution {
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
int result1=1;
int result2=1;
result1+=maxDepth(root.left);
result2+=maxDepth(root.right);
if(result1>result2)
return result1;
else
return result2;
}
}
首先可以想到使用深度优先搜索的方法,遍历整棵树,记录最小深度。对于每一个非叶子节点,我们只需要分别计算其左右子树的最小叶子节点深度。这样就将一个大问题转化为了小问题,可以递归地解决该问题。
官方写法如下,同时我也把我的写法也放在下面了,更好理解
这个一定要区别最大深度,最大深度可以三步解决,最小深度不可以主要原因是(比如左子树为空 右字树很深 最大深度我们可以max 直接忽略深度为0的 但是最小深度直接min得到结果0 那是不行的(与定义不符合:根节点到最近叶结点的距离)。
)
所以这个题分成了很多种情况考虑非常重要
四步思考
步二:逻辑事件 就是返回当前层的最小高度(分成多种情况考虑)看上面,你要获得当前层的高度就要知道下一层的 所以返回值是min下一层+1 逻辑事件和进入下一层都考虑了
class Solution {
public:
int minDepth(TreeNode *root) {
if (root == nullptr) {
return 0;
}
if (root->left == nullptr && root->right == nullptr) {
return 1;
}
int min_depth = INT_MAX;
if (root->left != nullptr) {
min_depth = min(minDepth(root->left), min_depth);
}
if (root->right != nullptr) {
min_depth = min(minDepth(root->right), min_depth);
}
return min_depth + 1;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode *root) {
if (root == nullptr) {
return 0;
//你想想根节点传入的情况 是不是根节点都没有 不就是表示当前层为0
}
if (root->left == nullptr && root->right == nullptr)
//如果传入根节点,是不是左右节点都是空的 是不是表示当前层为1
{
return 1;
}
int min_depth = INT_MAX;
if (root->left != nullptr&&root->right==nullptr)//如果左节点为空,那就只能考虑右节点了 不能简单看做1,看看题目的例二
{
// min_depth = min(minDepth(root->left), min_depth);
min_depth= minDepth(root->left);
}
if (root->right != nullptr&&root->left==nullptr)//如果右节点为空,那就只能考虑左节点了
{
//min_depth = min(minDepth(root->right), min_depth);
min_depth=minDepth(root->right);
}
if(root->right!=nullptr&&root->left!=nullptr)
{
min_depth=min(minDepth(root->right),minDepth(root->left));
}
return min_depth + 1;
}
};
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
当前层的逻辑条件就是看看
主要看第一个版本跟我想的是最相近的,首先我们一定要明确深度优先的迭代,就深到浅(一定要明确 比如下面的图 先遍历左子树 再右子树顺序就是 3 5 6 2 7 4 1 0 8
)
当前层的逻辑就是
所以需要进入下一层,通过返回结果判断
)。是的话我就一层一层返回上去(这边通过设置了found标记为,如果在左子树找到了,右子树都不用判进去了,直接再次return
),补充
:就是真的很难想(就是你不断把p换成跟 推导上一层函数 它又发现子树就是是p 又把p换成跟实现了一种上传 。found是引用 全局唯一的 注意 。最后就变成当前层的函数传入的p和q是不是一样了)(你看他分的情况 基本上就是左子树不为空 右子树不为空 这两种情况 如果分成你空我不空应该也是可以的)/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* search(TreeNode* root,TreeNode*p,TreeNode*q,bool &found)
{
if (root->left==NULL&&root->right==NULL)//迭代终止条件
{
return NULL;
}
//当前层的逻辑条件就是
if(root->left!=NULL)
{
TreeNode* nleft=search(root->left,p,q,found);
if(found) return nleft;//注意看这边 他返回的就是上一层的结果 可能是上上层的结果 反正就是一层一层返回上来的
if(root->left->val==p->val) p->val=root->val;
if(root->left->val==q->val) q->val=root->val;
if(p->val==q->val){found=true;return root;}//注意看这边
}
if(root->right!=NULL)
{
TreeNode* nright=search(root->right,p,q,found);
if(found) return nright;
if(root->right->val==p->val) p->val=root->val;
if(root->right->val==q->val) q->val=root->val;
if(p->val==q->val){found=true;return root;}
}
return NULL;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
bool found=false;
return search(root,p,q,found);
}
};
//评论写法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root==p||root==q)//终止条件 就是找到了
{
return root;
}
if (root!=null)//跟节点不为空
{
TreeNode lNode=lowestCommonAncestor(root.left,p,q);//左节点传进去
TreeNode rNode=lowestCommonAncestor(root.right,p,q);//右节点传进去
if (lNode!=null&&rNode!=null)
return root;//表示找到了这个
else if(lNode==null)//左子树为空 右子树不为空 那就说明都在右子树
{//两个都在右子树
return rNode;
}
else { //两个都在左子树里面
return lNode;
}
}
return null;
}
}
//官方写法
class Solution {
public:
TreeNode* ans;
bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr) return false;
bool lson = dfs(root->left, p, q);
bool rson = dfs(root->right, p, q);
if ((lson && rson) || ((root->val == p->val || root->val == q->val) && (lson || rson))) {
ans = root;
}
return lson || rson || (root->val == p->val || root->val == q->val);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);
return ans;
}
};
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//这里是思考难点。定义规则:比如在某一棵子树上先找到了p,则无需继续遍历这棵子树,因为即使这棵子树有q,p也一定是q的祖先,也就是它们两个的最近公共祖先。
if(null == root || root.val == p.val || root.val == q.val) return root;
//按照上述规则,找到root的左子树的最近公共祖先。
TreeNode left = lowestCommonAncestor(root.left, p, q);
//按照上述规则,找到root的右子树的最近公共祖先。
TreeNode right = lowestCommonAncestor(root.right, p, q);
//一边找到了,一边没找到,根据上述规则,找到的就是最近公共祖先。
if(null == left) return right;
if(null == right) return left;
//如果在左右子树分别找到了p和q,则说明root是它们两个的最近公共祖先。
return root;
}
}
class Solution {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = index[preorder[preorder_root]];
// 先把根节点建立出来
TreeNode* root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造哈希映射,帮助我们快速定位根节点
for (int i = 0; i < n; ++i) {
index[inorder[i]] = i;
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
};
给你一个n和k 从1-n中抽取k个有几种取法
回溯做的(听网上说 递归+for+取出,)
终止条件 个数达到要求
当前层的逻辑事件就是向数组里加一个数
具体实现就是一个for循环 内部加数字的 一个进入下一层的 还有一个就是删除一个pop,好好理解这个删除,每次满了退到上一层删除一个 然后又加一个
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
dfs(n,k,1);
return Res;
}
vector<int> res;//这边的两个设为全局变量
vector<vector<int>> Res;
void dfs(int n, int k,int start)
{
if(res.size()==k)//终止条件
{
Res.push_back(res);
return;
}
for (int i = start;i<=n;i++)//
{
res.push_back(i);//添加数据
dfs(n,k,i+1);//注意第三个参数 就好像 1 那你只能从2 3 4后面选一个。好像2 你只能从 3 4中选择
res.pop_back();
}
}
};
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& used) {
// 此时说明找到了一组
if (path.size() == nums.size()) {
result.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++) {
if (used[i] == true) continue; // path里已经收录的元素,直接跳过
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = false;
}
}
vector<vector<int>> permute(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
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;
}
};
class Solution {
public:
double quickMul(double x, long long N) {
if (N == 0)//终止条件 等于1或许更好理解 可以改成1 一样的结果(下面的return 改成x)
{
return 1.0;
}
double y = quickMul(x, N / 2);//当前层的逻辑 获取上一层的结果(并且进入上一层了)
if (N % 2==0) //合并 偶数合并
{
return y*y;
}
else//奇数都乘x
{
return y*y*x;
}
}
double myPow(double x, int n) {
long long N = n;
if(N>0)//技术处理 如果n是负数的情况
{
double res = quickMul(x, N) ;
return res;
}
else
{
double res = 1.0/quickMul(x, -N);
return res;
}
}
};
删除这层放入的
)(模拟一下 一开始放入1 2 3然后立马执行进入dfs 判断发现满了 退出这个dfs 就走到了pop删除 把3删除了。然后走第二个dfs发现不放入,1 2 这个函数走完了,退出上一层也结束了(第二个dfs所以退出到底了),再推到上一层(回到第一个dfs),开始删除2了
)所以是删除当前层
class Solution {
public:
vector<int> t;//全局变量
vector<vector<int>> ans;//全局变量
void dfs(int cur, vector<int>& nums) {
if (cur == nums.size())//终止条件 当前位置等于总长度
{
ans.push_back(t);//cur 表示的是当前位置
return;
}
//选择考虑当前位置
t.push_back(nums[cur]);
dfs(cur + 1, nums);//进入下一层(下一个位置)
t.pop_back();//什么时候删除,走出来函数就删除
//考虑不走当前位置
dfs(cur + 1, nums);
}
vector<vector<int>> subsets(vector<int>& nums) {
dfs(0, nums);
return ans;
}
};
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int, int> counts;
int majority = 0, cnt = 0;
for (int num: nums) {
++counts[num];
if (counts[num] > cnt) {
majority = num;
cnt = counts[num];
}
}
return majority;
}
};
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
这边通过子结果来判断确定当前层的结果,这不就是分治要的么
)class Solution {
int count_in_range(vector<int>& nums, int target, int l, int r)
//辅助函数,用来统计一个数在一个序列中出现的次数
{
int count = 0;
for (int i = l; i <= r; ++i)//注意这边是《=
if (nums[i] == target)
++count;
return count;
}
int helper(vector<int>& nums, int l, int r) {
if (l == r)//迭代结束条件
return nums[l];
int mid = (l + r) / 2;//逻辑事件
int left_majority = helper(nums, l, mid);
int right_majority = helper(nums, mid + 1, r);
//memgr 汇总子结果
//如果子序列众数相同 直接返回
if (left_majority==right_majority) return left_majority;
//否则谁在本序列多 谁就是本序列的众数 注意这边的l和r边界
int left_count = count_in_range(nums, left_majority, l, r);
int right_count = count_in_range(nums, right_majority, l, r) ;
return left_count>right_count?left_majority:right_majority;
}
public:
int majorityElement(vector<int>& nums) {
return helper(nums, 0, nums.size() - 1);
}
};
不能是两个if 而是一个for循环,理解一下这边,本质上是一样的。还有全排列都是一样的
cclass Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> combinations;//最终返回的结果集
if (digits.empty()) //特判
{
return combinations;
}
unordered_map<char, string> phoneMap//构建一hash树
{
{'2', "abc"},
{'3', "def"},
{'4', "ghi"},
{'5', "jkl"},
{'6', "mno"},
{'7', "pqrs"},
{'8', "tuv"},
{'9', "wxyz"}
};
string combination;//一维的数据结果集
//参数一是结果集 参数二是树 参数三是题目的数据集 参数四是第几个放入位置了 参数五是 一维数据结果集
backtrack(combinations, phoneMap, digits, 0, combination);
return combinations;
}
//注意这边传入的都是引用 对同一个对象进行操作 所以每次都要pop回溯
void backtrack(vector<string>& combinations, const unordered_map<char, string>& phoneMap, const string& digits, int index, string& combination)
{
if (index == digits.length()) //如果当前放入的位置=长度(从0开始)放入 直接return 结束就好
{
combinations.push_back(combination);
return ;
}
//这两步就是为了下面for循环服务
char digit = digits[index];//取出数字
const string& letters = phoneMap.at(digit);//取出英文
for (int i=0;i<letters.size();i++)//遍历英文
{
combination.push_back(letters[i]);//经典 放入 函数 删除
backtrack(combinations, phoneMap, digits, index + 1, combination);//位置记得+1
combination.pop_back();
}
}
};
参考链接
视频参考
- 回溯算法其实就是暴力搜索
class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到***的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
if (row == n) {
result.push_back(chessboard);
return;
}
for (int col = 0; col < n; col++) {
if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
chessboard[row][col] = 'Q'; // 放置皇后
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.'; // 回溯,撤销皇后
}
}
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
int count = 0;
// 检查列
for (int i = 0; i < row; i++) { // 这是一个剪枝
if (chessboard[i][col] == 'Q') {
return false;
}
}
// 检查 45度角是否有皇后
for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查 135度角是否有皇后
for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
public:
vector<vector<string>> solveNQueens(int n) {
result.clear();
std::vector<std::string> chessboard(n, std::string(n, '.'));
backtracking(n, 0, chessboard);
return result;
}
};
同时可以看看我修改的flat的版本,区别在于 第一个版本是for循环结束吧一维答案放入二维 第二个版本是在标志位识别到了。全局 和局部变量
//代码随想录写法
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
queue<TreeNode*> que;//创建了一个队列存放节点
if (root != NULL)//初始把跟节点放入
{
que.push(root);
}
vector<vector<int>> result;//存放一个放返回值的
while (!que.empty())//队列不为空
{
int size = que.size();//统计队列的大小
//这边创建的是临时变量 就不用清空了 每一次执行for循环之前
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++)
{
TreeNode* node = que.front();//取出
que.pop();//删除
vec.push_back(node->val);//把值放入
if (node->left) que.push(node->left);//把左节点放入
if (node->right) que.push(node->right);//把右节点放入
}
result.push_back(vec);
}
return result;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> ret;
queue<TreeNode*> que;
vector<int> vec;
int flat=1;
if(root!=NULL)
{
que.push(root);
}
while(!que.empty())
{
TreeNode* node = que.front();//每次取出来一个
que.pop();
vec.push_back(node->val);//把值放入
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
flat--;
if(flat==0)
{
ret.push_back(vec);//二维存入一维
flat=que.size();
vec.clear();
}
}
return ret;
}
};
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
// 将vector转成unordered_set,提高查询速度
unordered_set<string> wordSet(bank.begin(), bank.end());
// 如果end没有在wordSet出现,直接返回0
if (wordSet.find(end) == wordSet.end()) return -1;
// 记录word是否访问过
unordered_map<string, int> visitMap; //
// 初始化队列
queue<string> que;
que.push(start);
// 初始化visitMap
visitMap.insert(pair<string, int>(start, 0));
//下标的写法不知道可不可以
char replaces[4] = {'A', 'C', 'G', 'T'};
while(!que.empty()) {
string word = que.front();
que.pop();
int path = visitMap[word]; // 这个word的路径长度
for (int i = 0; i < word.size(); i++) {
string newWord = word; // 用一个新单词替换word,因为每次置换一个字母
for (int j = 0 ; j < 4; j++) {
newWord[i] = replaces[j];
if (newWord == end) return path + 1; // 找到了end,返回path+1
// wordSet出现了newWord,并且newWord没有被访问过
if (wordSet.find(newWord) != wordSet.end()
&& visitMap.find(newWord) == visitMap.end()) {
// 添加访问信息
visitMap.insert(pair<string, int>(newWord, path + 1));
que.push(newWord);
}
}
}
}
return -1;
}
};
void back(参数)
{
if(终止结果)
存放结果
return
for(选择 本层集合中的元素)
{
处理节点;一般不就是放入:
back()递归
回溯 pop 撤销
}
}
class Solution {
void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
if (cur.size() == n * 2) //结束条件
{
ans.push_back(cur);
return;
}
if (open < n) {
cur.push_back('(');//步二
backtrack(ans, cur, open + 1, close, n);//步三
cur.pop_back();//步四
}
if (close < open) {
cur.push_back(')');
backtrack(ans, cur, open, close + 1, n);
cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};
这个体和之前的没有任何区别,多一个判断,很常见的方法多哦一个变量 int maxValue = INT_MIN;
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);//老样子 初始把值放入
vector<int> result;//这次不需要二维了
while (!que.empty())
{
int size = que.size();
int maxValue = INT_MIN; // 取每一层的最大值 这个也是临时变量 每次for循环之前都要初始化
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if(node->val>maxValue)
{
maxValue=node->val;
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(maxValue); // 把最大值放进数组
}
return result;
}
};
判断点与点之间的关系,要自己判断是不是差一个字符,如果差一个字符,那就是有链接。
然后就是求起点和终点的最短路径长度,这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径
。因为广搜就是以起点中心向四周扩散的搜索。这边我说说自己的想法:第一层只有一个单词,第二层是修改了一个字母的单词,第三层再修改了一个字母的单词,如果深搜右边的路径比左边的多很不对,如果是广搜,第几层就是第几步(你想想我们广搜用队列是不是一层一层的处理的,在那一层不是很容易知道)广搜的一般步骤,我自己写的
看看我写伪代码,有一个不同就是size的含义变了,之前表示一层的数量,现在一个单词就是单独的一次,因为有map记录他的路径(第几层),不怕他乱掉了
在开始可能初始一些数组(比较常见的层次遍历就是练两个数组) 或者 map(这边就是map);
首先就是把根节点放入队列;
一个while(队列不为空)
{
每次取出一个;
删除这个;
//如果是树层次遍历;
我们常常这边统计size(这个size就是一层的数量呗)
通过size构建for循环
{
进行操作
}
}
//我们这题的伪代码
1 创建了一个unordered_set 存放wordlist(方便后期查询在不在这边)
2 进行特判,wordSet.find(endWord) == wordSet.end()。如果最终要的单词不在这边那就直接结束
3 unordered_map<string, int> visitMap;创建map用来存储修改的单词和路径,同时可以用来判断走没走过
4 初始化队列 que
5 队列放入初始的beginword 根
6 把beginword 和 路径长度1插入 map中
7 while(队列非空)
8 取出队列中的一个
9 删除队列最前面的那个
10 通过map输出这个单词的路径方便后面通过他的改造都+1
11 两个for循环 第一次for循环遍历的是单词的起始位到尾巴,第二个循环是字母a到z的选择(这次这个size不用写在for之前,因为它不会变化了,但是之前的会 需要注意)
12 把修改的字母判断是不是结果是的化直接返回path+1
13 判断这个单词是不是在set中,出现了,并且没出现在map中(新单词并且是在字典里面)(同时满足,那就添加到map中并且路径+1 )
14 队列中添加这个单词
15 特别注意这个,如果全部便利完了都没找到 while退出记得返回0
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
// 将vector转成unordered_set,提高查询速度
unordered_set<string> wordSet(wordList.begin(), wordList.end());
// 如果endWord没有在wordSet出现,直接返回0
if (wordSet.find(endWord) == wordSet.end()) return 0;
// 记录word是否访问过
unordered_map<string, int> visitMap; //
// 初始化队列
queue<string> que;
que.push(beginWord);
// 初始化visitMap
visitMap.insert(pair<string, int>(beginWord, 1));
//下标的写法不知道可不可以
while(!que.empty()) {
string word = que.front();
que.pop();
int path = visitMap[word]; // 这个word的路径长度
for (int i = 0; i < word.size(); i++) {
string newWord = word; // 用一个新单词替换word,因为每次置换一个字母
for (int j = 0 ; j < 26; j++) {
newWord[i] = j + 'a';
if (newWord == endWord) return path + 1; // 找到了end,返回path+1
// wordSet出现了newWord,并且newWord没有被访问过
if (wordSet.find(newWord) != wordSet.end()
&& visitMap.find(newWord) == visitMap.end()) {
// 添加访问信息
visitMap.insert(pair<string, int>(newWord, path + 1));
que.push(newWord);
}
}
}
}
return 0;
}
};
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class Solution {
public:
vector<vector<string>> findLadders(string beginWord, string endWord, vector<string> &wordList) {
vector<vector<string>> res;
// 因为需要快速判断扩展出的单词是否在 wordList 里,因此需要将 wordList 存入哈希表,这里命名为「字典」
unordered_set<string> dict = {wordList.begin(), wordList.end()};
// 修改以后看一下,如果根本就不在 dict 里面,跳过
if (dict.find(endWord) == dict.end()) {
return res;
}
// 特殊用例处理
dict.erase(beginWord);
// 第 1 步:广度优先遍历建图
// 记录扩展出的单词是在第几次扩展的时候得到的,key:单词,value:在广度优先遍历的第几层
unordered_map<string, int> steps = {{beginWord, 0}};
// 记录了单词是从哪些单词扩展而来,key:单词,value:单词列表,这些单词可以变换到 key ,它们是一对多关系
unordered_map<string, set<string>> from = {{beginWord, {}}};
int step = 0;
bool found = false;
queue<string> q = queue<string>{{beginWord}};
int wordLen = beginWord.length();
while (!q.empty()) {
step++;
int size = q.size();
for (int i = 0; i < size; i++) {
const string currWord = move(q.front());
string nextWord = currWord;
q.pop();
// 将每一位替换成 26 个小写英文字母
for (int j = 0; j < wordLen; ++j) {
const char origin = nextWord[j];
for (char c = 'a'; c <= 'z'; ++c) {
nextWord[j] = c;
if (steps[nextWord] == step) {
from[nextWord].insert(currWord);
}
if (dict.find(nextWord) == dict.end()) {
continue;
}
// 如果从一个单词扩展出来的单词以前遍历过,距离一定更远,为了避免搜索到已经遍历到,且距离更远的单词,需要将它从 dict 中删除
dict.erase(nextWord);
// 这一层扩展出的单词进入队列
q.push(nextWord);
// 记录 nextWord 从 currWord 而来
from[nextWord].insert(currWord);
// 记录 nextWord 的 step
steps[nextWord] = step;
if (nextWord == endWord) {
found = true;
}
}
nextWord[j] = origin;
}
}
if (found) {
break;
}
}
// 第 2 步:深度优先遍历找到所有解,从 endWord 恢复到 beginWord ,所以每次尝试操作 path 列表的头部
if (found) {
vector<string> Path = {endWord};
dfs(res, endWord, from, Path);
}
return res;
}
void dfs(vector<vector<string>> &res, const string &Node, unordered_map<string, set<string>> &from,
vector<string> &path) {
if (from[Node].empty()) {
res.push_back({path.rbegin(), path.rend()});
return;
}
for (const string &Parent: from[Node]) {
path.push_back(Parent);
dfs(res, Parent, from, path);
path.pop_back();
}
}
};
对比一下二叉树深度优先进入左右
)class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if (!nr) return 0;
int nc = grid[0].size();
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
++num_islands;
dfs(grid, r, c);
}
}
}
return num_islands;
}
bool inArea(vector<vector<char>>& grid,int r,int c)
{
return 0 <= r && r < grid.size() && 0 <= c && c < grid[0].size();
}
void dfs(vector<vector<char>>& grid, int r, int c)
{
if (!inArea(grid,r,c))//如果到达边界直接返回
{
return;
}
if(grid[r][c] != '1')//表示是海或者标记过了 注意这边 设置为2 就是为了防止一直转圈圈
{
return;
}
grid[r][c]=2;//把格子标记位已遍历过的 当前层的逻辑 就是
// 访问上、下、左、右四个相邻结点
dfs(grid, r - 1, c);
dfs(grid, r + 1, c);
dfs(grid, r, c - 1);
dfs(grid, r, c + 1);
}
};
class Solution {
private:
void dfs(vector<vector<char>>& grid, int r, int c) {
int nr = grid.size();
int nc = grid[0].size();
grid[r][c] = '0';
if (r - 1 >= 0 && grid[r-1][c] == '1') dfs(grid, r - 1, c);
if (r + 1 < nr && grid[r+1][c] == '1') dfs(grid, r + 1, c);
if (c - 1 >= 0 && grid[r][c-1] == '1') dfs(grid, r, c - 1);
if (c + 1 < nc && grid[r][c+1] == '1') dfs(grid, r, c + 1);
}
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if (!nr) return 0;
int nc = grid[0].size();
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
++num_islands;
dfs(grid, r, c);
}
}
}
return num_islands;
}
};
时间复杂度和空间复杂度
,两次排序都是nlogn 然后便利是n 相加取大的,那就是nlogn// 时间复杂度:O(nlogn)
// 空间复杂度:O(1)
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int index = s.size() - 1; // 饼干数组的下表
int result = 0;
//这边遍历的是孩子,其实饼干没必要走完,假设最大的饼干没有一个符合,那你有必要找次小的遍历么
for (int i = g.size() - 1; i >= 0; i--)
{
if (index >= 0 && s[index] >= g[i])//如果饼干比胃口大并且饼干没遍历完 就是把饼干分配完了 这边 要注意
{
result++;//符合+1
index--;//饼干前移
}
}
return result;
}
};
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans = 0;
int n = prices.size();
//这个就是不断对比今天比昨天涨了还是亏了 如果涨了,哪怕昨天卖掉了,那我就当作买回来,今天卖(这个差价相加就等于第一天买 第三天卖)
for (int i = 1; i < n; ++i)
{
ans += max(0, prices[i] - prices[i - 1]);
}
return ans;
}
};
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0, ten = 0;
for (auto& bill: bills) {
if (bill == 5) {
five++;
} else if (bill == 10) {
if (five == 0) {
return false;
}
five--;
ten++;
} else {
if (five > 0 && ten > 0) {
five--;
ten--;
} else if (five >= 3) {
five -= 3;
} else {
return false;
}
}
}
return true;
}
};
class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
int dir_x[4] = {0, 1, 0, -1};//这边分别表示 北 东 南 西 方向 走一步坐标变化
int dir_y[4] = {1, 0, -1, 0};
int x = 0;
int y = 0;
int status = 0;
int max_distance = 0;
//这边还是有点讲究的,你用map存入两个值怎么比较呢 应该也可以 但是吧pair看做一个值用set就简单了
set<pair<int, int>> obstacleSet;
for (vector<int> obstacle: obstacles)//把障碍放在hash里面
obstacleSet.insert(make_pair(obstacle[0], obstacle[1]));
for (int i;i<commands.size();i++)//遍历每一个指令
{
if (commands[i] == -2)//调整方向
status = (status + 3) % 4;
else if (commands[i] == -1)//调整方向
status = (status + 1) % 4;
else //只走 修改坐标
{
for (int k = 0; k < commands[i]; ++k)//一步一步走
{
int next_x = x + dir_x[status];//根据状态选择 x 是+1-1还是不变
int nexr_y = y + dir_y[status];//根据状态选择 y 是+1-1还是不变
//判断有没有遇到障碍
if (obstacleSet.find(make_pair(next_x, nexr_y)) == obstacleSet.end())
{
x = next_x;
y = nexr_y;
//不断比对最大距离
max_distance = max(max_distance, x*x + y*y);
}
}
}
}
return max_distance;
}
};
那么这个问题就转化为跳跃覆盖范围究竟可不可以覆盖到终点!
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点,但是有一个很重要的:就是在代码中你要判断当前的最大距离能不能支持走到下一个点,毕竟可以走0。
class Solution {
public:
bool canJump(vector<int>& nums) {
int size = nums.size() ;
if (size == 1) return true; // 只有一个元素,就是能达到
int max_instance=0;
for (int i = 0; i <= size; i++)
{ // 注意这里是小于等于cover
if(i<=max_instance)
{
max_instance = max(i + nums[i], max_instance);
if (max_instance >= nums.size() - 1) return true; // 说明可以覆盖到终点了
}
}
return false;
}
};
int jump(vector<int>& nums)
{
int ans = 0;
int end = 0;
int maxPos = 0;
for (int i = 0; i < nums.size() - 1; i++)
{
maxPos = max(nums[i] + i, maxPos);
if (i == end)
{
end = maxPos;
ans++;
}
}
return ans;
}
int jump(vector<int> &nums)
{
int ans = 0;
int start = 0;
int end = 1;
while (end < nums.size())
{
int maxPos = 0;
for (int i = start; i < end; i++)
{
// 能跳到最远的距离
maxPos = max(maxPos, i + nums[i]);
}
start = end; // 下一次起跳点范围开始的格子
end = maxPos + 1; // 下一次起跳点范围结束的格子
ans++; // 跳跃次数
}
return ans;
}
作者:ikaruga
链接:https://leetcode-cn.com/problems/jump-game-ii/solution/45-by-ikaruga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。