mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-25 22:46:20 +08:00
Add N-Queens.
This commit is contained in:
parent
f8222ed397
commit
e2ef46016d
17
README.md
17
README.md
@ -78,6 +78,7 @@
|
||||
* [Strongly Connected Components](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
|
||||
* **Uncategorized**
|
||||
* [Tower of Hanoi](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/hanoi-tower)
|
||||
* [N-Queens Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/n-queens)
|
||||
* Union-Find
|
||||
* Maze
|
||||
* Sudoku
|
||||
@ -110,9 +111,15 @@
|
||||
* [Maximum Subarray](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/maximum-subarray)
|
||||
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
|
||||
* **Backtracking**
|
||||
* [N-Queens Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/n-queens)
|
||||
* **Branch & Bound**
|
||||
|
||||
## Running Tests
|
||||
## How to use this repository
|
||||
|
||||
**Install all dependencies**
|
||||
```
|
||||
npm i
|
||||
```
|
||||
|
||||
**Run all tests**
|
||||
```
|
||||
@ -135,12 +142,12 @@ Then just simply run the following command to test if your playground code works
|
||||
npm test -- -t 'playground'
|
||||
```
|
||||
|
||||
## Useful links
|
||||
## Useful Information
|
||||
|
||||
### References
|
||||
|
||||
[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
|
||||
|
||||
## Useful Information
|
||||
|
||||
### Big O Notation
|
||||
|
||||
Order of growth of algorithms specified in Big O notation.
|
||||
@ -185,4 +192,4 @@ Below is the list of some of the most used Big O notations and their performance
|
||||
| **Heap sort** | n log(n) | n log(n) | n log(n) | 1 | No |
|
||||
| **Merge sort** | n log(n) | n log(n) | n log(n) | n | Yes |
|
||||
| **Quick sort** | n log(n) | n log(n) | n^2 | log(n) | No |
|
||||
| **Shell sort** | n log(n) | depends on gap sequence | n (log(n))^2 | 1 | No |
|
||||
| **Shell sort** | n log(n) | depends on gap sequence | n (log(n))^2 | 1 | No |
|
||||
|
39
src/algorithms/uncategorized/n-queens/QueenPosition.js
Normal file
39
src/algorithms/uncategorized/n-queens/QueenPosition.js
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Class that represents queen position on the chessboard.
|
||||
*/
|
||||
export default class QueenPosition {
|
||||
/**
|
||||
* @param {number} rowIndex
|
||||
* @param {number} columnIndex
|
||||
*/
|
||||
constructor(rowIndex, columnIndex) {
|
||||
this.rowIndex = rowIndex;
|
||||
this.columnIndex = columnIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
get leftDiagonal() {
|
||||
// Each position on the same left (\) diagonal has the same difference of
|
||||
// rowIndex and columnIndex. This fact may be used to quickly check if two
|
||||
// positions (queens) are on the same left diagonal.
|
||||
// @see https://youtu.be/xouin83ebxE?t=1m59s
|
||||
return this.rowIndex - this.columnIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
get rightDiagonal() {
|
||||
// Each position on the same right diagonal (/) has the same
|
||||
// sum of rowIndex and columnIndex. This fact may be used to quickly
|
||||
// check if two positions (queens) are on the same right diagonal.
|
||||
// @see https://youtu.be/xouin83ebxE?t=1m59s
|
||||
return this.rowIndex + this.columnIndex;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${this.rowIndex},${this.columnIndex}`;
|
||||
}
|
||||
}
|
73
src/algorithms/uncategorized/n-queens/README.md
Normal file
73
src/algorithms/uncategorized/n-queens/README.md
Normal file
@ -0,0 +1,73 @@
|
||||
# N-Queens Problem
|
||||
|
||||
The **eight queens puzzle** is the problem of placing eight chess queens
|
||||
on an `8×8` chessboard so that no two queens threaten each other.
|
||||
Thus, a solution requires that no two queens share the same row,
|
||||
column, or diagonal. The eight queens puzzle is an example of the
|
||||
more general *n queens problem* of placing n non-attacking queens
|
||||
on an `n×n` chessboard, for which solutions exist for all natural
|
||||
numbers `n` with the exception of `n=2` and `n=3`.
|
||||
|
||||
For example, following is a solution for 4 Queen problem.
|
||||
|
||||
![N Queens](https://cdncontribute.geeksforgeeks.org/wp-content/uploads/N_Queen_Problem.jpg)
|
||||
|
||||
The expected output is a binary matrix which has 1s for the blocks
|
||||
where queens are placed. For example following is the output matrix
|
||||
for above 4 queen solution.
|
||||
|
||||
```
|
||||
{ 0, 1, 0, 0}
|
||||
{ 0, 0, 0, 1}
|
||||
{ 1, 0, 0, 0}
|
||||
{ 0, 0, 1, 0}
|
||||
```
|
||||
|
||||
## Naive Algorithm
|
||||
|
||||
Generate all possible configurations of queens on board and print a
|
||||
configuration that satisfies the given constraints.
|
||||
|
||||
```
|
||||
while there are untried conflagrations
|
||||
{
|
||||
generate the next configuration
|
||||
if queens don't attack in this configuration then
|
||||
{
|
||||
print this configuration;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Backtracking Algorithm
|
||||
|
||||
The idea is to place queens one by one in different columns,
|
||||
starting from the leftmost column. When we place a queen in a
|
||||
column, we check for clashes with already placed queens. In
|
||||
the current column, if we find a row for which there is no
|
||||
clash, we mark this row and column as part of the solution.
|
||||
If we do not find such a row due to clashes then we backtrack
|
||||
and return false.
|
||||
|
||||
```
|
||||
1) Start in the leftmost column
|
||||
2) If all queens are placed
|
||||
return true
|
||||
3) Try all rows in the current column. Do following for every tried row.
|
||||
a) If the queen can be placed safely in this row then mark this [row,
|
||||
column] as part of the solution and recursively check if placing
|
||||
queen here leads to a solution.
|
||||
b) If placing queen in [row, column] leads to a solution then return
|
||||
true.
|
||||
c) If placing queen doesn't lead to a solution then umark this [row,
|
||||
column] (Backtrack) and go to step (a) to try other rows.
|
||||
3) If all rows have been tried and nothing worked, return false to trigger
|
||||
backtracking.
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/Eight_queens_puzzle)
|
||||
- [GeeksForGeeks](https://www.geeksforgeeks.org/backtracking-set-3-n-queen-problem/)
|
||||
- [On YouTube by Abdul Bari](https://www.youtube.com/watch?v=xFv_Hl4B83A)
|
||||
- [On YouTube by Tushar Roy](https://www.youtube.com/watch?v=xouin83ebxE)
|
@ -0,0 +1,16 @@
|
||||
import QueenPosition from '../QueenPosition';
|
||||
|
||||
describe('QueenPosition', () => {
|
||||
it('should store queen position on chessboard', () => {
|
||||
const position1 = new QueenPosition(0, 0);
|
||||
const position2 = new QueenPosition(2, 1);
|
||||
|
||||
expect(position2.columnIndex).toBe(1);
|
||||
expect(position2.rowIndex).toBe(2);
|
||||
expect(position1.leftDiagonal).toBe(0);
|
||||
expect(position1.rightDiagonal).toBe(0);
|
||||
expect(position2.leftDiagonal).toBe(1);
|
||||
expect(position2.rightDiagonal).toBe(3);
|
||||
expect(position2.toString()).toBe('2,1');
|
||||
});
|
||||
});
|
@ -0,0 +1,38 @@
|
||||
import nQueens from '../nQueens';
|
||||
|
||||
describe('nQueens', () => {
|
||||
it('should not hae solution for 3 queens', () => {
|
||||
const solutions = nQueens(3);
|
||||
expect(solutions.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should solve n-queens problem for 4 queens', () => {
|
||||
const solutions = nQueens(4);
|
||||
expect(solutions.length).toBe(2);
|
||||
|
||||
// First solution.
|
||||
expect(solutions[0][0].toString()).toBe('0,1');
|
||||
expect(solutions[0][1].toString()).toBe('1,3');
|
||||
expect(solutions[0][2].toString()).toBe('2,0');
|
||||
expect(solutions[0][3].toString()).toBe('3,2');
|
||||
|
||||
// Second solution (mirrored).
|
||||
expect(solutions[1][0].toString()).toBe('0,2');
|
||||
expect(solutions[1][1].toString()).toBe('1,0');
|
||||
expect(solutions[1][2].toString()).toBe('2,3');
|
||||
expect(solutions[1][3].toString()).toBe('3,1');
|
||||
});
|
||||
|
||||
it('should solve n-queens problem for 6 queens', () => {
|
||||
const solutions = nQueens(6);
|
||||
expect(solutions.length).toBe(4);
|
||||
|
||||
// First solution.
|
||||
expect(solutions[0][0].toString()).toBe('0,1');
|
||||
expect(solutions[0][1].toString()).toBe('1,3');
|
||||
expect(solutions[0][2].toString()).toBe('2,5');
|
||||
expect(solutions[0][3].toString()).toBe('3,0');
|
||||
expect(solutions[0][4].toString()).toBe('4,2');
|
||||
expect(solutions[0][5].toString()).toBe('5,4');
|
||||
});
|
||||
});
|
103
src/algorithms/uncategorized/n-queens/nQueens.js
Normal file
103
src/algorithms/uncategorized/n-queens/nQueens.js
Normal file
@ -0,0 +1,103 @@
|
||||
import QueenPosition from './QueenPosition';
|
||||
|
||||
/**
|
||||
* @param {QueenPosition[]} queensPositions
|
||||
* @param {number} rowIndex
|
||||
* @param {number} columnIndex
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isSafe(queensPositions, rowIndex, columnIndex) {
|
||||
// New position to which the Queen is going to be placed.
|
||||
const newQueenPosition = new QueenPosition(rowIndex, columnIndex);
|
||||
|
||||
// Check if new queen position conflicts with any other queens.
|
||||
for (let queenIndex = 0; queenIndex < queensPositions.length; queenIndex += 1) {
|
||||
const currentQueenPosition = queensPositions[queenIndex];
|
||||
|
||||
if (
|
||||
// Check if queen has been already placed.
|
||||
currentQueenPosition &&
|
||||
(
|
||||
// Check if there are any queen on the same column.
|
||||
newQueenPosition.columnIndex === currentQueenPosition.columnIndex ||
|
||||
// Check if there are any queen on the same row.
|
||||
newQueenPosition.rowIndex === currentQueenPosition.rowIndex ||
|
||||
// Check if there are any queen on the same left diagonal.
|
||||
newQueenPosition.leftDiagonal === currentQueenPosition.leftDiagonal ||
|
||||
// Check if there are any queen on the same right diagonal.
|
||||
newQueenPosition.rightDiagonal === currentQueenPosition.rightDiagonal
|
||||
)
|
||||
) {
|
||||
// Can't place queen into current position since there are other queens that
|
||||
// are threatening it.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Looks like we're safe.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {QueenPosition[][]} solutions
|
||||
* @param {QueenPosition[]} previousQueensPositions
|
||||
* @param {number} queensCount
|
||||
* @param {number} rowIndex
|
||||
* @return {boolean}
|
||||
*/
|
||||
function nQueensRecursive(solutions, previousQueensPositions, queensCount, rowIndex) {
|
||||
// Clone positions array.
|
||||
const queensPositions = [...previousQueensPositions].map((queenPosition) => {
|
||||
return !queenPosition ? queenPosition : new QueenPosition(
|
||||
queenPosition.rowIndex,
|
||||
queenPosition.columnIndex,
|
||||
);
|
||||
});
|
||||
|
||||
if (rowIndex === queensCount) {
|
||||
// We've successfully reached the end of the board.
|
||||
// Store solution to the list of solutions.
|
||||
solutions.push(queensPositions);
|
||||
|
||||
// Solution found.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Let's try to put queen at row rowIndex into its safe column position.
|
||||
for (let columnIndex = 0; columnIndex < queensCount; columnIndex += 1) {
|
||||
if (isSafe(queensPositions, rowIndex, columnIndex)) {
|
||||
// Place current queen to its current position.
|
||||
queensPositions[rowIndex] = new QueenPosition(rowIndex, columnIndex);
|
||||
|
||||
// Try to place all other queens as well.
|
||||
nQueensRecursive(solutions, queensPositions, queensCount, rowIndex + 1);
|
||||
|
||||
// Remove the queen from the row to avoid isSafe() returning false.
|
||||
queensPositions[rowIndex] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} queensCount
|
||||
* @return {QueenPosition[][]}
|
||||
*/
|
||||
export default function nQueens(queensCount) {
|
||||
// Init NxN chessboard with zeros.
|
||||
// const chessboard = Array(queensCount).fill(null).map(() => Array(queensCount).fill(0));
|
||||
|
||||
// This array will hold positions or coordinates of each of
|
||||
// N queens in form of [rowIndex, columnIndex].
|
||||
const queensPositions = Array(queensCount).fill(null);
|
||||
|
||||
/** @var {QueenPosition[][]} solutions */
|
||||
const solutions = [];
|
||||
|
||||
// Solve problem recursively.
|
||||
nQueensRecursive(solutions, queensPositions, queensCount, 0);
|
||||
|
||||
return solutions;
|
||||
}
|
Loading…
Reference in New Issue
Block a user