算法摘要 自我记录简介

本文深入探讨了算法中的动态规划、回溯算法和BFS广度优先搜索。通过实例解析凑零钱问题、全排列问题和二叉树最小高度计算,展示了这些算法的应用。动态规划解决最值问题,通过状态转移方程优化穷举;回溯算法用于决策树遍历,如全排列;BFS则常用于寻找最短路径。
摘要由CSDN通过智能技术生成

目录

随笔

动态规划

回溯算法

凑零钱问题-动态规划

全排列问题-回溯算法

BFS算法框架

二叉树最小高度



随笔

快速排序基于二分的思想

队列是广度优先搜索和队列优化的Bellman-Ford最短路算法的核心数据结构

深度优先搜索(depth first search,DFS),关键在于解决当下该如何做,至于下一步怎么做和当下怎么做是一样的。可用于寻找最短路径等,山洞里面找出口,放一个绳子一起走的思想

void dfs(int step){
  判断边界
  尝试每一种可能 for(i=1;i<n;i++){
  继续下一步 dfs(step+1)
    }
返回
}

深度优先遍历沿着图的某一个分支遍历到末端,然后回溯,再沿着另一条做同样的遍历,知道所有的点都被访问过。

广度优先算法:Breadth First Search ,BFS。可用于寻找最短路径等

某种程度,动态规划的暴力阶段就是回溯算法,只是有些问题可以通过最优子结构,找到重叠部分,使用DP table或者备忘录优化

DFS(depth first search)深度优先算法可以被看做回溯算法

双指针分为快慢指针和左右指针。快慢指针解决链表问题如链表是否包含环,左右指针解决数组或字符串问题,如二分搜索

动态规划

动态规划的一般形式就是求最值,是运筹学的一种最优方法,如求最长递增子序列,最小编辑距离

动态规划的核心问题是穷举

动态规划三要素:重叠子问题,最优子结构,状态转移方程。

重叠子问题解决方法:用 备忘录或者 DP table 方法优化穷举过程

回溯算法

穷举,解决决策树的遍历过程

  1. 路径:已经做出的选择
  2. 选择列表:当前可以做的选择
  3. 结束条件:到达决策树底层,无法再做出选择
result =[]
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return 
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

凑零钱问题-动态规划

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

coins = [1, 2, 5]
amount = 11

def coin_change(coins, amount):
    def dp(n):
        if n == 0: return 0
        if n < 0: return -1
        res = float('INF')
        for coin in coins:
            sub = dp(n - coin)
            if sub == -1: continue
            res = min(res,1+sub)

        return res

    return dp(amount)


print(coin_change(coins,amount))

自下而上解法 


coins = [1,2,5]
amount = 11

def coinChange( coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0

    for coin in coins:
        for x in range(coin, amount + 1):
            dp[x] = min(dp[x], dp[x - coin] + 1)
            # print(dp)
    return dp[amount]

print(coinChange(coins,amount))

全排列问题-回溯算法

numbers = [1, 2, 3]


def pai_lie(numbers):
    trace = []

    def backtrace(numbers: list, trace: list):
        if len(trace) == len(numbers):
            print(trace)
            return

        for num in numbers:
            if num in trace:
                continue
            trace.append(num)
            backtrace(numbers, trace)
            trace.pop()

    backtrace(numbers, trace)


pai_lie(numbers)

BFS算法框架

int BFS(Node start, Node target){
    // 核心数据结果:队列
    queue<Node> q;
    // 标记集合,避免走回头路
    set<Node> visited;
    // 首先将起点加入队尾
    q.push(start);
    // 记录扩散的次数(其实就是要求的路径长度)
    int step = 0;
    while(!q.empty()){
        int sz = q.size();
        // 将当前队列中的所有节点向其”周围(图就是邻接点,二叉树就是子节点)“扩散
        for(int i = 0; i < sz; i++){
            // 获取队首元素并将其出列
            Node cur = q.front();
            q.pop();
            // 划重点,这里判断是否到达终点
            if(cur is target)
                return step;
            
            // 将当前节点cur的所有相邻节点加入队尾
            for(Node p : cur.adj()){
                if(p not in visited){
                    // 加入队尾
                    q.push(p);
                    // 标记
                    visited.add(p);
                }
            }
        }
        // 划重点,更新步数在这里
        step++;
    }
}

二叉树最小高度

int minDepth(TreeNode root) {
    if (root == null) return 0;
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    // root 本身就是一层,depth 初始化为 1
    int depth = 1;

    while (!q.isEmpty()) {
        int sz = q.size();
        /* 将当前队列中的所有节点向四周扩散 */
        for (int i = 0; i < sz; i++) {
            TreeNode cur = q.poll();
            /* 判断是否到达终点 */
            if (cur.left == null && cur.right == null) 
                return depth;
            /* 将 cur 的相邻节点加入队列 */
            if (cur.left != null)
                q.offer(cur.left);
            if (cur.right != null) 
                q.offer(cur.right);
        }
        /* 这里增加步数 */
        depth++;
    }
    return depth;
}

双指针套路-快慢和左右和滑动

判断链表是否有环。快慢指针相遇就有环

boolean hasCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow)
            return true;
    }
    return false;
}

左右指针在数组中实际是指两个索引值,一般初始化为 left = 0, right = nums.length - 1 

# 二分查找
int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1;
    while(left <= right) {
        int mid = (right + left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; 
        else if (nums[mid] > target)
            right = mid - 1;
    }
    return -1;
}

滑动窗口算法框架

/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
    unordered_map<char, int> need, window;
    for (char c : t) need[c]++;

    int left = 0, right = 0;
    int valid = 0; 
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        ...

        /*** debug 输出的位置 ***/
        printf("window: [%d, %d)\n", left, right);
        /********************/

        // 判断左侧窗口是否要收缩
        while (window needs shrink) {
            // d 是将移出窗口的字符
            char d = s[left];
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值