Javascript算法——回溯算法(子集和全排列问题)

news2025/1/7 3:57:26

子集问题

在这里插入图片描述

思路

如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点
在这里插入图片描述

78.子集

在这里插入图片描述
相比组合问题,此子集问题题目更为简单,收集的是树的所有节点,无递归条件约束

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var subsets = function(nums) {
    //确定参数和返回值类型
    let res=[],path=[];

    let backtracking=(index)=>{
        //子集问题需要收集树的所有节点
        //又是这错的!!!path.slice()浅拷贝
        res.push([...path]);
        for(let i=index;i<nums.length;i++){
            path.push(nums[i]);
            backtracking(i+1);
            path.pop();
        }
    }
    backtracking(0);
    return res;
    
};

90子集II 在这里插入图片描述

在上题基础上,此题由于数组中有相同元素,所以要进行去重操作,去重和组合问题中去重类似j>index&&nums[j]===nums[j-1]j>index表示是树层去重index为此层的开始索引切记别写成j>0!

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var subsetsWithDup = function(nums) {
     //去重先需要排序
     nums.sort((a,b)=>a-b);
     //确定参数和返回值
     let res=[],path=[];
     let backtracking=(index)=>{
        res.push(path.slice());
        if(index>=nums.length)return;
        for(let j=index;j<nums.length;j++){
            let item=nums[j];
            //去重操作 
            //j>index表示“当层”后面的值是否与此值相同!!!!
            if(j>index&&nums[j]===nums[j-1]){
                continue;
            }
            path.push(item);
            backtracking(j+1);
            path.pop();
        }
     }
    backtracking(0);
    return res; 
    
};

递增子序列

在这里插入图片描述
此题,由题意知子序列长度至少为2,递归结束条件收集结果时需要进行约束。题目的另外一个核心就是递增要保证后续加进的元素大于数组的最后一个元素path.length>0&&item<path[path.length-1]不满足条件直接跳过continue,而不是在push方法加if条件!

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var findSubsequences = function(nums) {
    //排序
    // nums.sort((a,b)=>a-b);
    //定义参数和返回值
    let res=[],path=[];
    let backtracking=(index)=>{
        //至少有两个元素
        if(path.length>=2){
            res.push(path.slice());
        }
        let uset=[];
        for(let j=index;j<nums.length;j++){
            let item=nums[j];
            //树层去重|保证大于数组path最后一个元素
            //(位置应该放在此,不满足直接跳过!!而不是在push方法前加一个,这样仍会加入[4,4])
            if(j>index&&nums[j]===nums[j-1]||(path.length>0&&item<path[path.length-1])){
                continue;
            }
            //加入元素
            path.push(item);
            backtracking(j+1);
            path.pop();
        }
    }
    backtracking(0);
    return res;
};

子集问题详解

子集问题 是经典的回溯问题之一,它的目标是从一个给定的集合中生成所有的子集。具体来说,给定一个整数集合 nums,你需要找出所有的子集(包括空集和 nums 本身)。

我们通过 回溯 来实现这个问题。回溯算法的基本思想是探索所有可能的组合路径,在每一步做出选择后继续深入探索,如果某个路径不符合要求或已经完成,就返回上一步,尝试其他路径。

题目描述

给定一个整数数组 nums,返回该数组所有可能的子集(幂集)。返回的子集中,子集的元素可以按任意顺序排列。

示例:
输入: nums = [1, 2, 3]
输出: [
  [1],
  [2],
  [3],
  [1,2],
  [1,3],
  [2,3],
  [1,2,3],
  []
]

回溯算法的思路

  1. 路径选择:每一层递归都可以选择当前元素在当前子集中出现或不出现。
  2. 状态回溯:如果当前选择了某个元素进入子集,进入下一层递归后会回退,尝试不选择当前元素,继续递归。
  3. 终止条件:递归的终止条件可以是遍历完所有元素。

解法步骤

  1. 初始化一个结果数组 res,用于存储最终的子集。
  2. 定义一个递归函数,该函数接受当前的索引 start 和一个当前子集 current,每次递归调用时向结果数组添加当前子集。
  3. 从当前索引开始,尝试将每个元素加入子集并递归。递归完成后,从当前子集中移除最后一个元素,回溯到上一步,尝试不加入该元素。

回溯算法实现

function subsets(nums) {
    let res = [];  // 存储所有子集
    let current = [];  // 当前子集
    let n = nums.length;

    // 回溯函数
    function backtrack(start) {
        // 将当前子集添加到结果数组
        res.push([...current]);
        
        // 从当前起始点开始遍历每个元素
        for (let i = start; i < n; i++) {
            // 选择当前元素
            current.push(nums[i]);
            // 递归调用,i + 1 确保每个元素只被选一次
            backtrack(i + 1);
            // 回溯,撤销选择
            current.pop();
        }
    }

    // 从索引 0 开始回溯
    backtrack(0);
    
    return res;
}

// 示例
console.log(subsets([1, 2, 3]));

代码分析

  • res.push([...current]);:每次将当前子集 current 的一个副本加入结果集。需要使用 [...]slice() 来避免引用共享问题。
  • current.push(nums[i]);:选择当前元素,将其加入当前子集。
  • backtrack(i + 1);:递归进入下一个元素,i + 1 确保每个元素只会被选择一次。
  • current.pop();:回溯时,撤销上一步的选择,尝试下一个选择。

时间复杂度

  • 递归的深度是 n,即数组的长度。
  • 每一层递归有 2 种选择(选或不选),因此总的子集数是 2^n
  • 因此,时间复杂度是 O(2^n),其中 n 是输入数组的长度。

空间复杂度

  • 递归栈的深度为 n,因此空间复杂度是 O(n)
  • 结果存储所有的子集,子集的总数为 2^n,每个子集最多有 n 个元素。所以空间复杂度为 O(2^n)

优化

  • 对于此类问题,回溯算法已经是最优解法,因为要枚举所有子集,最小复杂度也必须是 O(2^n)
  • 没有进一步的优化空间,除非能通过其他算法技巧(如动态规划)来处理更特殊的情况,但通常回溯是比较直观和易于实现的解决方法。

总结

子集问题的回溯解法通过递归的方式枚举所有可能的子集。每个决策点有 2 种选择:选或不选当前元素,通过递归深入到下一个元素,直到遍历完所有元素后返回。回溯算法清晰地展示了递归思想和状态回溯的特性,是理解深度优先搜索(DFS)的一种非常好的实践。

全排列问题

在这里插入图片描述

思路

排列问题和顺序有关,每次遍历都是相同的数组且从索引为0开始,为了要标识哪些已经遍历完需要借助一个额外数组
在这里插入图片描述

46全排列

在这里插入图片描述
此题需借助一个额外的used数组标识哪些元素遍历完,且在回溯时又需标识其未访问 if(used[i])continue; used[i]=true; backtracking(nums,used);used[i]=false;

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function(nums) {
    //确定参数和返回值
    let res=[],path=[];
    let backtracking=(arr,used)=>{
        //收集的叶子结点(全排列)
        if(path.length===arr.length){
            res.push(path.slice());
            return;
        }
        //排列问题考虑顺序无需index作为参数
        for(let i=0;i<arr.length;i++){
            //used数组标识是否已经访问过
            if(used[i])continue;
            path.push(arr[i]);
            used[i]=true;
            backtracking(arr,used);
            path.pop();
            //回溯时别忘记重新标识此原始至未访问
            used[i]=false;
        }
    }
    backtracking(nums,[]);
    return res;
    
};

47 全排列II

在这里插入图片描述
此题在上面题目,多了一个树层去重的工作!知道是数层去重,怎样表示嘞?这样的话i>0&&nums[i]===nums[i-1]未考虑相同元素在不同层,结果不是树层去重!还需加一个条件!used[i-1]排除第一个元素和上一层元素相同情况,分析过程

 //上一层访问过,此层直接跳过
            if(used[i])continue;
            //树层去重,对于全排列,相比其他多了一个条件!used[i-1],标识此层的首个元素不去重
            //例如:1 1 2 树的第二层传的数组仍是 1 1 2但是第一个1标识遍历过(第一层就遍历)
            //因此本层元素应该就是1 2 ,要保证1无需做去重操作
            if(i>0&&!used[i-1]&&nums[i]===nums[i-1])continue
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function(nums) {
    let res=[],path=[],len=nums.length;
    let backtracking=(arr,used)=>{
        if(path.length===len){
            res.push(path.slice());
            return;
        }
        for(let i=0;i<len;i++){
            //上一层访问过,此层直接跳过
            if(used[i])continue;
            //树层去重,对于全排列,相比其他多了一个条件!used[i-1],标识此层的首个元素不去重
            //例如:1 1 2 树的第二层传的数组仍是 1 1 2但是第一个1标识遍历过(第一层就遍历)
            //因此本层元素应该就是1 2 ,要保证1无需做去重操作
            if(i>0&&!used[i-1]&&nums[i]===nums[i-1])continue
            path.push(nums[i]);
            used[i]=true;
            backtracking(arr,used);
            path.pop();
            used[i]=false;
        }
    }
    backtracking(nums,[]);
    return res;
    
};

全排列问题详解

全排列问题 是回溯算法中的经典问题之一,目标是生成给定集合中所有可能的排列。全排列问题通常会涉及到整数数组、字符数组等,要求我们生成所有不同的排列。

题目描述

给定一个没有重复数字的整数数组 nums,返回所有这些数字的 排列

示例:
输入: nums = [1, 2, 3]
输出: [
  [1, 2, 3],
  [1, 3, 2],
  [2, 1, 3],
  [2, 3, 1],
  [3, 1, 2],
  [3, 2, 1]
]

解法思路

全排列问题本质上是要通过递归来探索每个位置可能的选择。对每个位置的选择,递归地决定其他位置的元素,直到所有位置都填充完。

回溯算法的思路

  1. 路径选择:每次从当前未使用的元素中选择一个,加入到排列中。
  2. 状态回溯:每次递归时,选择了一个元素后,进入下一层递归。在递归结束后,要撤销选择,恢复状态。
  3. 终止条件:当当前排列的长度等于输入数组的长度时,说明排列完成,可以将其加入结果集。

解法步骤

  1. 初始化一个空的结果数组 res,用来存储所有排列。
  2. 使用一个辅助数组 current 来存储当前的排列。
  3. 使用一个 used 数组(或标志)来记录哪些元素已经被选择过,避免重复选择。
  4. 递归地选择每个元素,生成所有的排列,并通过回溯撤销选择。

回溯算法实现

function permute(nums) {
    let res = [];  // 存储所有排列
    let current = [];  // 当前排列
    let used = Array(nums.length).fill(false);  // 记录每个元素是否被使用过

    // 回溯函数
    function backtrack() {
        // 当排列的长度与数组长度相等时,表示当前排列已完成
        if (current.length === nums.length) {
            res.push([...current]);  // 将当前排列加入结果集
            return;
        }

        // 遍历所有元素
        for (let i = 0; i < nums.length; i++) {
            if (used[i]) continue;  // 如果元素已经被使用,跳过

            // 选择当前元素
            current.push(nums[i]);
            used[i] = true;

            // 递归进行下一步选择
            backtrack();

            // 回溯,撤销选择
            current.pop();
            used[i] = false;
        }
    }

    // 从索引 0 开始回溯
    backtrack();
    
    return res;
}

// 示例
console.log(permute([1, 2, 3]));

代码解析

  • used[i]:用来标记当前元素 nums[i] 是否已经被使用过,避免重复排列。
  • current.push(nums[i]):将当前元素加入 current 中,形成一个部分排列。
  • used[i] = true:标记当前元素已被使用。
  • backtrack():递归进行下一步选择。
  • current.pop():回溯时,撤销当前元素的选择。
  • used[i] = false:恢复状态,表示当前元素未被使用。

时间复杂度

  • 排列的个数:对于长度为 n 的数组,排列的总数为 n!(阶乘)。
  • 递归深度:递归的深度为 n,每一层都需要对当前选择进行遍历。
  • 因此,时间复杂度是 O(n!),其中 n 是数组的长度。

空间复杂度

  • 结果数组 res 存储所有排列,空间复杂度为 O(n * n!)
  • 递归栈的深度为 n,所以额外空间复杂度为 O(n)

优化

对于没有重复元素的排列,回溯算法已经是最优的解决方法。每次递归只考虑当前未被使用的元素,并在每一层递归时做出选择,这样可以避免生成重复的排列。

总结

全排列问题是回溯算法的经典应用,要求从一组元素中生成所有可能的排列。回溯的基本思想是逐步构建排列,遇到选择时递归深入,遍历完所有可能的选择后通过回溯撤销选择,返回上一步继续尝试其他选择。由于全排列问题要求生成所有可能的排列,时间复杂度通常为 O(n!),其中 n 为数组的大小。

子集问题&全排列问题常见题目

下面我将列出 子集问题排列问题 的各 5 个经典前端 JS 算法题目,并提供相应的代码解析。


子集问题(回溯)

1. 子集(Subsets)

题目描述:

给定一个整数数组 nums,返回所有可能的子集(幂集)。可以包括空集和数组本身。

// 示例
// 输入: nums = [1, 2, 3]
// 输出: [
//   [1],
//   [2],
//   [3],
//   [1,2],
//   [1,3],
//   [2,3],
//   [1,2,3],
//   []
// ]

解法解析:

使用回溯算法枚举所有子集。

function subsets(nums) {
    let res = [];
    let current = [];
    
    function backtrack(start) {
        res.push([...current]);  // 存储当前子集
        for (let i = start; i < nums.length; i++) {
            current.push(nums[i]);
            backtrack(i + 1);  // 从下一个位置继续选择
            current.pop();  // 回溯
        }
    }
    
    backtrack(0);
    return res;
}

console.log(subsets([1, 2, 3]));

时间复杂度O(2^n),其中 n 是数组长度。
空间复杂度O(n),递归栈的最大深度。


2. 子集 II(Subsets II)

题目描述:

给定一个整数数组 nums,其中可能包含重复元素,返回所有唯一的子集(幂集)。

// 示例
// 输入: nums = [1, 2, 2]
// 输出: [
//   [1],
//   [1, 2],
//   [1, 2, 2],
//   [2],
//   [2, 2],
//   []
// ]

解法解析:

为了避免重复子集,可以在每一层递归中对相同元素进行去重处理。通过 i > start 来跳过重复元素。

function subsetsWithDup(nums) {
    let res = [];
    nums.sort((a, b) => a - b);  // 排序,确保相同元素相邻
    let current = [];
    
    function backtrack(start) {
        res.push([...current]);
        for (let i = start; i < nums.length; i++) {
            if (i > start && nums[i] === nums[i - 1]) continue;  // 跳过重复元素
            current.push(nums[i]);
            backtrack(i + 1);
            current.pop();
        }
    }
    
    backtrack(0);
    return res;
}

console.log(subsetsWithDup([1, 2, 2]));

时间复杂度O(2^n),与子集数量相关。
空间复杂度O(n),递归栈的最大深度。


3. 组合(Combinations)

题目描述:

给定一个整数 n 和一个整数 k,返回 1 到 n 中所有可能的 k 个数的组合。

// 示例
// 输入: n = 4, k = 2
// 输出: [
//   [2, 4],
//   [3, 4],
//   [1, 2],
//   [1, 3],
//   [1, 4],
//   [2, 3]
// ]

解法解析:

可以用回溯法生成从 1 到 n 中选择 k 个数的组合。

function combine(n, k) {
    let res = [];
    let current = [];
    
    function backtrack(start) {
        if (current.length === k) {
            res.push([...current]);
            return;
        }
        
        for (let i = start; i <= n; i++) {
            current.push(i);
            backtrack(i + 1);  // 下一个选择
            current.pop();  // 回溯
        }
    }
    
    backtrack(1);
    return res;
}

console.log(combine(4, 2));

时间复杂度O(C(n, k)),即从 n 中选择 k 个元素的组合数。
空间复杂度O(k),递归栈的最大深度。


4. 组合总和(Combination Sum)

题目描述:

给定一个无重复的整数数组 candidates 和一个目标值 target,找出所有组合,使得这些组合的和为 target

// 示例
// 输入: candidates = [2, 3, 6, 7], target = 7
// 输出: [
//   [2, 2, 3],
//   [7]
// ]

解法解析:

使用回溯算法,允许重复选择相同的数字,因此每个数字可以多次选。

function combinationSum(candidates, target) {
    let res = [];
    let current = [];
    
    function backtrack(start, target) {
        if (target === 0) {
            res.push([...current]);
            return;
        }
        if (target < 0) return;
        
        for (let i = start; i < candidates.length; i++) {
            current.push(candidates[i]);
            backtrack(i, target - candidates[i]);  // 允许重复选当前元素
            current.pop();  // 回溯
        }
    }
    
    backtrack(0, target);
    return res;
}

console.log(combinationSum([2, 3, 6, 7], 7));

时间复杂度O(2^n),最坏情况下,需要遍历所有子集。
空间复杂度O(target),递归栈的最大深度。


5. 划分为若干子集(Partition to K Equal Sum Subsets)

题目描述:

给定一个整数数组 nums 和一个整数 k,判断能否将 nums 划分为 k 个非空子集,且每个子集的和相等。

// 示例
// 输入: nums = [4, 3, 2, 3, 5, 4, 1], k = 4
// 输出: true

解法解析:

使用回溯算法,尝试将数字分配到 k 个子集,确保每个子集的和相等。

function canPartitionKSubsets(nums, k) {
    const totalSum = nums.reduce((a, b) => a + b, 0);
    if (totalSum % k !== 0) return false;
    
    const target = totalSum / k;
    let visited = new Array(nums.length).fill(false);
    
    function backtrack(start, kLeft, currentSum) {
        if (kLeft === 0) return true;
        if (currentSum === target) return backtrack(0, kLeft - 1, 0);
        
        for (let i = start; i < nums.length; i++) {
            if (visited[i] || currentSum + nums[i] > target) continue;
            visited[i] = true;
            if (backtrack(i + 1, kLeft, currentSum + nums[i])) return true;
            visited[i] = false;
        }
        
        return false;
    }
    
    return backtrack(0, k, 0);
}

console.log(canPartitionKSubsets([4, 3, 2, 3, 5, 4, 1], 4));

时间复杂度O(2^n),最坏情况下需要遍历所有子集。
空间复杂度O(n),递归栈的最大深度。


排列问题(回溯)

1. 全排列(Permutations)

题目描述:

给定一个没有重复数字的整数数组 nums,返回所有这些数字的排列。

// 示例
// 输入: nums = [1, 2, 3]
// 输出: [
//   [1, 2, 3],
//   [1, 3, 2],
//   [2, 1, 3],
//   [2, 3, 1],
//   [3, 1, 2],
//   [3, 2, 1]
// ]

解法解析:

通过回溯算法生成所有排列。

function permute(nums) {
    let res = [];
    let current = [];
    let used = Array(nums.length).fill(false);

    function backtrack() {
        if (current.length === nums.length) {
            res.push([...current]);
            return;
        }

        for (let i = 0; i < nums.length; i++) {
            if (used[i]) continue;

            current.push(nums[i]);
            used[i]

 = true;
            backtrack();
            current.pop();
            used[i] = false;
        }
    }

    backtrack();
    return res;
}

console.log(permute([1, 2, 3]));

时间复杂度O(n!),生成所有排列。
空间复杂度O(n),递归栈深度。


2. 全排列 II(Permutations II)

题目描述:

给定一个包含重复数字的整数数组 nums,返回所有不重复的排列。

// 示例
// 输入: nums = [1, 1, 2]
// 输出: [
//   [1, 1, 2],
//   [1, 2, 1],
//   [2, 1, 1]
// ]

解法解析:

为避免重复排列,排序数组,确保相同的元素在递归时相邻,然后跳过重复的元素。

function permuteUnique(nums) {
    let res = [];
    nums.sort((a, b) => a - b);
    let current = [];
    let used = Array(nums.length).fill(false);

    function backtrack() {
        if (current.length === nums.length) {
            res.push([...current]);
            return;
        }

        for (let i = 0; i < nums.length; i++) {
            if (used[i] || (i > 0 && nums[i] === nums[i - 1] && !used[i - 1])) continue;
            current.push(nums[i]);
            used[i] = true;
            backtrack();
            current.pop();
            used[i] = false;
        }
    }

    backtrack();
    return res;
}

console.log(permuteUnique([1, 1, 2]));

时间复杂度O(n!),生成所有排列。
空间复杂度O(n),递归栈深度。


3. 组合总和 III(Combination Sum III)

题目描述:

找出所有 k 个数字的排列,使得它们的和为 n

// 示例
// 输入: k = 3, n = 7
// 输出: [[1, 2, 4]]

解法解析:

通过回溯,选择不同的数来组成和为 n 的排列。

function combinationSum3(k, n) {
    let res = [];
    let current = [];
    
    function backtrack(start, target) {
        if (current.length === k && target === 0) {
            res.push([...current]);
            return;
        }
        
        for (let i = start; i <= 9; i++) {
            current.push(i);
            backtrack(i + 1, target - i);  // 防止重复元素
            current.pop();
        }
    }
    
    backtrack(1, n);
    return res;
}

console.log(combinationSum3(3, 7));

时间复杂度O(C(n, k)),从 n 中选择 k 个元素的组合数。
空间复杂度O(k),递归栈的最大深度。


4. 排列组合(Permutations and Combinations)

题目描述:

给定一个整数数组 nums 和一个整数 k,返回所有长度为 k 的排列。

// 示例
// 输入: nums = [1, 2, 3], k = 2
// 输出: [
//   [1, 2],
//   [1, 3],
//   [2, 1],
//   [2, 3],
//   [3, 1],
//   [3, 2]
// ]

解法解析:

使用回溯法生成排列。每次递归时选择当前未选择的元素。

function permute(nums) {
    let res = [];
    let current = [];
    let used = Array(nums.length).fill(false);
    
    function backtrack() {
        if (current.length === nums.length) {
            res.push([...current]);
            return;
        }
        
        for (let i = 0; i < nums.length; i++) {
            if (used[i]) continue;

            current.push(nums[i]);
            used[i] = true;
            backtrack();
            current.pop();
            used[i] = false;
        }
    }
    
    backtrack();
    return res;
}

console.log(permute([1, 2, 3]));

5. 排列总和(Permutation Sum)

题目描述:

给定一个数组和一个目标值,找出所有可以排列成目标值的数字组合。

// 示例
// 输入: nums = [2, 3, 5], target = 8
// 输出: [[3, 5], [5, 3]]

解法解析:

通过回溯选取元素进行排列,直到找到和为 target 的组合。

function permutationSum(nums, target) {
    let res = [];
    let current = [];
    
    function backtrack(target) {
        if (target === 0) {
            res.push([...current]);
            return;
        }
        
        for (let i = 0; i < nums.length; i++) {
            if (target - nums[i] >= 0) {
                current.push(nums[i]);
                backtrack(target - nums[i]);
                current.pop();
            }
        }
    }
    
    backtrack(target);
    return res;
}

console.log(permutationSum([2, 3, 5], 8));

总结

这些题目都可以通过回溯算法来解决,关键是通过递归进行深度优先搜索,尽可能枚举出所有可能的情况。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全系统学习实验1:RDP远程登录配置

准备工作&#xff1a; 0、准备好虚拟机 1、服务器侧&#xff08;虚拟机Windows 2003-01&#xff09;IP地址&#xff1a; # 获得服务器的IP地址192.168.58.223 ipconfig /all2、客户端侧(虚拟机Win7 pte_czj)IP地址&#xff1a; # 客户端侧IP地址192.168.58.222 ipconfig /al…

SMMU软件指南之系统架构考虑

安全之安全(security)博客目录导读 目录 5.1 I/O 一致性 5.2 客户端设备 5.2.1 地址大小 5.2.2 缓存 5.3 PCIe 注意事项 5.3.1 点对点通信 5.3.2 No_snoop 5.3.3 ATS 5.4 StreamID 分配 5.5 MSI 本博客介绍与 SMMU 相关的一些系统架构注意事项。 5.1 I/O 一致性 如…

[网络安全]sqli-labs Less-3 解题详析

判断注入类型 GET1 and 11&#xff0c;回显如下&#xff1a;GET1 and 12&#xff1a;没有回显&#xff0c;说明该漏洞类型为GET型单引号字符型注入 判断注入点个数 GET1 order by 2 --&#xff0c;回显如下&#xff1a;由上图可知&#xff0c;sql语法中给$id加上了() 猜测后…

vulnhub Earth靶机

搭建靶机直接拖进来就行 1.扫描靶机IP arp-scan -l 2.信息收集 nmap -sS -A -T4 192.168.47.132 得到两个DNS; 在443端口处会让我们加https dirb https://earth.local/ dirb https://terratest.earth.local/ #页面下有三行数值 37090b59030f11060b0a1b4e0000000000004312170a…

AWS 申请证书、配置load balancer、配置域名

申请AWS证书 点击 request 申请完证书&#xff0c;AWS 会验证你对于域名的所有权&#xff0c;有两种方式&#xff0c;DSN 验证和邮箱验证。 这里说一下DSN 验证&#xff0c;上图中 Domains 中有CNAME name 和 CNAME value 。 在domain 网站中添加一个CNAME DSN 项&#xff0c;…

【WPF】 数据绑定机制之INotifyPropertyChanged

INotifyPropertyChanged 是 WPF 中的一个接口&#xff0c;用于实现 数据绑定 中的 属性更改通知。它的主要作用是&#xff0c;当对象的某个属性值发生更改时&#xff0c;通知绑定到该属性的 UI 控件更新其显示内容。 以下是有关 INotifyPropertyChanged 的详细信息和实现方法&…

基于Spring Boot的IT技术交流和分享平台的设计与实现源码

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的IT技术交流和分享平台的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于S…

齿轮缺陷检测数据集VOC+YOLO格式485张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;485 标注数量(xml文件个数)&#xff1a;485 标注数量(txt文件个数)&#xff1a;485 标注…

Airflow:HttpSensor实现API驱动数据流程

数据管道工作流通常依赖于api来访问、获取和处理来自外部系统的数据。为了处理这些场景&#xff0c;Apache Airflow提供了HttpSensor&#xff0c;这是一个内置的Sensor&#xff0c;用于监视HTTP请求的状态&#xff0c;并在满足指定条件时触发后续任务。在这篇博文中&#xff0c…

活动预告 | Microsoft Power Platform 在线技术公开课:实现业务流程自动化

课程介绍 参加“Microsoft Power Platform 在线技术公开课&#xff1a;实现业务流程自动化”活动&#xff0c;了解如何更高效地开展业务。参加我们举办的本次免费培训活动&#xff0c;了解如何借助 Microsoft AI Builder 和 Power Automate 优化工作流。结合使用这些工具可以帮…

【SpringBoot教程】搭建SpringBoot项目之编写pom.xml

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f44f;今天毛毛张分享的内容主要是Maven 中 pom 文件&#x1f195;&#xff0c;涵盖基本概念、标签属性、配置等内容 文章目录 1.前言&#x1f96d;2.项目基本…

职场常用Excel基础04-二维表转换

大家好&#xff0c;今天和大家一起分享一下excel的二维表转换相关内容~ 在Excel中&#xff0c;二维表&#xff08;也称为矩阵或表格&#xff09;是一种组织数据的方式&#xff0c;其中数据按照行和列的格式进行排列。然而&#xff0c;在实际的数据分析过程中&#xff0c;我们常…

ASA第六天笔记

Botnet Traffic Filter简介 1.僵死网络流量过滤特性是一个基于名誉的机制&#xff0c;用于阻止流量源自于或者去往已知的感染主机。 2.僵死网络流量过滤比较每一个连接中的源和目的IP地址。 动态SensorBase数据库&#xff0c;被Cisco动态更新。静态数据库&#xff0c;需要手动…

【ArcGISPro/GeoScenePro】检查多光谱影像的属性并优化其外观

数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 操作 其他数据 检查影像的属性 熟悉检查您正在使用的栅格属性非常重要。

MySQL图形化界面工具--DataGrip

之前介绍了在命令行进行操作&#xff0c;但是不够直观&#xff0c;本次介绍图形化界面工具–DataGrip。 安装DataGrip 官网链接&#xff1a;官网下载链接 常规的软件安装流程。 参考链接&#xff1a;DataGrip安装 使用DataGrip 添加数据源&#xff1a; 第一次使用最下面会…

企业微信——智能表格学习

智能表格 应用限制条件 获取 token https://developer.work.weixin.qq.com/document/10013#%E5%BC%80%E5%8F%91%E6%AD%A5%E9%AA%A4 开发步骤 你可以通过以下步骤&#xff0c;使用access_token来访问企业微信的接口。需要注意的是&#xff0c;所有的接口需使用Https协议、Js…

调试:用电脑开发移动端网页,然后用手机真机调试

一、背景 电脑开发移动端&#xff0c;然后想真机调试... 二、实现 2.1、电脑和手机链接相同局域网 2.2、pnpm run dev 启动项目 2.3、浏览器访问 localhost:3001/login 2.4、Windowsr 输入cmd&#xff0c;在cmd输入 ipconfig 2.5、浏览器访问 ip地址加/login 2.6、手机端…

华为ensp-BGP路由过滤

学习新思想&#xff0c;争做新青年&#xff0c;今天学习的是BGP路由过滤 实验目的&#xff1a; 掌握利用BGP路由属性AS_Path进行路由过滤的方法 掌握利用BGP路由属性Community进行路由过滤的方法 掌握利用BGP路由属性Next_Hop进行路由过滤的方法 实验内容&#xff1a; 本实…

【书籍连载】《软件测试架构实践与精准测试》| 有关软件测试模型的调查结果

各位软件领域的精英们&#xff0c;今天小编邀请你继续深入学习《软件测试架构实践与精准测试》。 《软件测试架构实践与精准测试》是作者李龙&#xff08;安畅检测首席技术专家&#xff09;基于软件测试“川模型”的著作。本书结合作者首次提出的软件测试新的模型“川模型”测试…

nginx学习之路-windows系统安装nginx

文章目录 1. 下载2. 启动3. 验证参考文档 1. 下载 官方下载地址&#xff1a;https://nginx.org/en/download.html 可以下载windows版本&#xff0c;如nginx-1.26.2.zip。解压后&#xff0c;加入系统变量。 2. 启动 可以使用命令行启动&#xff08;windows系统自带的cmd可能…