算法哪些事儿---2

耐下心来

1.vector和数组在大小指定上的区别

2.O(1)时间复杂度可以获取容器内元素大小的容器(size)

它们的本质都是在实现容器的时候维护了一个计算元素个数的计数器;

3.string在算法题中的常用函数

①判断字符串是否为空,s.empty();

②字符串的尾插,s.push_back();

③字符串的尾删,s.pop_back();

④字符串的指定位置删除,s.erase(str.begin()+3);删除s的第四个元素,时间复杂度O(n);

⑤字符串在指定位置插入元素,s.insert(5,"hello"); 时间复杂度O(n)

⑥字符串的拼接操作,s+="hello",等价于 s.append("hello");

⑦字符串的截取substr(0,5);从0位置截取5个;

⑧字符串翻转reverse(s.begin(),s.end()),翻转整个数组;

4.string::npos是什么

是string中的一个静态常量,表示"未找到"或"直到字符串末尾"的特殊值,一般用于

是否找到的判断;

5.stack在算法中常用的函数

①遇到表达式求和这类题我们通常使用的是栈来模拟,这类题的解决方法是这个;

在leetcode字符串解码这道题中,我们会使用两个栈来解决这个问题

5. 队列算法题常用知识点

队列和宽搜题往往是密不可分的,因为队列在算法题中的多数情况是服务于宽搜题的,宽搜就是BFS广度优先搜索。属于搜索类的算法,而搜索类的算法说白了其实也就两种,一种是宽搜,一种是深搜,常见的leetcode算法题中关于宽搜的题包括,迷宫最短路径(网格图,上下左右 4 个方向走)二叉树层序遍历(一层一层打印树)岛屿数量(搜索连通块)打开转盘锁、腐烂的橘子;

接下来我们先探讨一下宽搜这种算法在树中的应用;

首先我们要连接队列的基本函数

① 队列在层序遍历中的应用

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(Node* root)
    {
         vector<vector<int>> ret;   //记录最终结果
         queue<Node*> q;             //层序遍历需要的队列
         if(root == nullptr)  return ret;
         q.push(root);
         while(q.size())
         {
            vector<int> tmp; //存放本层的结点
            int sz = q.size();  //统计本层的结点个数
            for(int i=0;i<sz;i++)
            {
                Node * t = q.front();
                q.pop();
                tmp.push_back(t->val);
                for(Node * child :t->children)
                {
                    if(child != nullptr)
                    {
                         q.push(child);
                    }
                }
            }
            ret.push_back(tmp);
         }
        return ret;
    }
};

有的队列的容器可以查找队头和队尾,但是有的队列容器你只能查找对头查不到队尾;

这个题其实就是一个解决队列和树的关系的模板方法,遇到这种题可以考虑用这个模板;

6.优先级队列(堆)算法

堆的常用接口有

如何在算法中创建大堆小堆;

面试常常会考手写堆,所以我们需要自己会手撕堆的实现

cpp 复制代码
#include <vector>
using namespace std;

class MaxHeap {
public:
    vector<int> heap;

    //上浮
    void up(int i) {
        while(i > 0) {
            int fa = (i - 1) / 2;
            if(heap[i] > heap[fa]) {
                swap(heap[i], heap[fa]);
                i = fa;
            } else {
                break;
            }
        }
    }

    //下沉
    void down(int i) {
        int n = heap.size();
        while(true) {
            int left = i * 2 + 1;
            int right = i * 2 + 2;
            int maxIdx = i;

            if(left < n && heap[left] > heap[maxIdx]) maxIdx = left;
            if(right < n && heap[right] > heap[maxIdx]) maxIdx = right;

            if(maxIdx == i) break;
            swap(heap[i], heap[maxIdx]);
            i = maxIdx;
        }
    }

    void push(int x) {
        heap.push_back(x);
        up(heap.size() - 1);
    }

    void pop() {
        swap(heap[0], heap.back());
        heap.pop_back();
        down(0);
    }

    int top() {
        return heap[0];
    }

    bool empty() {
        return heap.empty();
    }
};

在面试过程中我们遇到面试官问我们Topk问题,面试官一般想要我们的解决方法有①堆,②快排,也就是快速选择算法,这两个方法是时间复杂度都比价低,

关于用堆的方法解决第K大或者第K小的问题,找第K大我们就建一个之后K个容量的小根堆,从头到尾遍历这组数字,遇到数字大于当前heap的top的值的时候,我们删除heap的top,然后把这个 数字插入heap,这样做的目的是让heap中始终保存的是当前见到的K个最大的数字,当走到数组结尾的时候,在heap这个小根堆中保存的刚好是从最大数字到第K大的数值,并且因为我们建的是小根堆,所以堆顶元素的值就是我们要找的第K大;

同理当我们要找第K小这个问题,我们建的是大根堆,比如我们要找第3小,我们每次要做的是让当前指针遍历到的值和堆顶元素的值比较,如果比堆顶值小,就删除堆顶的,将这个元素push进堆中,这样当遍历完之后我们就找到了整个数组中的3个最小值,因为我们创建的是大根堆,第3小肯定是最小的元素中的最大的呢个,所以我们返回top即为第K小;

下面是我用heap实现的一道topK问题

cpp 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k)
    {
         priority_queue<int,vector<int>,greater<int>> heap;
         for(auto & x:nums)
         {
             heap.push(x);    //堆在push之后会自动调整排序的
             if(heap.size() > k)
             {
                 heap.pop();   //因为创建的是小根堆所以现在pop的一定是最小的元素
             }
         }
         return heap.top();
    }
};

值得注意的是我们创建的堆的比较方式是可以自己定义的,下面这道算法题我们就是通过自己定义比较方式来进行比较的;

cpp 复制代码
class Solution {
    typedef pair<string,int> PSI;
    struct cmp
    {
       bool operator()(const PSI&a,const PSI&b)
       {
          if(a.second == b.second) //频次相同,字典序按照大根堆的方式排序
          {
             return a.first < b.first;
          }
          return a.second > b.second;
       }
    };
public:
    vector<string> topKFrequent(vector<string>& words, int k)
    {
        //1.统计单词出现的频次
        unordered_map<string,int> hash;
        for(auto &w:words)  hash[w]++; 
       
        //2.创建堆
        priority_queue<PSI,vector<PSI>,cmp> heap;

        //3.topK的主逻辑
        for(auto &psi:hash)
        {
            heap.push(psi);
            if(heap.size()>k) heap.pop();
        }

        //4.提取结果,创建一个vector,默认大小是k,然后让堆顶元素依次倒着放进vector中
        vector<string> ret(k);
        for(int i=k-1;i>=0;i--)
        {
            ret[i] = heap.top().first;
            heap.pop();
        }
        return ret;
    }
};

7.关于算法题中的精度问题

意思就是在我们做算法题的时候如果想要返回值是符合题目要求的精度的时候可以想办法利用这种精度提成的方式;

相关推荐
方知我14 分钟前
NumPy一小时速成
开发语言·python·numpy
深入云栈40 分钟前
CompleteFuture VS CompletableFuture:Netty 为何自研 Future
java·架构
OPEN-F40 分钟前
C++综合实战:面向对象图书管理系统
开发语言·c++
ttwuai1 小时前
Go 后台清空操作日志失败,权限和无 WHERE 删除怎么排查?
开发语言·后端·golang
choumou_M1 小时前
SpringBoot_7:用户资源的上传与修改
java·spring boot·后端
西西弗Sisyphus1 小时前
Qt 在无边框窗口上做一套换肤系统
开发语言·数据库·qt
西西弗Sisyphus1 小时前
Qt 启动 UsageStatistic 插件报错
开发语言·qt
NeoGressAI外贸数字化1 小时前
外贸独立站零询盘排查:从 Google Search Console 到 PageSpeed 的技术实操
开发语言·c++
无小道1 小时前
C/C++——异步编程小记
开发语言·c++·c++11