算法学习笔记Day8——回溯算法

本文解决几个问题:

回溯算法是什么?解决回溯算法相关的问题有什么技巧?回溯算法代码是否有规律可循?

一、介绍

1.回溯算法是什么?

回溯算法就是个多叉树的遍历问题,关键在于在前序和后序时间点做一些操作,本质是一种暴力枚举算法,它和DFS非常相似,区别在于,回溯算法关注点在于树的树枝,DFS关注点在于树的节点。

2.回溯算法的技巧

站在一棵决策树的节点,需要考虑三个问题:

1、路径:也就是已经做出的选择。

2、选择列表:也就是你当前可以做的选择。

3、结束条件:也就是到达决策树底层,无法再做选择的条件。

3. 回溯算法的框架(规律)

result = []
def backtrack(路径,选择列表){
    if(满足结束条件){
        result.add(路径)
        return;
    }
    
    for(选择 : 选择列表){
        //做选择
        将该选择从选择列表移除
        路径.add(选择)
        backtrack(路径, 选择列表)
        //撤销选择
        路径.remove(选择)
        将该选择再加入选择列表
    }

}

其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,其实就是在维护每个节点的路径和选择列表信息,

抽象地说,解决一个回溯问题,实际上就是遍历一棵决策树的过程,树的每个叶子节点存放着一个合法答案。你把整棵树遍历一遍,把叶子节点上的答案都收集起来,就能得到所有的合法答案。

我们定义的 backtrack 函数在这棵树上游走,同时要正确维护每个节点的属性,每当走到树的底层叶子节点,其「路径」就是一个答案。

4. 回溯算法和DFS的关系

回溯法 采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

i)找到一个可能存在的正确的答案;
ii)在尝试了所有可能的分步方法后宣告该问题没有答案。

深度优先搜索 是一种用于遍历或搜索树或图的算法。这个算法会 尽可能深 的搜索树的分支。当结点 v 的所在边都己被探寻过,搜索将 回溯 到发现结点 v 的那条边的起始结点。这一过程一直进行到已发现从源结点可达的所有结点为止。如果还存在未被发现的结点,则选择其中一个作为源结点并重复以上过程,整个进程反复进行直到所有结点都被访问为止。

5. 回溯算法的拓展

由于回溯算法的时间复杂度很高,因此在遍历的时候,如果能够提前知道这一条分支不能搜索到满意的结果,就可以提前结束,这一步操作称为 剪枝。剪枝是一种技巧,通常需要根据不同问题场景采用不同的剪枝策略,需要在做题的过程中不断总结。

二、排列、组合、子集相关问题

无论是排列、组合还是子集问题,简单说无非就是让你从序列 nums 中以给定规则取若干元素,主要有以下几种变体:

形式一、元素无重不可复选

形式二、元素可重不可复选

形式三、元素无重可复选

但无论形式怎么变化,其本质就是穷举所有解,而这些解呈现树形结构,所以合理使用回溯算法框架,稍改代码框架即可把这些问题一网打尽

为什么只要记住这两种树形结构就能解决所有相关问题呢?

首先,组合问题和子集问题其实是等价的,这个后面会讲;至于之前说的三种变化形式,无非是在这两棵树上剪掉或者增加一些树枝罢了

无重不可复选

核心:通过保证元素之间的相对顺序不变来防止出现重复的子集。

具体方法:使用 start 参数控制树枝的生长避免产生重复的子集,用 track 记录根节点到每个节点的路径的值,同时在前序位置把每个节点的路径值收集起来,完成回溯树的遍历就收集了所有子集。

例题1:子集

代码

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> track;
    void traceback(vector<int>& nums, int start){
        // 每个节点的值都是一个子集
        ans.emplace_back(track);
        for(int i = start; i < nums.size(); i++){
            track.emplace_back(nums[i]);
            traceback(nums, i+1);
            track.pop_back();
        }
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        traceback(nums, 0);
        return ans;
    }
};

例题2:组合 

分析

代码

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> track;
    void backtrack(int n, int k, int start){
        if(track.size() == k){
            ans.emplace_back(track);
        }
        for(int i = start; i<= n; i++){
            track.emplace_back(i);
            backtrack(n, k, i+1);
            track.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        backtrack(n, k, 1);
        return ans;
    }
};

例题3:全排列

分析

选择列表就是遍历整个数组,如果没有用过某个数字,就选择它,然后把used数组对应的值设为true,然后进入节点(递归调用backtrack()),出来后撤销选择,方式是对路径弹出元素,然后更新used数组。

代码

class Solution {
public:
    vector<vector<int>> ans;
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> track;
        vector<bool> used(nums.size(), false);
        backtrack(nums, track, used);
        return ans;
    }
    void backtrack(vector<int>&nums, vector<int>&track, vector<bool>& used){
        if(track.size() == nums.size()){
            ans.push_back(track);
            return;
        }
        for(int i  =0; i < nums.size(); i++){
            if(used[i] == true){
                continue;
            }
            track.push_back(nums[i]);
            used[i] = true;
            backtrack(nums, track, used);
            track.pop_back();
            used[i] = false;
        }
    }
};

但如果题目不让你算全排列,而是让你算元素个数为 k 的排列,怎么算?

也很简单,改下 backtrack 函数的 base case,仅收集第 k 层的节点值即可

可重不可复选

例题4:子集 II

分析

元素可重的通用解决方法:排序 + used数组,连续相同的元素只能按顺序取,不能前面的没取取后面的。

代码

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> track;
    void backtrack(vector<int>& nums, int start, vector<bool> used){
        ans.emplace_back(track);
        for(int i = start; i<nums.size(); i++){
             // 剪枝逻辑,值相同的相邻树枝,只遍历第一条
            if(i>0 && nums[i] == nums[i-1] && used[i-1] == false){
                continue;
            }
            track.emplace_back(nums[i]);
            used[i] = true;
            backtrack(nums, i+1, used);
            used[i] = false;
            track.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        backtrack(nums, 0, used);
        return ans;
    }
};

例题5:组合总和 II

代码

class Solution {
public:
    vector<int> track;
    vector<vector<int>> ans;
    void traceback(vector<int>& candidates, int target, vector<bool>& used, int start){
        int sum = accumulate(track.begin(), track.end(), 0);
        if(sum == target){
            ans.emplace_back(track);
            return;
        }
        if(sum > target){
            return;
        }
        for(int i  = start ; i< candidates.size(); i++){
            if(used[i] || ( i>0 && candidates[i-1] == candidates[i] && used[i-1] == false)){
                continue;
            }
            used[i] = true;
            track.emplace_back(candidates[i]);
            traceback(candidates, target, used, i+1);
            track.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        vector<bool> used(candidates.size(), false);
        traceback(candidates, target, used, 0);
        return ans;
    }
};

例题6:全排列 II

分析

所有相同的数字,他的排列组合只有一个,记住这一点,然后用nums[i-1] == nums[i] && used[i-1] == false这个条件去限制,就可以做到相同数字只有一个排列进入答案。

代码

class Solution {
public:
    vector<vector<int>> ans;
    void backtrack(vector<int>& nums, vector<int>& track, vector<bool>& used){
        if(track.size() == nums.size()){
            ans.push_back(track);
            return;
        }
        for(int i = 0; i< nums.size(); i++){
            if(used[i]==true || (i > 0 && nums[i-1] == nums[i] && used[i-1] == false)){
                continue;
            }
            used[i] = true;
            track.push_back(nums[i]);
            backtrack(nums, track, used);
            track.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<int> track;
        vector<bool> used(nums.size(), false);
        backtrack(nums, track, used);
        return ans;
    }
};

元素无重可复选

例题7:组合总和

分析

想解决这种类型的问题,也得回到回溯树上,我们不妨先思考思考,标准的子集/组合问题是如何保证不重复使用元素的

答案在于 backtrack 递归时输入的参数 start,这个 i 从 start 开始,那么下一层回溯树就是从 start + 1 开始,从而保证 nums[start] 这个元素不会被重复使用,那么反过来,如果我想让每个元素被重复使用,我只要把 i + 1 改成 i 即可:

代码

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> track;
    int sum;
    void backtrack(vector<int>& nums, int target, int start){
        sum = accumulate(track.begin(), track.end(), 0);
        if(sum > target){
            return;
        }
        if(sum == target){
            ans.emplace_back(track);
            return;
        }
        for(int i  = start; i<nums.size(); i++){
            track.emplace_back(nums[i]);
            backtrack(nums, target, i);
            track.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& nums, int target) {
        backtrack(nums, target, 0);
        return ans;
    }
};

其他

例题8:排列序列

代码

思路1:回溯 + 剪枝

class Solution {
public:
    vector<vector<char>> ans;
    vector<char> track;
    int cnt = 0;
    void backtrack(int n, vector<bool> used){
        if(track.size() == n){
            ans.emplace_back(track);
            cnt++;
            return;
        }
        for(int i  = 0; i< n; i++){
            if(used[i]){
                continue;
            }
            track.emplace_back(i+1+'0');
            used[i] = true;
            backtrack(n, used);
            used[i] = false;
            track.pop_back();
        }
    }
    string getPermutation(int n, int k) {
        vector<bool> used(n, false);
        //如果数字过大,直接定位第一位
        if( k > 120){
            int factorial = 1;
            int first;
            for(int i = 1; i < n; i++){
                factorial *= i;
            }
            first = k/factorial;
            k %= factorial;
            track.emplace_back(first + 1 + '0');
            used[first] = true;
        }
        
        //业务逻辑
        backtrack(n, used);
        string ret(ans[k-1].begin(), ans[k-1].end());
        return ret;
    }
};

 思路2:直接定位

class Solution {
public:
    string getPermutation(int n, int k) {
    vector<char> ans, array;
    int tmp, ncp = n, f = 1;
    array.emplace_back('1');
    for(int i = 1; i < ncp; i++){
        f*= i;
        array.emplace_back(i+1+'0');
    }
    f*= ncp;
    k--;
    while(n != 0){
        f /= n--;
        tmp = k/f;
        k %= f;
        ans.emplace_back(array[tmp%ncp]);
        array.erase(array.begin() + tmp);
    }
    string ret(ans.begin(), ans.end());
    return ret;
    }
};

例题9:复原 IP 地址

代码

class Solution {
public:
    vector<string> ans;
    vector<int> segment;
    void backtrack(string s,int pos, int segcnt){
        int segnum = 0;
        if(pos == s.size() && segcnt  == 4){
            string track;
            for(int i = 0; i< 4; i++){
                track += to_string(segment[i]);
                if(i != 3) track += '.';
            }
            ans.emplace_back(track);
            //到达末尾return回去
            return;
        }
        else if((segcnt == 4 && pos != s.size()) || (pos == s.size() && segcnt != 4)){
            return;
        }
        if(s[pos] == '0'){
            segment[segcnt] = 0;
            backtrack(s, pos + 1, segcnt + 1);
            //撤销选择的方法是return
            return;
        }
        for(int i  = pos; i<s.size(); i++){
            segnum  = segnum*10 + (s[i] - '0');
            if(segnum > 0 && segnum <= 0xFF){
                segment[segcnt] = segnum;
                backtrack(s, i+1, segcnt + 1);
            }
            else{
                return;
            }
        }

    }
    vector<string> restoreIpAddresses(string s) {
        segment.resize(4);
        backtrack(s, 0, 0);
        return ans;
    }
};

三、Flood Fill(DFS)

例题10:单词搜索

分析

本题说是回溯,其实很像DFS的方法,参考:

DFS 算法解决岛屿题目

代码

class Solution {
public:
    int m, n;
    bool backtrack(vector<vector<char>>& board, string word, int pos, int i, int j){
        if(i >= m || j >= n || i<0 || j<0 || board[i][j] != word[pos]){
            return false;
        }
        if(pos == word.size()-1){
            return true;
        }
        bool res;
        board[i][j] = 0;
        res = backtrack(board, word, pos+1, i+1, j) || backtrack(board, word, pos+1, i-1, j)||
        backtrack(board, word, pos+1, i, j+1) || backtrack(board, word, pos+1, i, j-1);
        board[i][j] = word[pos];
        return res;
    }
    bool exist(vector<vector<char>>& board, string word) {
        m = board.size();n = board[0].size();
        for(int i = 0; i< m; i++){
            for(int j = 0; j<n; j++){
                if(backtrack(board, word, 0, i, j))
                    return true;
            }
        }
        return false;
    }
};

例题11:被围绕的区域

DFS 算法解决岛屿题目 中有详解

例题12:岛屿数量

DFS 算法解决岛屿题目 中有详解

例题13:​​​​​​​图像渲染

分析

dfs模板:越界返回

                访问返回

                非目标返回

                dfs递归

代码

class Solution {
public:
    int m, n;
    int samecolor;
    void dfs(vector<vector<int>>& image, int i, int j, int color){
        if(i>=m || j>= n|| i<0 || j<0){
            return;
        }
        if(image[i][j] == color){
            return;
        }
        if(image[i][j] != samecolor){
            return;
        }
        image[i][j] = color;
        dfs(image, i+1, j, color);
        dfs(image, i, j+1, color);
        dfs(image, i, j-1, color);
        dfs(image, i-1, j, color);
    }
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        m = image.size(); n = image[0].size();
        samecolor = image[sr][sc];
        dfs(image, sr, sc, color);
        return image;
    }
};

 

三、字符串中的回溯问题

例题14:电话号码的字母组合

代码

class Solution {
public:
    vector<string> ans;
    string track;
    vector<string>array;
    void backtrack(string digits, int pos){
        if(track.size() == digits.size()){
            ans.emplace_back(track);
            return;
        }
        //根据数字算出其对应的选择有哪些
        for(char c : array[digits[pos]-'0']){
            track += c;
            backtrack(digits, pos+1);
            track.erase(track.end()-1);
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size() == 0){
            return ans;
        }
        array.resize(10);
        for(int i = 0; i<18; i++){
            array[i/3+2] += i+'a';
        }
        array[7] += 's'; array[8] = "tuv"; array[9] = "wxyz";
        backtrack(digits, 0);
        return ans;
    }
};

改良:这里用hashmap存储选择数组比较好

class Solution {
public:
    vector<string> ans;
    string track;
    unordered_map<char, string> array;
    void backtrack(string digits, int pos){
        if(track.size() == digits.size()){
            ans.emplace_back(track);
            return;
        }
        for(char c : array[digits[pos]]){
            track += c;
            backtrack(digits, pos+1);
            track.erase(track.end()-1);
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size() == 0){
            return ans;
        }
        array = {
            {'2', "abc"},
            {'3', "def"},
            {'4', "ghi"},
            {'5', "jkl"},
            {'6', "mno"},
            {'7', "pqrs"},
            {'8', "tuv"},
            {'9', "wxyz"}
        };
        backtrack(digits, 0);
        return ans;
    }
};

 

例题15:字母大小写全排列

代码

class Solution {
public:
    vector<string> ans;
    string track;
    string choice(char c){
        string ret;
        ret += c;
        if(c >= 'a' && c <= 'z'){
            ret += toupper(c);
        }
        if(c >= 'A' && c <= 'Z'){
            ret += tolower(c);
        }
        return ret;
    }
    void backtrack(string s, int pos){
        if(track.size() == s.size()){
            ans.emplace_back(track);
            return;
        }
        //做选择
        for(char c : choice(s[pos])){
            track += c;
            backtrack(s, pos+1);
            track.erase(track.end()-1);
        }
    }
    vector<string> letterCasePermutation(string s) {
        backtrack(s, 0);
        return ans;
    }
};

例题16:括号生成

分析

代码

三、游戏问题

例题17:N 皇后

分析

代码

例题18:解数独

分析

代码

例题19:​​​​​​​祖玛游戏

分析

代码

例题20:​​​​​​​扫雷游戏

分析

代码

四、总结

i)全排列解决方法:

用used数组来选取没有选过的元素

ii)元素可重的通用解决方法

排序 + used 数组,连续相同的元素只能按顺序取,不能前面的没取取后面的。

iii)组合/子集(非排列)解决方法

backtrack传start进去,控制取元素的顺序,避免重复访问。

可重复选:递归的时候传 i

不可重复选:递归的时候传 i + 1

比如,[1, 2, 3],固定一的时候[1,3]取走了,下一次固定3,如果不控制顺序,还会取到[3,1],这样按组合的逻辑来说就是重复的。

iii)只要从树的角度思考,这些问题看似复杂多变,实则改改 base case 就能解决

iv)vector<char>转化为string:直接string str(v.begin(), v.end());

v)string删去最后一个元素: string.erase(string.end()-1);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/573819.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

操作steam搬砖有哪些风险?你有中招吗?揭秘有没有规避技巧?

一、关于steam账号的地区问题&#xff1a; steam账号地区不要频繁的去更换&#xff0c;这样很容易导致让账号红信不能操作使用。 二、关于steam账号的充值问题&#xff1a; 一定要充值正规的礼品卡图&#xff0c;否则遇到黑卡分分钟让你的账号红锁&#xff0c;从而造成账号里…

Nginx下载安装,什么是nginx,什么是反向代理,Windows下、linux下安装nginx(保姆级教程)

文章目录 一、Nginx简介为什么要使用NginxNginx的特点Nginx的相关概念正向代理反向代理动静分离负载均衡 二、Nginx安装1. Windows安装2. Linux安装 一、Nginx简介 Nginx 是一个高性能的 HTTP&#xff08;静态资源服务器&#xff09; 和 反向代理 Web 服务器。 为什么要使用N…

MySQL锁详解

之前的博客给小伙伴们分享了java中的锁&#xff0c;今天我们一起来看看mysql中有什么锁吧 一、图示 二、粒度分类 2.1、全局锁&#xff1a; 什么是全局锁&#xff1f; MySQL的锁定主要分为全局锁、表锁和行锁。现在我们来看看MySQL全局锁。 MySQL全局锁是针对整个数据库的锁…

FreeRTOS之列表

1.FreeRTOS的列表和列表项十分重要。列表类相当于链表&#xff0c;列表项则相当于链表中的节点。列表项的地址是非连续的&#xff0c;列表项的数量可随时修改。在OS中的任务状态和数量会发生改变&#xff0c;因此使用列表可以很好的满足需求。 列表和列表项的相关定义与操作函…

网工交换基础——生成树协议(01)

一、生成树的技术概述 1、技术背景 二层交换机网络的冗余性导致出现二层环路&#xff1a; 人为因素导致的二层环路问题&#xff1a; 二层环路带来的网络问题&#xff1a; 生成树协议的概念&#xff1a; STP(Spanning Tree Protocol)是生成树协议的英文缩写。该协议可应用于在网…

vue3 -- 项目使用自定义字体font-family

在Vue 3项目中使用自定义字体(font-family)的方法与在普通的HTML/CSS项目中类似。可以按照以下步骤进行操作: 引入字体文件: 首先,确保你的字体文件(通常是.woff、.woff2、.ttf等格式)位于项目中的某个目录下,比如src/assets/font/。 在全局样式中定义字体: 在你的全局…

智慧健康旅居养老产业,做智慧旅居养老服务的公司

随着社会的进步和科技的飞速发展&#xff0c;传统的养老模式已经无法满足 现代老年人的多元化 需求。智慧健康旅居养老产业应运而生&#xff0c;成为了一种新型的养老模式&#xff0c;旨在为老年人提供更加舒适、便捷、安全的养老生活。随着社会的进步和人口老龄化趋势的加剧&a…

pytest数据驱动DDT

常见的DDT技术 数据结构&#xff1a; 列表、字典、json串 文件&#xff1a; txt、csv、xcel 数据库&#xff1a; 数据库链接 数据库提取 参数化&#xff1a; pytest.mark.parametrize() pytest.fixture() D…

【课程发布】软考高项目十大管理ITTO宫殿记忆法新版第四版正式发布

软考高项十大管理ITTO宫殿记忆法视频课程&#xff1a; 平台&#xff1a;荔枝微课 连接&#xff1a;十方教育 各位软考高级信息系统项目管理师考生好&#xff0c;新版第四版十大管理ITTO宫殿记忆法视频课程终于发布了&#xff0c;之前苦等的考生终于迎来了救星&#xff0c;再也…

OAuth2、JWT

文章目录 OAuth2JWT OAuth2 官网&#xff1a; https://oauth.net/2/ 在 RFC 6749 中说明 1、资源所有者 resource owner&#xff0c; 如 github 用户 2、客户端/第三方应用 client&#xff0c; 如 支持github 登录的 csdn 3、资源服务器 resource server&#xff0c; 如 4、授…

【C/C++笔试练习】OSI分层模型、源端口和目的端口、网段地址、SNMP、状态码、tcp报文、域名解析、HTTP协议、计算机网络、美国节日、分解因数

文章目录 C/C笔试练习选择部分&#xff08;1&#xff09;OSI分层模型&#xff08;2&#xff09;源端口和目的端口&#xff08;3&#xff09;网段地址&#xff08;4&#xff09;SNMP&#xff08;5&#xff09;状态码&#xff08;6&#xff09;tcp报文&#xff08;7&#xff09;域…

GRASSHOPPER电池Expression

Grasshopper中如果要实现简单的条件if语句的效果&#xff0c;可以使用电池Expression。 举例&#xff1a;获取两个数的差值&#xff0c;永远用大数减去小数

Geoserver中点击切片图层报错问题

最近想试试wmts&#xff0c;其中有一步需要用到切片图层 但是点击页面老是报错&#xff0c; 于是乎想断点&#xff0c;可惜代码太复杂 弃了&#xff0c;所以想重新部署一下新版本&#xff0c;结果还是报错&#xff0c;想着可能tomcat有缓存吧&#xff0c;在换个tomcat还是报错…

《QT实用小工具·四十二》圆形发光图像

1、概述 源码放在文章末尾 该项目实现了图像的发光效果&#xff0c;特别适合做头像&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; import QtQuick 2.7 import QtGraphicalEffects 1.12Item {id: rootwidth: 80height: 80property int ra…

【JVM】java内存区域

目录 一、运行时数据区域 1、方法区 2、堆 3、虚拟机栈 4、本地方法栈 5、程序计数器 6、运行时常量池 二、HotSpot虚拟机的对象 1、对象的创建 指针碰撞&#xff1a; 空闲列表&#xff1a; 2、对象的内存布局 对象头 实例数据 对齐填充 3、对象的访问定位 句…

一种基于 RFID 技术的养老院人员定位解决方案

在今日的中国社会结构老龄化日趋增长&#xff0c;带来了一系列的社会问题。社会老龄化、高龄化、空巢化和病残化的迅速发展&#xff0c;将使得越来越多的老人住进养老院。养老院主要为老人提供集体居住&#xff0c;并具有相对完整的配套服务设施。养老院管理的最终目的就是为老…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Combo Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Combo Box的使用及说明 文章编号&#xff…

张大哥笔记:普通人如何搞钱?

追求短期利润和长期价值相结合的创业方式&#xff0c;是我一直倡导的。短期内寻求盈利的项目进行快速变现&#xff0c;比如各种小风口项目&#xff0c;而长期项目则需保持数年乃至10年不变&#xff0c;比如自媒体内容输出&#xff0c;做有长期价值的事情&#xff01; 短期项目通…

详解MySQL C API 相关接口(大白话就是:MySQL的c语言怎么写)

文章目录 1、C API 官方文档2、初始化 MYSQL3、连接 MySQL设置连接字符集&#xff08;使得客户端编码方式匹配&#xff09; 4、下发 mysql 指令5、获取 mysql 查询结果(保存起来)获取行与列遍历存储结果 6、释放 MYSQL\_RES 对象7、关闭 MySQL 连接8、总结 1、C API 官方文档 …

Linux安装Docker的多版本PHP和多版本MySQL共存

1: 先安装docker 安装完后执行,权限设置 sudo usermod -aG docker $USER或者sudo usermod -aG docker kentrl#添加当前用户到Docker用户组中 sudo newgrp docker#更新用户组数据,必须执行否则无效 sudo systemctl restart docker 先看目录结构: 2:按照目录结构挂载磁盘,…