mirror of
https://github.com/trekhleb/javascript-algorithms.git
synced 2025-07-07 09:54:53 +08:00
Add N-Queens.
This commit is contained in:
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
|
* [Strongly Connected Components](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
|
||||||
* **Uncategorized**
|
* **Uncategorized**
|
||||||
* [Tower of Hanoi](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/hanoi-tower)
|
* [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
|
* Union-Find
|
||||||
* Maze
|
* Maze
|
||||||
* Sudoku
|
* Sudoku
|
||||||
@ -110,9 +111,15 @@
|
|||||||
* [Maximum Subarray](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/maximum-subarray)
|
* [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
|
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
|
||||||
* **Backtracking**
|
* **Backtracking**
|
||||||
|
* [N-Queens Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/n-queens)
|
||||||
* **Branch & Bound**
|
* **Branch & Bound**
|
||||||
|
|
||||||
## Running Tests
|
## How to use this repository
|
||||||
|
|
||||||
|
**Install all dependencies**
|
||||||
|
```
|
||||||
|
npm i
|
||||||
|
```
|
||||||
|
|
||||||
**Run all tests**
|
**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'
|
npm test -- -t 'playground'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Useful links
|
## Useful Information
|
||||||
|
|
||||||
|
### References
|
||||||
|
|
||||||
[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
|
[▶ Data Structures and Algorithms on YouTube](https://www.youtube.com/playlist?list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
|
||||||
|
|
||||||
## Useful Information
|
|
||||||
|
|
||||||
### Big O Notation
|
### Big O Notation
|
||||||
|
|
||||||
Order of growth of algorithms specified in 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 |
|
| **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 |
|
| **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 |
|
| **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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Reference in New Issue
Block a user