力扣第五十二题——N皇后II

news2024/10/2 20:32:45

内容介绍

n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。

示例 1:

输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:1

提示:

  • 1 <= n <= 9

完整代码

 struct hashTable {
    int key;
    UT_hash_handle hh;
};

struct hashTable* find(struct hashTable** hashtable, int ikey) {
    struct hashTable* tmp = NULL;
    HASH_FIND_INT(*hashtable, &ikey, tmp);
    return tmp;
}

void insert(struct hashTable** hashtable, int ikey) {
    struct hashTable* tmp = NULL;
    HASH_FIND_INT(*hashtable, &ikey, tmp);
    if (tmp == NULL) {
        tmp = malloc(sizeof(struct hashTable));
        tmp->key = ikey;
        HASH_ADD_INT(*hashtable, key, tmp);
    }
}

void erase(struct hashTable** hashtable, int ikey) {
    struct hashTable* tmp = NULL;
    HASH_FIND_INT(*hashtable, &ikey, tmp);
    if (tmp != NULL) {
        HASH_DEL(*hashtable, tmp);
        free(tmp);
    }
}

struct hashTable *columns, *diagonals1, *diagonals2;

int backtrack(int n, int row) {
    if (row == n) {
        return 1;
    } else {
        int count = 0;
        for (int i = 0; i < n; i++) {
            if (find(&columns, i) != NULL) {
                continue;
            }
            int diagonal1 = row - i;
            if (find(&diagonals1, diagonal1) != NULL) {
                continue;
            }
            int diagonal2 = row + i;
            if (find(&diagonals2, diagonal2) != NULL) {
                continue;
            }
            insert(&columns, i);
            insert(&diagonals1, diagonal1);
            insert(&diagonals2, diagonal2);
            count += backtrack(n, row + 1);
            erase(&columns, i);
            erase(&diagonals1, diagonal1);
            erase(&diagonals2, diagonal2);
        }
        return count;
    }
}

int totalNQueens(int n) {
    columns = diagonals1 = diagonals2 = NULL;
    return backtrack(n, 0);
}

思路详解

一、问题背景

N皇后问题是一个经典的计算机科学问题,要求在一个N×N的棋盘上放置N个皇后,使得它们互不攻击。也就是说,任何两个皇后都不能处于同一行、同一列或同一斜线上。

二、解题思路

  1. 表示棋盘

    • 使用哈希表来表示棋盘,其中每个键代表棋盘上的一列,值代表该列上皇后的位置。
  2. 回溯算法

    • 采用回溯法尝试在每一行放置一个皇后,并递归地检查后续行的放置情况。
  3. 冲突检测

    • 使用三个哈希表columnsdiagonals1diagonals2分别表示列和两个方向的斜线是否已被占用。
    • 在放置皇后时,检查新皇后是否与已放置的皇后冲突。

三、代码详解

  1. 哈希表定义
    • hashTable结构体包含一个整型键和哈希处理句柄。
struct hashTable {
    int key;
    UT_hash_handle hh;
};
  1. 查找操作
    • find函数用于在哈希表中查找指定键的值。
struct hashTable* find(struct hashTable** hashtable, int ikey) {
    struct hashTable* tmp = NULL;
    HASH_FIND_INT(*hashtable, &ikey, tmp);
    return tmp;
}
  1. 插入操作
    • insert函数用于在哈希表中插入一个新的键值对。
void insert(struct hashTable** hashtable, int ikey) {
    struct hashTable* tmp = NULL;
    HASH_FIND_INT(*hashtable, &ikey, tmp);
    if (tmp == NULL) {
        tmp = malloc(sizeof(struct hashTable));
        tmp->key = ikey;
        HASH_ADD_INT(*hashtable, key, tmp);
    }
}
  1. 删除操作
    • erase函数用于从哈希表中删除指定键的值。
void erase(struct hashTable** hashtable, int ikey) {
    struct hashTable* tmp = NULL;
    HASH_FIND_INT(*hashtable, &ikey, tmp);
    if (tmp != NULL) {
        HASH_DEL(*hashtable, tmp);
        free(tmp);
    }
}
  1. 回溯函数
    • backtrack函数是回溯算法的核心,它尝试在每一行放置一个皇后,并递归地检查后续行的放置情况。
int backtrack(int n, int row) {
    if (row == n) {
        return 1;
    } else {
        int count = 0;
        for (int i = 0; i < n; i++) {
            if (find(&columns, i) != NULL) {
                continue;
            }
            int diagonal1 = row - i;
            if (find(&diagonals1, diagonal1) != NULL) {
                continue;
            }
            int diagonal2 = row + i;
            if (find(&diagonals2, diagonal2) != NULL) {
                continue;
            }
            insert(&columns, i);
            insert(&diagonals1, diagonal1);
            insert(&diagonals2, diagonal2);
            count += backtrack(n, row + 1);
            erase(&columns, i);
            erase(&diagonals1, diagonal1);
            erase(&diagonals2, diagonal2);
        }
        return count;
    }
}
  1. 主函数
    • totalNQueens函数初始化哈希表,并调用backtrack函数开始回溯过程。
int totalNQueens(int n) {
    columns = diagonals1 = diagonals2 = NULL;
    return backtrack(n, 0);
}

知识点精炼

一、核心概念

  1. 回溯算法:一种通过尝试所有可能的情况来解决问题的算法,适用于组合问题。
  2. 哈希表:一种基于键值对的数据结构,用于存储和查找数据。
  3. 冲突检测:在放置皇后时,确保新放置的皇后不与已放置的皇后在同一列或同一斜线上。

二、知识点精炼

  1. 棋盘表示

    • 使用哈希表来表示棋盘,其中键表示列号,值表示在该列上皇后的位置。
  2. 冲突检测哈希表

    • 使用三个哈希表columnsdiagonals1diagonals2分别表示列和两个方向的斜线是否已被占用。
  3. 回溯函数

    • backtrack函数递归地尝试在每一行放置一个皇后,并在放置后进行冲突检测。
    • 如果所有皇后都成功放置,则返回1;否则,返回0。
  4. 递归终止条件

    • 当所有行都已放置皇后时,表示找到一个有效解决方案。
  5. 状态重置

    • 在回溯时,需要将当前行的皇后位置从哈希表中删除,以便尝试其他可能的放置。
  6. 解决方案计数

    • 记录找到的解决方案数量,并在回溯过程中累加。

三、性能优化

  • 空间优化:使用哈希表替代整型数组进行冲突检测,减少内存使用。
  • 时间优化:通过提前终止递归路径来减少不必要的计算。

四、实际应用

  • 组合问题:N皇后问题是一种典型的组合问题,其解法可以推广到其他类似的组合优化问题。
  • 算法竞赛:在算法竞赛中,掌握回溯算法对于解决组合类问题非常有帮助。

五、代码实现要点

  • 动态内存分配:在生成哈希表和解决方案计数时,需要动态分配内存。
  • 内存释放:在实际应用中,需要确保在适当的时候释放分配的内存,避免内存泄漏。

 其他解法方案

  1. 深度优先搜索(DFS)

    • 类似于回溯算法,DFS尝试所有可能的皇后放置位置,并在发现冲突时回溯。
    • 区别在于,DFS不使用哈希表来跟踪已放置的皇后,而是使用递归栈来回溯。
  2. 位运算

    • 使用位运算来替代哈希表进行冲突检测,可以减少内存使用并提高速度。
    • 位运算使用整型变量来表示列和斜线是否已被占用,通过位掩码(bitmask)来表示皇后的位置和攻击范围。
  3. 分治法

    • 将棋盘分为四部分,分别放置皇后,然后合并解决方案。
    • 适用于较小规模的问题,对于N皇后问题,分治法通常不如回溯法高效。
  4. 启发式算法

    • 例如遗传算法、模拟退火等,这些算法可以用来寻找N皇后问题的近似解,而不是精确解。
  5. 并行算法

    • 使用并行计算来加速N皇后问题的求解,尤其是当问题的规模非常大时。
  6. 动态规划

    • 动态规划通常用于解决最优解问题,对于N皇后问题,动态规划不是最优的选择,因为问题的解不是最优解。

每种解法都有其适用场景和优缺点。回溯算法因其简单性和效率而成为解决N皇后问题的常见方法。其他方法可能在特定情况下提供更优的性能,但通常需要更复杂的实现和更长的学习曲线。在选择解法时,应根据具体问题和性能要求来决定。

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

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

相关文章

C# ADC数据波形显示

1. 串口显示汉字的程序设计 using System; using System.Text; using System.Windows.Forms;namespace 汉字显示 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private byte[] StringToBytes(string TheString){Encoding FromEncoding Encodin…

鼻咽癌综述

小罗碎碎念 本期推文主题&#xff1a;鼻咽癌综述 这篇文章提供了一个全面的综述&#xff0c;探讨了鼻咽癌&#xff08;NPC&#xff09;的关键研究进展&#xff0c;包括病理机制、治疗、筛查和生物标志物的发展。 文章首先强调了NPC在特定地理区域的流行情况&#xff0c;并讨论了…

微分方程的数值解法——Runge-Kutta (RK4)

Runge-Kutta (RK4)   The Runge-Kutta (RK4) methods are used to solve the solution of the non-liner ordinary differential equation. Here, we will simply summary this method.   Assume the Intial Value Piont (IVP) is satisfied: y ′ f ( t , y ) , y ( t 0 )…

深入底层源码,剖析AQS的来龙去脉!

这里写目录标题 回顾前缀知识一、Condition的概念二、Condition底层结构三、Condition源码解析3.1 newCondition()3.2 await() 总结主要方法&#xff1a; 回顾 如果你还没熟悉 AQS 中的独占锁&#xff0c;可以先看这篇文章的前导篇。上一篇文章是以ReentrantLock 里面的加锁、解…

【2024年华数杯C题老外游中国】(完整题解+代码+完整参考论文)

请问 352 个城市中所有 35200 个景点评分的最高分&#xff08;Best Score&#xff0c;简称 BS&#xff09;是多少&#xff1f;全国有多少个景点获评了这个最高评分&#xff08;BS&#xff09;&#xff1f;获评了这个最高评分&#xff08;BS&#xff09;景点最多的城市有哪些&am…

2024带你轻松玩转Parallels Desktop19虚拟机!让你在Mac电脑上运行Windows系统

大家好&#xff0c;今天我要给大家安利一款神奇的软件——Parallels Desktop 19虚拟机。这款软件不仅可以让你在Mac电脑上运行Windows系统&#xff0c;还能轻松切换两个操作系统之间的文件和应用程序&#xff0c;让你的工作效率翻倍&#xff01; 让我来介绍一下Parallels Desk…

【口语】基础英语之疑问句 | 描述一个认为音乐很重要的人

文章目录 一、基础英语之疑问句二、口语题&#xff1a;描述一个认为音乐很重要并且喜欢音乐的人 一、基础英语之疑问句 英语中的疑问句可以根据结构和用途被分为几种主要类型&#xff1a; 一般疑问句&#xff08;General Questions&#xff09;: 结构&#xff1a;助动词 主语…

Learn ComputeShader 03 Passing data to shader

这次我们想要在一个平面中生成随机运动的圆形。之前传递数据都是通过setInt&#xff0c;setVector等方法进行的&#xff0c;但是这些方法并不能一下传递大量数据&#xff0c;比如一个结构体数组&#xff0c;一个数据块。所以这次的主要内容就是通过buffer传递大量数据。 首先是…

Android 本地化、多语言切换:Localization

目录 1&#xff09;如何实现多语言切换、如何实现跟随手机语言切换而切换app语言 2&#xff09;Localization是什么 3&#xff09;不管手机语言如何&#xff0c;根据用户在App选择的语言&#xff0c;只切换App语言 4&#xff09;文字长短不一样&#xff0c;怎么办呢? 一、Lo…

积分的简介

积分的简介 集成是一种添加切片以找到整体的方法。积分可用于查找区域、体积、中心点和许多有用的东西。但是&#xff0c;最简单的方法是从找到函数和 x 轴之间的区域开始&#xff0c;如下所示&#xff1a; 1.面积是什么&#xff1f;是片 我们可以在几个点上计算函数&#xf…

Error in importing environment OpenAI Gym

题意&#xff1a;尝试导入OpenAI Gym库中的某个环境时发生了错误 问题背景&#xff1a; I am trying to run an OpenAI Gym environment however I get the following error: 我正在尝试运行一个OpenAI Gym环境&#xff0c;但是我遇到了以下错误&#xff1a; import gym env…

Spring Boot整合MyBatis-Flex

说明&#xff1a;MyBatis-Flex&#xff08;官网地址&#xff1a;https://mybatis-flex.com/&#xff09;&#xff0c;是一款数据访问层框架&#xff0c;可实现项目中对数据库的访问&#xff0c;类比MyBatis-Plus。本文介绍&#xff0c;在Spring Boot项目整合MyBatis-Flex。 创…

专业解析:U盘打不开的应对与数据恢复策略

一、U盘打不开的困境解析 在日常的数据存储与传输中&#xff0c;U盘作为便携的存储媒介&#xff0c;其重要性不言而喻。然而&#xff0c;当您急需使用U盘时&#xff0c;却遭遇“U盘打不开”的尴尬境地&#xff0c;这无疑会给工作和学习带来极大的不便。U盘打不开的原因多种多样…

Javase--Date

1.Date简介 Date的学习: 1. java.util包下的类 2.用于日期、时间的描述 3. 实际上时距离一个固定时间点1970年1月1日00:00:00的毫秒数 4.我们常用的是格林威治时间:GMT UTC:世界调整时间 5.固定时间点:说的其实是本初子午线的时间。因此北京时间是1970年1月1日8:00:…

评估生成分子/对接分子的物理合理性工具 PoseBusters 评测

最近在一些分子生成或者对接模型中&#xff0c;出现了新的评估方法 PoseBusters&#xff0c;用于评估生成的分子或者对接的分子是否符合化学有效性和物理合理性。以往的分子生成&#xff0c;经常以生成分子的有效性、新颖性、化学空间分布&#xff0c;与口袋的结合力等方面进行…

.NET反混淆神器de4dot使用介绍

最近在逛看雪时&#xff0c;发现一个帖子&#xff0c;[原创]常见语言基础逆向方法合集-软件逆向-看雪-安全社区|安全招聘|kanxue.com。里面介绍 了常见语言基础逆向方法合集。关于.net程序逆向这块&#xff0c;介绍了三个工具。 .NET Reflector .NET Decompiler: Decompile A…

C++中string类常用函数的用法介绍

在C中&#xff0c;string是一个功能强大的类&#xff0c;用于处理和操作文本数据。它属于C标准库中的字符串库部分&#xff0c;专门用于处理字符串。与传统的C风格字符串相比&#xff0c;它提供了动态内存管理、类型安全和丰富的操作方法。 目录 一、构造和初始化 二、获取字…

算法训练,项目

一.木材加工 题解&#xff1a; 二分答案&#xff0c;左边0&#xff0c;右边可以为最长的木头&#xff0c;但我直接赋值了一个很大的值&#xff0c;进行二分&#xff0c;随后写个check;内部遍历木头截取为mid木块的个数&#xff0c;要是>k&#xff0c;满足要求&#xff0c;还…

【时时三省】(C语言基础)一维数组

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ——csdn时时三省 数组 数组就是一组数 数组的官方定义是一组相同类型元素的集合 一堆数组的创建和初始化 求组的创建 数组是一组相同类型元素的集合。数组的创建当时是: type&#xff3f;t arr&#x…

【JavaEE】定时器

目录 前言 什么是定时器 如何使用java中的定时器 实现计时器 实现MyTimeTask类 Time类中存储任务的数据结构 实现Timer中的schedule方法 实现MyTimer中的构造方法 处理构造方法中出现的线程安全问题 完整代码 考虑在限时等待wait中能否用sleep替换 能否用PriorityBlo…