算法练习——力扣随笔【LeetCode】【C++】


在这里插入图片描述

LeetCode 练习随笔

做题环境 C++

中等题很值,收获挺多的

不会的题看题解,一道题卡1 h +,多来几道,时间上耗不起。

力扣上的题目和 OJ题目相比不同之处?

一开始上手力扣不习惯,OJ 的题目提交的是完整代码,力扣上的C++只提交目标函数代码,比如某个题目你只需要完成topKFrequent(nums,k)这个函数。

class Solution {
    vector<int> topKFrequent(vector<int>& nums, int k) {

    }
};

这也就意味着程序设计的输入不需要自己额外设计了,同时限制了你数据的输入和返回内容的格式。
多亏了oj许多来自力扣的题并补全了完整代码(目标函数部分空缺),渐渐习惯了编写目标函数的答题习惯。

定义问题

  • 在哪定义结构体

    有两个位置可写:

    1)类里面,讲究的话写在private中,这里就不讲究了。

    class Solution {
    public:
        struct node
        {
          int data;  
        };
        vector<int> topKFrequent(vector<int>& nums, int k) {
    
        }
    };
    

    2)顶部区域

     struct node
        {
          int data;  
        };
    
    class Solution {
    public:
        struct node
        {
          int data;  
        };
        vector<int> topKFrequent(vector<int>& nums, int k) {
    
        }
    };
    
  • 在哪定义全局变量

    1)顶部区域

    #include<algorithm>
    int a =3;
    class Solution {
    public:
        vector<int> topKFrequent(vector<int>& nums, int k) {
            
            vector<int>v;
            v.push_back(a);
    return v;
        }
    };
    

    2)换个方式,传参方式改为引用

    很多全局变量是可以被替代的,比如希望值被修改并返回到原来作用域,但返回值位置紧张。

    暴力时,全局变量开大数组还是有点用处的。

  • 在哪定义头文件

    一般是全面的,多虑了。

    下面这种做题区域顶部写头文件试过,编译通过。

    #include<algorithm>
    
    class Solution {
    public:
        vector<int> topKFrequent(vector<int>& nums, int k) {
    
        }
    };
    

排序问题

  • vector 存入结构体怎么自定义比较规则?

    1)自定义规则为结构体形式(优先队列也可用)

    class Solution
    {
    
    public:
        typedef struct node
        {
            int data;
            int sum;
        } ll;
    //    static bool cmp(ll a, ll b)
    //    {
    //        return a.sum > b.sum;
    //    }
    
        struct CompareBySumDesc
        {
            bool operator()( ll a,  ll b) 
            {
               return a.sum > b.sum;
            }
        };
    
    
        vector<int> topKFrequent(vector <int> &nums,int k)
        {
           
            vector<ll>v;
            //...
            
    		//vector排序
            sort(v.begin(),v.end(),CompareBySumDesc());
    //        sort(v.begin(),v.end(),cmp);
            //...
            return res;
    
    
        }
    
    
    };
    

    2)自定义规则为静态函数(简单)

    class Solution
    {
    
    public:
        typedef struct node
        {
            int data;
            int sum;
        } ll;
        static bool cmp(ll a, ll b)
        {
            return a.sum > b.sum;
        }
    
    //    struct CompareBySumDesc
    //    {
    //        bool operator()(const ll a, const ll b) const
    //        {
    //           return a.sum > b.sum;
    //        }
    //    };
    
    
        vector<int> topKFrequent(vector <int> &nums,int k)
        {
           
            vector<ll>v;
            //...
            
    		//vector排序
    //        sort(v.begin(),v.end(),CompareBySumDesc());
            sort(v.begin(),v.end(),cmp);
            //...
            return res;
    
    
        }
    
    
    };
    
  • vector 存入结构体怎么逆置?

    用 stack。

  • 存入结构体的 priority_queue 怎么自定义比较规则?

    给 ll 按照 sum 排序

    class Solution
    {
    public:
        typedef struct node
        {
            int data;
            int sum;
        } ll;
      //试过了,static 函数行不通,只能 
        struct cmp
        {
            bool operator()( ll a,  ll b)
            {
                return a.sum > b.sum;
            }
        };
        
        void f()
        {
            priority_queue<ll, vector<ll>, cmp> pq;
    
          
    };
    int main()
    {
        Solution().f();
    
        return 0;
    }
    
    
    
    //默认降序
    
    //没有结构体,升序
    priority_queue <int,vector<int>,greater<int> > q;
    
  • 使用 priority_queue 实现堆排序?

    优先队列本身是堆实现的。只需维护好优先队列的容量 k,超过pop掉。

统计问题

map 统计的神。遍历别忘了迭代器初始化。

注意事项

  • vector<vector<int > >v;尖括号嵌套尽可能规范地隔开,

    priority_queue <int,vector<int>,greater<int>> pq;这种尖括号紧贴一起依稀记得编译没通过。

  • unordered_mapunordered_set遍历别忘了迭代器初始化。

玄学

可能不对,原理没有细究,欢迎留言纠正。

  • 字符内容 的最大公约数的长度 == 两字符长度的最大公约数。(若字符内容最大公约数存在为前提)

新 get!

新思路

新要求

新问题

1-单调栈

单调栈(Monotonic Stack)是一种常用的数据结构,用于解决一些关于单调性的问题。它通常用于处理需要维护一个递增或递减的元素序列的场景。单调栈的主要思想是保持栈内元素的单调性。

单调递增栈:栈顶元素是栈中最小的元素,当要入栈的元素小于等于栈顶元素时,将栈顶元素弹出,直到栈为空或栈顶元素小于新元素。这样,栈内的元素呈现递增顺序。

单调递减栈:栈顶元素是栈中最大的元素,当要入栈的元素大于等于栈顶元素时,将栈顶元素弹出,直到栈为空或栈顶元素大于新元素。这样,栈内的元素呈现递减顺序。

单调栈在解决一些问题时具有很好的效率,特别是涉及连续区间的问题。例如,在找到数组中每个元素的下一个更大元素、下一个更小元素,或者找到满足特定条件的最大连续子数组等问题中,单调栈都可以派上用场。

example 1

#include <iostream>
#include <stack>
#include <vector>

using namespace std;
//单调递减栈的特性,通过遍历输入数组,找出每个元素的下一个更大元素,并将结果保存在 result 向量中。
vector<int> nextGreaterElement(const   vector<int>& nums) {
      vector<int> result(nums.size(), -1); // Initialize result vector with -1
      stack<int> monoStack; // Monotonic decreasing stack of indices
    
    for (int i = 0; i < nums.size(); ++i) {
        while (!monoStack.empty() && nums[i] > nums[monoStack.top()]) {
            result[monoStack.top()] = nums[i];
            monoStack.pop();
        }
        monoStack.push(i);
    }
    
    return result;
}

int main() {
      vector<int> nums = {4, 3, 7, 1, 8, 5, 2, 6};
      vector<int> nextGreater = nextGreaterElement(nums);
    
      cout << "Next Greater Elements: ";
    for (int val : nextGreater) {
          cout << val << " ";
    }
      cout <<   endl;
    
    return 0;
}

2-滑动窗口?

连续子序列和问题,优先考虑滑动窗口。

3-auto 应用【c++11 ,STL】

用了auto,代码太简洁了。

example 1

vector<vector<int> >v;

遍历v中的整型,我的常规做法:

for(int i=0;i<v.size();i++)
{
    for(int j=0;j<v[i].size();j++)
    {
        cout<<v[i][j]<<" ";
    }
}

用auto遍历v中的整型:

for(auto items:v)
{
    for(auto item:items)
    {
        cout<<item<<" ";
    }
}

example 2

string s;

遍历string,我的常规做法:

for(int i=0;i<s.size();i++)
    cout<<s[i]<<" ";

auto遍历string更简洁:

for(auto c:s)
	cout<<c<<" ";

example 3

函数参数声明(复杂类型):

void f(vector<vector<int> >a,vector<vector<int> >b)
{
    //...
}

auto简化参数声明:

void f(auto a,auto b)
{
    //...
}

example 4

#include<set>
set<int>s;

STL容器使用迭代器遍历:

for(set<int>::iterator it = s.begin();it != s.end();++it)
    cout<<*it<<" ";

auto简化迭代器:

for(auto it = s.begin();it != s.end();++it)
    cout<<*it<<" ";

4-sort() 内嵌式规则

vector<int>v;

通常:

sort(v.begin(),v.end());
bool cmp(int a,int b)
{
    return a > b;
}

sort(v.begin(),v.end(),cmp);

内嵌式书写排序规则:

//sort(BEGIN,END,[](a,b){...});
sort(v.begin(),v.end(),[](const auto &a, const auto &b){return a > b;})

5-实现无删遍历queue

不删除元素遍历queue

queue<int>q;
int len =q.size();
//原理:模拟循环
while(len--)
{
    int qf = q.front();
    //qf
    q.pop();
    q.push(qf);
}

6-存储二叉树层次遍历的每一层

连这三道题都用到了,记录一下当板子。

vector<vector<int> >zigzagLevelOrder(TreeNode*root)
    {
        if(!root)return {};//不空才有必要继续。
    
        vector<vector<int> >v;

        //pre 先存入漏掉的root,后面都是存新的一层,root永远都不属于新的一层,这里补上。
        vector<int>temp;
        temp.push_back(root->val);
        v.push_back(temp);
    
        //queue operation
        queue<TreeNode*>q;
        q.push(root);
        TreeNode *t ;
        int ct=1,nex_ct=0;//关键  借助次数标记旧层全无,全是新层的时刻
        while(!q.empty())
        {
            t=q.front();
            q.pop();
            ct--;
            //新层进
            if(t->left)
            {
                q.push(t->left);
                nex_ct++;
            }
            if(t->right)
            {
                q.push(t->right);
                nex_ct++;
            }
            //
            if(ct==0)//全是最新的一层
            {
                vector<int>item;
                int len = q.size();
                while(len--)//无删遍历queue
                {
                    TreeNode*qf = q.front();
                    item.push_back(qf->val);

                    q.pop();
                    q.push(qf);
                }
                if(!item.empty())v.push_back(item);//力扣空的都不给过。oj里没事
                //next
                ct= nex_ct;
                nex_ct=0;
            }


        }
        return v;

    }

7-删除 vector 任意位置元素

vector<int>v;
//删除 v[i]
swap( v[i],v[v.size()-1]);
v.pop_back();

其实只是 v.size()减少,内存不释放。

 vector<int >v;
    v.push_back(11);
    v.push_back(22);
    v.push_back(33);

    v.pop_back();
    v.pop_back();
    v.pop_back();
    
    cout<<v.size()<<endl;
	v.push_back(44);

    cout<<v[0]<<endl;
    cout<<v[1]<<endl;
    cout<<v[2]<<endl;

这里存入三个元素,之后全部删除,v.size() 结果是 0,是预期的结果,

但此时通过下标访问v[0]~v[2],原来的值仍然可以访问到,删除时内存没有释放掉。

之后再加入新的元素44,v[0] 内容被覆盖了。

8-手写实现dijkstra

const int INF = 0x3f3f3f3f;
vector<vector<int> >pojects;  //[ [1,2,3],[1,2,3],[1,2,3] ]  [from,to,cost]

int dijkstra(auto &pojects, int n, int k)//from to k
    {
        int vis[n];
        memset(vis,0,sizeof(vis));
        //int temp[n+1];
        //memset(temp,INF,sizeof(temp));
    	vector<int>temp(n+1,INF);//用vector 定义temp数组只需要一行。因为下标要用 1-n,定义n+1更合适。
    
        for(auto it:pojects)
        {
            if(it[0] == k)
            {
                int to = it[1];
                int dis = it[2];
                temp[to] = min(temp[to],dis);
            }
            temp[k]=0;
        }
        int tot =n;
        int v=k;
        while(tot--)
        {
            int mi_len = INF;
            for(int i=1; i<=n; i++)//这里顶点范围[1,n]
            {
                if(vis[i] == 0 && temp[i] < mi_len)
                {
                    mi_len =temp[i];
                    v = i;
                }
            }
            if(mi_len == INF)return -1;//没有邻边了,存在不可达点
            vis[v]=1;

           
            for(auto it:pojects)
            {
                if(it[0] == v)
                {
                    int to = it[1];
                    int dis = it[2];
                    temp[to] = min(temp[to],temp[v] + dis);
                }
                temp[k]=0;
            }


        }
    }

9-最大值写法

忘了,记录一下。

const int INF = 0x3f3f3f3f;

10-vector初始化

vector<int>v(n,0);

容量 n,初始值 0。

打个形状为 nxm 的 0矩阵

vector<vector<int>> v(n,vector<int>(m,0));

其实默认初始化值为0,可以再省一步:

vector<int>v(n);
vector<vector<int>> v(n,vector<int>(m));

11-binary_search【c++11 ,STL】

使用STL内置binary_search

#include<algorithm>
vector<int>v;
bool res = binary_search(v.begin(),v.end(),target);

cout<<(res?"true":"false")<<endl;
binary_search(startaddress, endaddress, valuetofind);

startaddress: 列表起始地址
endaddress: 列表末尾地址
valuetofind: 查找的目标值

12-unordered_map【c++11 ,STL】

#include<unordered_map>
using namespace std;//注意需要使用命名空间

unordered_mapmap相比实现原理、特点和应用?

insertsearch底层原理限制
unordered_mapO(logn)O(1)hash表键值对只能是基本类型
mapO(logn)O(logn)红黑树

想用键值对,优先考虑unordered_map

13-unordered_set【c++11 ,STL】

#include<unordered_set>
using namespace std;//注意需要使用命名空间

想用集合,优先考虑unordered_set

unordered_setset相比实现原理和特点?

  • set底层实现是二叉树【红黑树】,适合频繁插入删除,且保证元素有序【默认升序】。
  • unordered_set底层实现是 hash表,常数级查询速度。

14-__gcd(len1,len2)【c++11,STL】

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int num1 = 48;
    int num2 = 18;

    int gcd_result = __gcd(num1, num2);

   cout << gcd_result << endl;//最大公约数

    return 0;
}

14-stirng.find

查找不到返回什么?

string a;
a ="abcd";
cout<<a.find("ab")<<endl;
cout<<a.find("aa")<<endl;
cout<<a.find("abb")<<endl;
0
4294967295
4294967295

返回值:

找到:[0,a.size())

找不到:4294967295,换句话说,范围之外的数。

15-INT_MAX

c++中,最大int常量。

INT_MAX 与 0x3f3f3f3f 大小比较。

INT_MAX 更大。

INT_MAX - 0x3f3f3f3f = 1086374080

16-快慢指针

以移动0到末尾为例,要求不能复制数组,在原数组上操作:

class Solution
{
public:
    void moveZeroes(vector<int>& nums)
    {

        int slowIndex = 0;
        for(int fastIndex=0;fastIndex<nums.size();fastIndex++)
        {
            if(nums[fastIndex] != 0//匹配到目标数组
            {
                swap(nums[fastIndex],nums[slowIndex++]);//slowIndex就是用来向后扩展目标数组,并指定下一个位置
            }
        }

    }
};

找字符子序列也可以这么做,思路如下:

把目标子序列abc类比成目标数组,在提供的字符串ahbkc中,用快慢指针凑出来这个目标abc,凑成了【比如这里的abchk】就是存在abc

class Solution {
public:
    bool isSubsequence(string s, string t) {
        if(s.size()> t.size())return false;
        if(s.size() == t.size())return s==t;

        int pos = 0;//s[pos]

        int slow = 0;
        for(int fast = 0;fast< t.size();fast++)
        {
            if(t[fast] ==s[pos])
            {
                swap(t[fast],t[slow++]);
                ++pos;
                if(pos >=s.size())break;

            }
        }
        if(pos >=s.size())return true;
        return false;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来杯Sherry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值