洛谷熟悉语法(C++)
数组
过河卒问题
该题链接如下https://www.luogu.com.cn/problem/P1002,是一道很基础的动态规划题,观察题目要求,内存限制125MB,而时间限制为1.00s。
思路一
使用递归,即
//https://www.luogu.com.cn/problem/P1002
//看题解思路:使用递归(x) 使用递归复杂度约是2^(n+m) 必爆掉
#include<bits/stdc++.h>
using namespace std;
long long int path_sum(int x,int y,int h_x,int h_y);
bool is_horse(int x,int y,int h_x,int h_y);
int main(){
int p_x=0,p_y=0,h_x=0,h_y=0;
cin >> p_x >> p_y >> h_x >> h_y;
// cout << p_x << p_y << h_x << h_y;
cout << path_sum(p_x,p_y,h_x,h_y);
return 0;
}
long long int path_sum(int x,int y,int h_x,int h_y){
if( is_horse(x,y,h_x,h_y)){
return 0;
}else if(x==0 || y==0){
return 1;
}else{
return path_sum(x-1,y,h_x,h_y) + path_sum(x,y-1,h_x,h_y);
}
}
bool is_horse(int x,int y,int h_x,int h_y){
int abx = ((x - h_x) < 0) ? (h_x - x) : (x - h_x);
int aby = ((y - h_y) < 0) ? (h_y - y) : (y - h_y);
if(abx == 1){
return (aby==2) ? true : false;
}else if(abx == 2){
return (aby==1) ? true : false;
}
return (abx == 0) && (aby == 0) ;
}
这样提交上去会有TLE,简单看其复杂度,为
思路二
使用动态规划,然鹅动态规划基本不会了,参考博客https://zhuanlan.zhihu.com/p/365698607,手动感谢大佬orz。
简单来说,动态规划采用自底向上的方法,避免了递归时大量重复计算的问题(本题中,可以想象,
一个朴素的想法就是:自底向上,把先计算的计算结果存起来,利用递推关系,就可以很快的计算出最终结果。使用空间换取时间。
本题,可以建一个二维数组,从
//https://www.luogu.com.cn/problem/P1002
#include<bits/stdc++.h>
using namespace std;
#define N 25
bool is_horse(int x ,int y,int h_x,int h_y);
int main(){
long long int m[N][N];
int p_x=0,p_y=0,h_x=0,h_y=0;
cin >> p_x >> p_y >> h_x >> h_y;
//初始化
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
m[i][j] = 0;
}
}
m[0][0] = 1;
//计算
for(int i=0;i<=p_x;i++){
for(int j=0;j<=p_y;j++){
if(is_horse(i,j,h_x,h_y)){
m[i][j] = 0;
continue;
}
if(i==0){
if(j==0) continue;
m[i][j] = m[i][j - 1];
continue;
}
if(j==0){
m[i][j] = m[i-1][j];
continue;
}
m[i][j] = m[i-1][j] + m[i][j-1];
}
}
for(int i=0;i<=p_x;i++){
for(int j =0;j<=p_y;j++){
cout << m[i][j] << " ";
}
cout << "\n";
}
cout << m[p_x][p_y];
return 0;
}
bool is_horse(int x,int y,int h_x,int h_y){
int abx = ((x - h_x) < 0) ? (h_x - x) : (x - h_x);
int aby = ((y - h_y) < 0) ? (h_y - y) : (y - h_y);
if(abx == 1){
return (aby==2) ? true : false;
}else if(abx == 2){
return (aby==1) ? true : false;
}
return (abx == 0) && (aby == 0) ;
}
运行结果如下

解决!
求最小子数组问题
题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/description/
这一题刚开始理解错了,以为随机选n个来组成数组,但实际是子数组(类似字符子串)。因此不用排序就可以解决。最容易想到的就是暴力两个for循环这里就不再放了。简单说下滑动窗口叭。
滑动窗口解法
个人理解,滑动窗口问题最重要的是三步走:
- 判断起始位置怎么动(这代表了窗口的滑动)
- 判断终止位置怎么动(这也代表了窗口的滑动。ps: 也就是一个管着增大,一个管着减小)
- 何时更新滑动窗口大小
这一题里,终止位置(代码中的 )一直叠加就好了(即在刚开始时,窗口一直增大,直到触发要更新滑动窗口大小的条件)。
更新滑动窗口大小的条件也很简单,就是 ;
起始位置要随着滑动窗口大小更新时随着更新,也就是滑动窗口减小(即 向右移动),注意:要同时更新sum。
代码如下:
#include<iostream>
// #include<algorithm>
#include<vector>
using namespace std;
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int subLen = nums.size()+1;
int sum = 0;
int i=0;
for(int j=0;j<nums.size();j++){
sum += nums[j];
while(sum >= target){
int subl = j - i + 1;
if(subl < subLen) subLen = subl;
sum -= nums[i++];
}
}
return (subLen == nums.size()+1) ? 0 : subLen;
}
};
int main(){
vector<int> nums = {12,28,83,4,25,26,25,2,25,25,25,12};
Solution s;
cout << s.minSubArrayLen(213,nums);
return 0;
}
更多的东西,代码随想录中动画很好理解,参见:https://www.programmercarl.com/0209.长度最小的子数组.html
链表
删除倒数第N个节点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
很容易想到是快慢指针,但是刚开始有几个点没想明白。
思路一
- 快慢指针写法:我是使用
计数,这就会出现慢指针何时等于head的问题,这里应该是n,即让慢指针指向要删除节点的前一个。 - 慢指针为空时,代表要删除head
代码如下
#include<iostream>
struct ListNode{
int val;
ListNode* next;
ListNode():val(0),next(nullptr){};
ListNode(int x):val(x),next(nullptr){};
ListNode(int x,ListNode* p):val(x),next(p){};
};
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode*p=head;
ListNode*p_n = nullptr;
for(int i=0;p!=nullptr;i++){
p = p->next;
if(i == n){
p_n = head;
}else if(i > n){
p_n = p_n->next;
}
}
if(p_n == nullptr){
return head->next;
}else{
ListNode* tmp = p_n->next;
p_n->next = p_n->next->next;
delete tmp;
return head;
}
}
};
int main(){
int n = 5;
ListNode* head,*p;
ListNode* tmp;
for(int i=0;i<n;i++){
tmp = new ListNode(i);
if(i==0){
head = tmp;
p = head;
continue;
}
p->next = tmp;
p = tmp;
}
for(p=head;p!=nullptr;p=p->next){
std::cout << p->val << "\t";
}
Solution s;
ListNode* r = s.removeNthFromEnd(head,1);
for(p=head;p!=nullptr;p=p->next){
std::cout << p->val << "\t";
}
return 0;
}
思路二
直接定义快慢指针指向head。遵循如下两步:
- 快指针先跑n,若发现中间快指针指向nullptr,则代表要删除头节点或者错误处理(题中保证有结果,则不用判断
是否小于n-1,即不用错误处理) - 后面快指针和慢指针一起跑,直到快指针到链表尾部
代码如下
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* pFast = head;
ListNode* pSlow = head;
for (int i=0; i<n; i++){
if (pFast->next == nullptr) return head->next;
pFast = pFast->next;
}
while(pFast->next){
pFast = pFast->next;
pSlow = pSlow->next;
}
pSlow->next = pSlow->next->next;
return head;
}
};
链表相交
题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/
思路一
很容易想到暴力解法,就是两层循环遍历。代码如下。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// 暴力写法
ListNode* tempA=headA;
ListNode* tempB=headB;
while(tempA != nullptr){
tempB = headB;
while(tempB != nullptr){
if(tempB == tempA){
return tempA;
}
tempB = tempB->next;
}
tempA = tempA->next;
}
return nullptr;
}
};
复杂度为
思路二
相交的话不可避免要遍历,但是怎么避免两层循环遍历是关键。
朴素想法:要是有类似快慢指针的路子,可以让两个指针分别指向两个链,然后两个指针一起走,时间复杂度就会降下来
问题关键:长度不一,不好指针一起走
解决办法:长的先走长出来的那部分,因为长出的那部分一定不会相交
复杂度这样就将为了
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int len_A = 0,len_B=0;
ListNode* tempA=headA;
ListNode* tempB=headB;
while(tempA!=nullptr){
tempA = tempA->next;
len_A++;
}
while(tempB!=nullptr){
tempB = tempB->next;
len_B++;
}
tempA = headA;
tempB = headB;
if(len_A > len_B){
for(int i=len_B;i<len_A;i++){
tempA = tempA->next;
}
}else{
for(int i=len_A;i<len_B;i++){
tempB = tempB->next;
}
}
while(tempA != nullptr){
if(tempA == tempB){
return tempA;
}
tempA=tempA->next;
tempB=tempB->next;
}
return nullptr;
}
};
链表成环判断
原题出处:https://leetcode.cn/problems/linked-list-cycle-ii/
思路一
一个数组,存储过所有见过的节点指针,当指针重复时,返回
class Solution{
public:
ListNode* deletCycle(ListNode* head){
unordered_set<ListNode *> visited;
while(head != nullptr){
if(visited.count(head)){
return head;
}else{
visited.insert(head);
}
head = head->next;
}
return nullptr;
}
};
这里用到了STL库中的<unordered_set>关联容器
通过hash索引,最好查找情况o(1),最坏情况时o(n)
这种方法实现简单,但是需要较高的空间复杂度(o(n))
思路二
快慢指针,利用的时路程关系,详细推导见题解,再赘述并无太大意思,只需注意理解一点:
- 在慢指针进入环的第一圈内,快指针一定会和慢指针相遇。因为当慢指针进入环时,快指针的追击距离小于一圈,而快指针速度是慢指针的两倍(即慢指针再走一个追击距离就会被追上,而此距离小于一圈)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head==nullptr){
return nullptr;
}
ListNode* fast = head;
ListNode* slow = head;
do{
fast = fast->next;
slow = slow->next;
if(fast != nullptr){
fast = fast->next;
}else{
return nullptr;
}
}while(fast!=nullptr && fast!=slow);
ListNode* ptr = head;
while(ptr!=slow){
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
};
三数之和
力扣原题:https://leetcode.cn/problems/3sum/
用双指针解法(左右指针),也可以叫三指针
基本思路
- 排序
- 一个指针i负责遍历
- 一个left指针和一个right指针分别指向:
i+1
和数组末尾
- 若三个指针所指数和小于0,则right--;小于0则left++;等于零则更新right和left,添加结果集;每一轮的结束条件是:left>=right
有几个需要注意的点:
- i指针:[-1,-1,-1,0,1,2]这种情况,i指针显然要调,但是比较的方法是 nums[i] == nums[i-1],即
要和前一个比较是否相同
- 更新left和right不能简单++和--,例如[0,0,0,0,0]这种情况,left和right要一直++和--
直到遇到和之前不同的值
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> result_set;
for(int i=0;i<nums.size() - 2;i++){
if(nums[i] > 0) break;
if((i>0) && nums[i] == nums[i-1]) continue;
int left = i + 1;
int right = nums.size() - 1;
while(left != right){
int tmp_sum = nums[i] + nums[left] + nums[right];
if (tmp_sum == 0){
result_set.push_back(vector<int>{nums[i],nums[left],nums[right]});
int temp_left = nums[left];
while(nums[left] == temp_left && left!= right) left++;
int temp_right = nums[right];
while(nums[right] == temp_right && right != left) right--;
}else if(tmp_sum < 0){
left++;
}else{
right--;
}
}
}
return result_set;
}
};
四数之和
力扣原题:https://leetcode.cn/problems/4sum/
思路基本一样,只需要两层遍历
注意
- 对于第二层遍历,其不能越过
逻辑界限
:i+1(对于i这个界限是0,即正常的数组越界)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> result_vector;
if(nums.size() < 4){
return vector<vector<int>> {};
}
for(int i=0;i<nums.size()-3;i++){
if(i>0 && nums[i] == nums[i-1]) continue;
for(int j=i+1;j<nums.size()-2;j++){
if(j>i+1 && nums[j] == nums[j-1]) continue;
int left = j+1;
int right = nums.size() - 1;
while(left != right){
long long int temp_sum = static_cast<long long int>(nums[i]) + static_cast<long long int>(nums[j]) + static_cast<long long int>(nums[left]) + static_cast<long long int>(nums[right]);
if(temp_sum == target){
result_vector.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
int temp_left = nums[left];
while(temp_left == nums[left] && left != right) left++;
int temp_right = nums[right];
while(temp_right == nums[right] && left != right) right--;
}else if(temp_sum > target){
right--;
}else{
left++;
}
}
}
}
return result_vector;
}
};
本文作者:超超不会写代码
本文链接:https://www.cnblogs.com/cc8910/p/18523573
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步