• 算法


    时间复杂度

    1. 线性时间复杂度算法
      1. 单调栈
      2. 前缀和
      3. 差分数组
      4. 双指针
      5. 滑动窗口

    排序

    桶排序

    leetcode-164 最大间距

    max_, min_ = max(nums), min(nums)
    bucket_len = max(1, (max_ - min_) // (n - 1))       # 确定桶的长度 最小为1
    bucket_num = (max_ - min_) // bucket_len + 1        # 确定桶的数量 要加一
    buckets = [[] for _ in range(bucket_num)]           # 初始化所有桶
    
    for num in nums:       # 将所有元素添加到桶内
        buckets[(num - min_) // bucket_len].append(num)
    

    归并排序

    将数组分为两个子数组,子数组各自排序后再合并
    leetcode-493 翻转对

    def merge(nums, lo, mid, hi): # 合并两个排序好的数组
        aux = nums[lo : hi + 1]             # 备份 储存原始值
        i, j = lo, mid + 1        # 左右两个子数组的起点
        for k in range(lo, hi + 1): 
            if (i > mid):         # 左边数组已经全部合并
                nums[k] = aux[j - lo]
                j += 1
            elif (j > hi):        # 右边数组已经全部合并
                nums[k] = aux[i - lo]
                i += 1
            elif aux[i - lo] < aux[j - lo]:
                nums[k] = aux[i - lo]
                i += 1
            else:
                nums[k] = aux[j - lo]
                j += 1
    
    def merge_sort(nums, lo, hi): # 归并排序 包括lo到hi的全部数字 列表表示应该是range(lo, hi+1)
        if lo < hi:
            mid = (lo + hi) // 2
            merge_sort(nums, lo, mid)
            merge_sort(nums, mid+1, hi)
            merge(nums, lo, mid, hi)
    

    二分

    leetcode-4 寻找两个正序数组的中位数
    leetcode-1712 将数组分成三个子数组的方案数

    回溯

    leetcode-46 全排列
    leetcode-90 子集Ⅱ:集合去重
    leetcode-842 将数组拆分乘斐波那契序列

    位运算

    leetcode-5620 连接连续二进制数字

    a = a << 1 # 左移
    a = a >> 1 # 右移
    a = a & b  # 与
    a = a | b  # 或
    a = a ^ b  # 异或 若a等于b^c 则有b等于a^c
    ~a         # 非
    
    int a = 22; (10110)
    # 遍历所有1的组合
    for (int x = a; x > 0; x = a & (x - 1)) {
        cout << x << endl;
    }
    #输出22(10110),20(10100),18(10010),16(10000),6(00110),4(00100),2(00010)
    
    # 遍历每个1
    for (int x = a; x > 0; x = x & (x - 1)) {
        cout << x << endl;
    }
    #输出22(10110),20(10100),16(10000)
    

    数论

    计算排列组合

    leetcode-62 不同路径

    def C(total, t):
        ans = 1
        for i in range(1, t + 1):
            ans = ans * (total - i + 1) // i
        return ans
    

    快速幂

    时间复杂度O(logN)。
    leetcode-50 Pow(x,n)

    def myPow(x, n): # 计算x^n n>=0
        ans = 1.0
        x_contribute = x
        while n > 0:
            if n % 2 == 1:
                ans = ans * x_contribute
            x_contribute *= x_contribute
            n = n // 2
        return ans
    

    线性筛

    O(n)时间复杂度内找到小于n的素数。
    leetcode-204 计数质数

    def countPrimes(n):
        is_prime = [1] * n # 判断当前数字是否为素数
        is_prime[0] = is_prime[1] = 0
        primes = []
        for i in range(2, n):
            if is_prime[i]:
                primes.append(i)
            for prime in primes:
                if prime * i < n:
                    is_prime[prime * i] = 0
                    if i % prime == 0: break # 后面的数一定可以被prime筛到 退出循环
                else: break # 后面的素数*i也一定大于n 退出循环 
        return len(primes)
    

    动态规划

    leetcode-53 最大子序和:O(n)时间内求数组最大连续子数组和
    leetcode-188 买卖股票的最佳时机IV
    状态dp
    leetcode-5619 最小不兼容性

    字符串

    找回文子串

    动态规划O(N^2 ),中心扩展O(N^2 ),Manacher算法(N)
    leetcode-5 最长回文子串

    // valid[i][j]:字符串s[i]~s[j]是否为回文字符串
    string s;
    n = s.size();
    vector<vector<bool>> valid(n, vector<bool>(n, true));
    for (int i = n - 1; i >= 0; i--) {
        for (int j = i + 1; j < n; j++) {
            valid[i][j] = (s[i] == s[j]) & (valid[i + 1][j - 1]);
        }
    }
    

    数据结构

    优先队列

    能够完成下列操作的数据结构叫做优先队列:
    (1)插入一个数值
    (2)取出最小的数值(获得数值,并且删除)
    使用堆来进行解决,准确的说是二叉堆。
    leetcode-1046 最后一块石头的重量
    leetcode-295 数据流的中位数
    leetcode-480 滑动窗口中位数

    leetcode-402 移掉K位数字
    leetcode-316 去除重复字母

    单调栈

    线性时间复杂度下,维护一个单调递增或者递减的栈。
    在原始顺序下,针对每一个元素找到下一个恰好大于或小于它的元素。
    leetcode-42 接雨水
    leetcode-84 柱状图中的最大矩形
    leetcode-85 最大矩形
    leetcode-321 拼接最大数
    leetcode-5614 找出最具竞争力的子序列
    leetcode-239 滑动窗口最大值

    stack = []
    heights = [0] + heights + [0] # 处理好边界
    ans = 0
    for i in range(len(heights)):
        while stack and heights[stack[- 1]] > heights[i]:
            cur = stack.pop()
            ans = max(ans, heights[cur] * (i - 1 - stack[-1])) # 出栈时进行相应计算
        stack.append(i)
    return ans
    

    单调队列

    leetcode-1438 绝对差不超过限制的最长连续子数组

    二叉搜索树

    二叉搜索树能够高效进行如下操作:
    (1)插入一个数值
    (2)查询是否包含某个数值
    (3)删除某个数值
    二叉搜索树所有的节点满足左子树上的所有节点都比自己小,右子树上的所有节点都比自己大。
    leetcode-98 验证二叉搜索树

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    
    # 二叉树的中序遍历
    stack = []
    while stack or root:
        while root:
            stack.append(root)
            root = root.left
        root = stack.pop()
        print(root.val)
        root = root.right
    

    字典树

    利用嵌套哈希表来构建字典树
    0-1字典树
    leetcode-421 数组中两个数的最大异或值

    并查集

    一种管理元素分组情况的数据结构,可以高效的进行一下操作:
    (1)查询元素a和元素b是否属于同一组
    (2)合并元素a和元素b所在的组
    leetcode-1202 交换字符串中的元素
    leetcode-684 冗余连接
    leetcode-947 移除最多的同行或同列石头

    # 初始化
    for i in range(n):
        father[i] = i
    
    # 查询函数
    def find(x): # 查询函数
        if x == father[x]:
            return x
        else:
            father[x] = find(father[x]) # 带路径压缩
            return father[x]
    
    # 合并函数
    def merge(x, y, w):
        father[father[x]] = father[y]
    

    图论

    Dijkstra

    点的数量为n,边的数量为m。朴素的Dijkstra时间复杂度为(O(n^2)),使用堆优化的时间复杂度为(O(mlogn))

        struct Edge {
            int to, next, w;
        } myEdges[80010];
        int cnt = 0;
        vector<int> head = vector<int>(20010, 0);
        
    
        inline void addMyEdge(int p, int q, int w) {
            myEdges[++cnt].to = q;
            myEdges[cnt].w = w;
            myEdges[cnt].next = head[p];
            head[p] = cnt;
        }
        
        int solution(int n, vector<vector<int>>& edges) {
            for (auto edge : edges) {
                addMyEdge(edge[0] - 1, edge[1] - 1, edge[2]);
                addMyEdge(edge[1] - 1, edge[0] - 1, edge[2]);
            }
    
            vector<int> dist(n, INT_MAX); // 距离
            vector<bool> vis(n, false); // 是否访问过
            typedef pair<int, int> P;  // 堆中的元素要用pair类型
            priority_queue<P, vector<P>, greater<P>> q; // 小根堆
    
            dist[0] = 0; // 初始化起点的距离
            q.push(make_pair(dist[0], 0)); // 将起点添加进堆里
            while (!q.empty()) {
                int p = q.top().second;
                q.pop();
                if (vis[p]) continue;
                vis[p] = true;
                for (int j = head[p]; j != 0; j = myEdges[j].next) { // 遍历所有相邻节点
                    int to = myEdges[j].to;
                    dist[to] = min(dist[to], dist[p] + myEdges[j].w); // 松弛距离
                    if (!vis[to]) {
                        q.push( make_pair(dist[to], to) );
                    }
                }
            }
        }
    

    SPFA

    #include <iostream>
    using namespace std;
    
    struct Edge {
        int to, w;
        Edge (int to, int w) : to(to), w(w) {}
    }
    
    int MAX_N = 1000;
    vector<Edge> edges[MAX_N];
    
    void add(int from, int to, int w)
    {
        Edge e = {to, w};
        edges[from].push_back(e);
    }
    int main()
    {
        int n, m;
        cin >> n >> m;
        for (int i = 0; i < m; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w);
            add(v, u, w);
        }
        vector<int> dist(n, 99999999);
        dist[0] = 0;
        queue<int> q;
        vector<bool> inQueue(n, false);
        inQueue[0] = true;
        q.push(0);
        while (!q.empty()) {
            int node = q.front();
            q.pop();
            inQueue[node] = false;
            for (auto& edge : edges[node]) {
                if (dist[edge.to] > dist[node] + edge.w) {
                    dist[edge.to] = dist[node] + edge.w;
                    if (!inQueue[edge.to]) {
                        inQueue[edge.to] = true;
                        q.push(edge.to);
                    }
                }
            }
        }
        cout << dist[n - 1] << endl;
    }
    

    拓扑排序

    vector<vector<int>> g(n); // 邻接表
    vector<int> inDeg(n, 0);  // 入度
    for (auto& edge : edges) {
        inDeg[edge[1]]++;
        g[edge[0]].push_back(edge[1]); // 有向边
    }
    queue<int> q;
    for (int i = 0; i < n; i++) {
        if (inDeg[i] == 0) {
            q.push(i);
        }
    }
    int count = 0;
    while (!q.empty()) {
        int u = q.front();    
        q.pop();
        count++;
        for (auto& node : g[u]) {
            inDeg[node]--;
            if (inDeg[node] == 0) {
                q.push(node);
            }
        }
    }
    if (count != n) {
       cout << "有环" << endl;
    }
    
  • 相关阅读:
    L2TP协议
    PPP协议
    centos 更新linux内核
    关于GRUB2
    误删除libc.so.6 恢复
    LVS DR模式 负载均衡服务搭建
    进程地址空间分布和可执行文件分布
    centos 开机启动服务
    【netcore基础】ConcurrentDictionary 使用字符串作为key给代码加锁且使用EF事物防止并发调用数据混乱的问题
    【年终总结】个人的2018年年终总结
  • 原文地址:https://www.cnblogs.com/AlenDou-blog/p/14051826.html
Copyright © 2020-2023  润新知