当前位置: 首页 > wzjs >正文

网站做的好搜狗推广登录入口

网站做的好,搜狗推广登录入口,性价比高的网站建设,后端开发和前端开发的区别更新时间:2025-04-09 算法题解目录汇总:算法刷题记录——题解目录汇总技术博客总目录:计算机技术系列博客——目录页 优先整理热门100及面试150,不定期持续更新,欢迎关注! 51. N 皇后 按照国际象棋的规则…

更新时间:2025-04-09

  • 算法题解目录汇总:算法刷题记录——题解目录汇总
  • 技术博客总目录:计算机技术系列博客——目录页

优先整理热门100及面试150,不定期持续更新,欢迎关注!


51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

N 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]

解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

提示:

  • 1 <= n <= 9

方法一:回溯法 + 剪枝优化(行列、对角线冲突检测)

通过回溯算法逐行放置皇后,使用三个辅助数组快速检测列、主对角线和副对角线的冲突,极大提升搜索效率。

代码实现(Java):

class Solution {public List<List<String>> solveNQueens(int n) {List<List<String>> res = new ArrayList<>();// 初始化标记数组:列、主对角线、副对角线boolean[] cols = new boolean[n];boolean[] mainDiag = new boolean[2 * n - 1]; // 主对角线(左上到右下)boolean[] subDiag = new boolean[2 * n - 1]; // 副对角线(右上到左下)int[] queens = new int[n]; // 记录每行皇后的列位置backtrack(0, queens, cols, mainDiag, subDiag, res, n);return res;}private void backtrack(int row, int[] queens, boolean[] cols,boolean[] mainDiag, boolean[] subDiag,List<List<String>> res, int n) {if (row == n) {res.add(generateBoard(queens, n));return;}for (int col = 0; col < n; col++) {// 计算主、副对角线索引int mainIdx = row - col + n - 1;int subIdx = row + col;// 冲突检测if (cols[col] || mainDiag[mainIdx] || subDiag[subIdx]) continue;// 放置皇后并标记cols[col] = true;mainDiag[mainIdx] = true;subDiag[subIdx] = true;queens[row] = col;// 递归处理下一行backtrack(row + 1, queens, cols, mainDiag, subDiag, res, n);// 回溯撤销标记cols[col] = false;mainDiag[mainIdx] = false;subDiag[subIdx] = false;}}// 根据queens数组生成棋盘private List<String> generateBoard(int[] queens, int n) {List<String> board = new ArrayList<>();for (int i = 0; i < n; i++) {char[] row = new char[n];Arrays.fill(row, '.');row[queens[i]] = 'Q';board.add(new String(row));}return board;}
}

方法二:经典回溯法(逐行检查冲突)

通过逐行放置皇后并检查与之前行的冲突,逻辑更直观但效率略低,适合理解回溯基本原理。

代码实现(Java):

class Solution {public List<List<String>> solveNQueens(int n) {List<List<String>> res = new ArrayList<>();backtrack(0, new int[n], res, n);return res;}private void backtrack(int row, int[] queens, List<List<String>> res, int n) {if (row == n) {res.add(generateBoard(queens, n));return;}for (int col = 0; col < n; col++) {if (isValid(row, col, queens)) {queens[row] = col;backtrack(row + 1, queens, res, n);}}}// 检查当前位置是否合法private boolean isValid(int row, int col, int[] queens) {for (int i = 0; i < row; i++) {int prevCol = queens[i];if (prevCol == col || Math.abs(row - i) == Math.abs(col - prevCol)) {return false;}}return true;}private List<String> generateBoard(int[] queens, int n) {List<String> board = new ArrayList<>();for (int i = 0; i < n; i++) {char[] arr = new char[n];Arrays.fill(arr, '.');arr[queens[i]] = 'Q';board.add(new String(arr));}return board;}
}

复杂度分析

方法时间复杂度空间复杂度特点
方法一O(n!)O(n)快速冲突检测,适合n较大时使用
方法二O(n! * n)O(n)逻辑直观,适合理解回溯原理

核心思路:

  1. 回溯框架:逐行放置皇后,每行尝试所有可能列。
  2. 冲突检测
    • 方法一:通过三个标记数组实现O(1)时间检测列、主/副对角线冲突。
    • 方法二:通过遍历已放置皇后实现O(n)时间检测。

优化点:

  • 使用行列、对角线标记数组将冲突检测复杂度从O(n)降为O(1)
  • 通过queens数组记录每行放置位置,避免二维数组操作。

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6

解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4

方法一:Kadane算法(动态规划)

维护两个变量:currentMax(当前子数组的最大和)和 maxSum(全局最大和)。遍历数组时,动态决定是扩展当前子数组还是以当前元素重新开始子数组。每次更新 currentMaxmaxSum

代码实现(Java):

public class Solution {public int maxSubArray(int[] nums) {int maxSum = nums[0];int currentMax = nums[0];for (int i = 1; i < nums.length; i++) {currentMax = Math.max(nums[i], currentMax + nums[i]);maxSum = Math.max(maxSum, currentMax);}return maxSum;}
}

复杂度分析:

  • 时间复杂度O(n),每个元素遍历一次。
  • 空间复杂度O(1),仅用常数变量。

方法二:分治法

将数组递归分为左右两部分,最大子数组和可能出现在左半部分、右半部分或跨越中点的情况。合并结果时,计算三种情况的最大值。

代码实现(Java):

public class Solution {static class Result {int maxSum;int leftMax;int rightMax;int total;Result(int maxSum, int leftMax, int rightMax, int total) {this.maxSum = maxSum;this.leftMax = leftMax;this.rightMax = rightMax;this.total = total;}}public int maxSubArray(int[] nums) {return divideAndConquer(nums, 0, nums.length - 1).maxSum;}private Result divideAndConquer(int[] nums, int left, int right) {if (left == right) {return new Result(nums[left], nums[left], nums[left], nums[left]);}int mid = left + (right - left) / 2;Result leftResult = divideAndConquer(nums, left, mid);Result rightResult = divideAndConquer(nums, mid + 1, right);int crossSum = leftResult.rightMax + rightResult.leftMax;int maxSum = Math.max(Math.max(leftResult.maxSum, rightResult.maxSum), crossSum);int newLeftMax = Math.max(leftResult.leftMax, leftResult.total + rightResult.leftMax);int newRightMax = Math.max(rightResult.rightMax, rightResult.total + leftResult.rightMax);int total = leftResult.total + rightResult.total;return new Result(maxSum, newLeftMax, newRightMax, total);}
}

复杂度分析:

  • 时间复杂度O(n log n),递归深度为 log n,每层处理 O(n) 数据。
  • 空间复杂度O(log n),递归调用栈的深度。

方法三:前缀和优化

维护前缀和 prefixSum 和最小前缀和 minPrefix。每次计算当前前缀和与最小前缀的差值,更新最大子数组和。通过跟踪最小前缀,确保每次计算的是最大差值。

代码实现(Java):

public class Solution {public int maxSubArray(int[] nums) {int maxSum = Integer.MIN_VALUE;int prefixSum = 0;int minPrefix = 0;for (int num : nums) {prefixSum += num;maxSum = Math.max(maxSum, prefixSum - minPrefix);minPrefix = Math.min(minPrefix, prefixSum);}return maxSum;}
}

复杂度分析:

  • 时间复杂度O(n),单次遍历数组。
  • 空间复杂度O(1),仅用常数变量。

55. 跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false

示例 1:

输入:nums = [2,3,1,1,4]
输出:true

解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false

解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:

  • 1 <= nums.length <= 10^4
  • 0 <= nums[i] <= 10^5

方法一:正向贪心(最优解法)

维护一个变量 maxReach 表示当前能到达的最远位置。遍历数组时,如果当前位置 i 在可达范围内,则更新 maxReach。若 maxReach 覆盖最后一个位置,直接返回 true;若遍历中发现 i 超过 maxReach,说明无法到达后续位置,返回 false

代码实现(Java):

public class Solution {public boolean canJump(int[] nums) {int maxReach = 0;int n = nums.length;for (int i = 0; i < n; i++) {if (i > maxReach) return false; // 无法到达当前点maxReach = Math.max(maxReach, i + nums[i]);if (maxReach >= n - 1) return true; // 提前终止}return true;}
}

复杂度分析:

  • 时间复杂度O(n),每个元素遍历一次。
  • 空间复杂度O(1),仅用常数变量。

方法二:逆向贪心

从后向前遍历,维护变量 lastPos 表示需要到达的位置。若当前点 i 能跳到 lastPos,则更新 lastPosi。最终判断 lastPos 是否为起点。

代码实现(Java):

public class Solution {public boolean canJump(int[] nums) {int lastPos = nums.length - 1;for (int i = nums.length - 2; i >= 0; i--) {if (i + nums[i] >= lastPos) { // 能到达目标点lastPos = i; // 更新目标点为当前点}}return lastPos == 0;}
}

复杂度分析:

  • 时间复杂度O(n),遍历整个数组一次。
  • 空间复杂度O(1),仅用常数变量。

方法三:动态规划(仅理论,超时不可用)

dp[i] 记录位置 i 是否可达。对每个可达的点 i,标记后续可达的位置。但时间复杂度为 O(n²),无法处理大输入。

代码实现(Java):

public class Solution {public boolean canJump(int[] nums) {int n = nums.length;boolean[] dp = new boolean[n];dp[0] = true;for (int i = 0; i < n; i++) {if (!dp[i]) continue;int reach = Math.min(i + nums[i], n - 1);for (int j = i + 1; j <= reach; j++) {dp[j] = true;}}return dp[n - 1];}
}

复杂度分析:

  • 时间复杂度O(n²),最坏情况下每个点需更新大量后续点。
  • 空间复杂度O(n),需要维护布尔数组。

56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]

解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]。

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]

解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

  • 1 <= intervals.length <= 10^4
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 10^4

方法一:排序后合并

  1. 排序:将所有区间按起始点升序排列。这使得相邻的区间若有重叠必然连续出现。
  2. 合并:遍历排序后的区间,逐个检查当前区间是否与结果列表最后一个区间重叠。若重叠则合并,否则直接加入结果列表。

代码实现(Java):

public class Solution {public int[][] merge(int[][] intervals) {if (intervals.length <= 1) {return intervals;}// 按区间起始点排序Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));List<int[]> merged = new ArrayList<>();merged.add(intervals[0]);for (int i = 1; i < intervals.length; i++) {int[] last = merged.get(merged.size() - 1);int[] current = intervals[i];if (current[0] <= last[1]) {// 合并区间,更新结束点last[1] = Math.max(last[1], current[1]);} else {// 无重叠,直接添加merged.add(current);}}return merged.toArray(new int[merged.size()][]);}
}

复杂度分析:

  • 时间复杂度O(n log n),排序耗时 O(n log n),遍历合并耗时 O(n)
  • 空间复杂度O(n),存储合并后的结果。

方法二:扫描线算法(理论补充)

  1. 事件标记:将每个区间的起点和终点标记为事件(如起点为+1,终点为-1)。
  2. 排序事件:按坐标排序所有事件点,若坐标相同则起点事件优先。
  3. 扫描合并:遍历事件点,统计当前覆盖的区间层数。当区间层数从0变为1时记录起点,当层数变为0时记录终点,完成一个合并后的区间。

伪代码:

1. 将区间转换为事件点(如 [start, +1], [end, -1])。
2. 按坐标排序事件点,坐标相同时起点优先。
3. 遍历事件点,统计当前覆盖层数:- 当层数从0→1时,记录起点。- 当层数从1→0时,记录终点,并生成合并后的区间。

复杂度分析:

  • 时间复杂度O(n log n),事件排序耗时 O(n log n)
  • 空间复杂度O(n),存储事件点。

声明

  1. 本文版权归 CSDN 用户 Allen Wurlitzer 所有,遵循CC-BY-SA协议发布,转载请注明出处。
  2. 本文题目来源 力扣-LeetCode ,著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
http://www.dtcms.com/wzjs/39066.html

相关文章:

  • 宜昌广告制作公司桂林网站优化
  • 请人做游戏的网站网络营销推广软件
  • 培训学校网站建设网站优化外包公司
  • 网站标题logo修改代码阿亮seo技术
  • 免费网站建设软件有哪些百度24小时人工电话
  • 网站备案资料修改泉州百度竞价推广
  • 网站别人给我做的备案 我能更改吗专业seo培训
  • 青园网站建设企业seo排名哪家好
  • 企业网站分析报告网站收录一键提交
  • asp网站模板安装seo外包 靠谱
  • 公司做网站还是做阿里好呢郑州seo网络营销
  • 免费网站模板mbxzbseo关键词首页排名代发
  • 卦神岭做网站市场营销四大基本策略
  • 织梦网站+当前位置限制宽度旺道seo
  • 做外贸的要有自己的网站吗整站seo教程
  • 可靠的手机网站建设优化搜索曝光次数的方法
  • 网站建设与维护是什么意思自己如何注册网站
  • 手机网页设计公司知名的搜索引擎优化
  • 电子商城网站设计公司哪个好无锡整站百度快照优化
  • 类似中企动力的做网站的清远今日头条新闻
  • 如何做一个公司网站济南seo整站优化厂家
  • ...温岭做网站网站制作和推广
  • 建设局和住建局区别武汉百度推广seo
  • 网站的用户登录一般怎么做的广州市口碑seo推广
  • 做网站能赚钱吗表情包拉新推广平台有哪些
  • 乌鲁木齐网站建设外链平台
  • wordpress域名更换插件简述seo的基本步骤
  • 朝阳网站建设是什么网站如何优化排名
  • 做公司网站需要注意什么网站换友链平台
  • 有没有做宠物的网站网络营销公司热线电话