Skip to content

Commit 9d61f0f

Browse files
committed
✨ feat: implement '@algorithm.ts/dijkstra'
1 parent 188e1bc commit 9d61f0f

File tree

9 files changed

+333
-0
lines changed

9 files changed

+333
-0
lines changed

Diff for: .vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"findset",
88
"gitmoji",
99
"guanghechen",
10+
"leetcode",
1011
"lowbit",
1112
"maxflow",
1213
"mcmf",

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Package | Description
5656
:----------------------------------:|:--------------------------
5757
[@algorithm.ts/binary-index-tree][] | Binary Index Tree.
5858
[@algorithm.ts/circular-queue][] | Circular queue.
59+
[@algorithm.ts/dijkstra][] | Dijkstra algorithm optimized with [@algorithm.ts/priority-queue][]. #ShortestPath.
5960
[@algorithm.ts/dinic][] | Dinic algorithm. #MaxFlow, #NetworkFlow.
6061
[@algorithm.ts/dlx][] | DLX algorithm.
6162
[@algorithm.ts/findset][] | Find set.
@@ -75,6 +76,7 @@ algorithm.ts is [MIT licensed](https://github.com/guanghechen/algorithm.ts/blob/
7576
[homepage]: https://github.com/guanghechen/algorithm.ts
7677
[@algorithm.ts/binary-index-tree]: ./packages/binary-index-tree
7778
[@algorithm.ts/circular-queue]: ./packages/circular-queue
79+
[@algorithm.ts/dijkstra]: ./packages/dijkstra
7880
[@algorithm.ts/dinic]: ./packages/dinic
7981
[@algorithm.ts/dlx]: ./packages/dlx
8082
[@algorithm.ts/findset]: ./packages/findset

Diff for: packages/dijkstra/README.md

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<header>
2+
<h1 align="center">
3+
<a href="https://github.com/guanghechen/algorithm.ts/tree/main/packages/dijkstra#readme">@algorithm.ts/dijkstra</a>
4+
</h1>
5+
<div align="center">
6+
<a href="https://www.npmjs.com/package/@algorithm.ts/dijkstra">
7+
<img
8+
alt="Npm Version"
9+
src="https://img.shields.io/npm/v/@algorithm.ts/dijkstra.svg"
10+
/>
11+
</a>
12+
<a href="https://www.npmjs.com/package/@algorithm.ts/dijkstra">
13+
<img
14+
alt="Npm Download"
15+
src="https://img.shields.io/npm/dm/@algorithm.ts/dijkstra.svg"
16+
/>
17+
</a>
18+
<a href="https://www.npmjs.com/package/@algorithm.ts/dijkstra">
19+
<img
20+
alt="Npm License"
21+
src="https://img.shields.io/npm/l/@algorithm.ts/dijkstra.svg"
22+
/>
23+
</a>
24+
<a href="#install">
25+
<img
26+
alt="Module Formats: cjs, esm"
27+
src="https://img.shields.io/badge/module_formats-cjs%2C%20esm-green.svg"
28+
/>
29+
</a>
30+
<a href="https://github.com/nodejs/node">
31+
<img
32+
alt="Node.js Version"
33+
src="https://img.shields.io/node/v/@algorithm.ts/dijkstra"
34+
/>
35+
</a>
36+
<a href="https://github.com/facebook/jest">
37+
<img
38+
alt="Tested with Jest"
39+
src="https://img.shields.io/badge/tested_with-jest-9c465e.svg"
40+
/>
41+
</a>
42+
<a href="https://github.com/prettier/prettier">
43+
<img
44+
alt="Code Style: prettier"
45+
src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"
46+
/>
47+
</a>
48+
</div>
49+
</header>
50+
<br/>
51+
52+
53+
A typescript implementation of the **dijkstra** algorithm.
54+
55+
The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm):
56+
57+
> Dijkstra's algorithm (/ˈdaɪkstrəz/ *DYKE-strəz*) is an algorithm for finding
58+
> the shortest paths between nodes in a graph, which may represent, for example,
59+
> road networks. It was conceived by computer scientist Edsger W. Dijkstra in
60+
> 1956 and published three years later.
61+
62+
63+
## Install
64+
65+
* npm
66+
67+
```bash
68+
npm install --save @algorithm.ts/dijkstra
69+
```
70+
71+
* yarn
72+
73+
```bash
74+
yarn add @algorithm.ts/dijkstra
75+
```
76+
77+
## Usage
78+
79+
A solution for leetcode "Number of Ways to Arrive at Destination"
80+
(https://leetcode.com/problems/number-of-ways-to-arrive-at-destination/):
81+
82+
```typescript
83+
import type { DijkstraEdge } from '@algorithm.ts/dijkstra'
84+
import dijkstra from '@algorithm.ts/dijkstra'
85+
86+
export function countPaths(N: number, roads: number[][]): number {
87+
const MOD = 1e9 + 7
88+
const G: Array<Array<DijkstraEdge<number>>> = new Array(N)
89+
for (let i = 0; i < N; ++i) G[i] = []
90+
for (const road of roads) {
91+
G[road[0]].push({ to: road[1], cost: road[2] })
92+
G[road[1]].push({ to: road[0], cost: road[2] })
93+
}
94+
95+
const source = 0
96+
const target = N - 1
97+
const dist: number[] = dijkstra<number>(N, target, G, 0, 0x3f3f3f)
98+
99+
const dp: number[] = new Array(N).fill(-1)
100+
return dfs(source)
101+
102+
function dfs(o: number): number {
103+
if (o === target) return 1
104+
105+
let answer = dp[o]
106+
if (answer !== -1) return answer
107+
108+
answer = 0
109+
const d = dist[o]
110+
for (const e of G[o]) {
111+
if (dist[e.to] + e.cost === d) {
112+
const t = dfs(e.to)
113+
answer = modAdd(answer, t)
114+
}
115+
}
116+
dp[o] = answer
117+
return answer
118+
}
119+
120+
function modAdd(x: number, y: number): number {
121+
const z: number = x + y
122+
return z < MOD ? z : z - MOD
123+
}
124+
}
125+
```
126+
127+
128+
## Related
129+
130+
* [dijkstra 算法][dijkstra]
131+
* [@algorithm.ts/priority-queue][]
132+
133+
134+
[homepage]: https://github.com/guanghechen/algorithm.ts/tree/main/packages/dijkstra#readme
135+
[dijkstra]: https://me.guanghechen.com/post/algorithm/graph/shortest-path/dijkstra
136+
[@algorithm.ts/priority-queue]: https://github.com/guanghechen/algorithm.ts/tree/main/packages/priority-queue

Diff for: packages/dijkstra/__test__/dijkstra.spec.ts

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { DijkstraEdge } from '../src'
2+
import dijkstra from '../src'
3+
4+
describe('dijkstra', function () {
5+
// https://leetcode.com/problems/number-of-ways-to-arrive-at-destination/
6+
test('leetcode/number-of-ways-to-arrive-at-destination', function () {
7+
const data: any[] = [
8+
{
9+
input: [
10+
7,
11+
[
12+
[0, 6, 7],
13+
[0, 1, 2],
14+
[1, 2, 3],
15+
[1, 3, 3],
16+
[6, 3, 3],
17+
[3, 5, 1],
18+
[6, 5, 1],
19+
[2, 5, 1],
20+
[0, 4, 5],
21+
[4, 6, 2],
22+
],
23+
],
24+
answer: 4,
25+
},
26+
{
27+
input: [2, [[1, 0, 10]]],
28+
answer: 1,
29+
},
30+
]
31+
32+
for (const kase of data) {
33+
expect(countPaths(kase.input[0], kase.input[1])).toEqual(kase.answer)
34+
}
35+
})
36+
})
37+
38+
const MOD = 1e9 + 7
39+
function countPaths(N: number, roads: number[][]): number {
40+
const G: Array<Array<DijkstraEdge<number>>> = new Array(N)
41+
for (let i = 0; i < N; ++i) G[i] = []
42+
for (const road of roads) {
43+
G[road[0]].push({ to: road[1], cost: road[2] })
44+
G[road[1]].push({ to: road[0], cost: road[2] })
45+
}
46+
47+
const source = 0
48+
const target = N - 1
49+
const dist: number[] = dijkstra<number>(N, target, G, 0, 0x3f3f3f)
50+
51+
const dp: number[] = new Array(N).fill(-1)
52+
return dfs(source)
53+
54+
function dfs(o: number): number {
55+
if (o === target) return 1
56+
57+
let answer = dp[o]
58+
if (answer !== -1) return answer
59+
60+
answer = 0
61+
const d = dist[o]
62+
for (const e of G[o]) {
63+
if (dist[e.to] + e.cost === d) {
64+
const t = dfs(e.to)
65+
answer = modAdd(answer, t)
66+
}
67+
}
68+
dp[o] = answer
69+
return answer
70+
}
71+
}
72+
73+
function modAdd(x: number, y: number): number {
74+
const z: number = x + y
75+
return z < MOD ? z : z - MOD
76+
}

Diff for: packages/dijkstra/package.json

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@algorithm.ts/dijkstra",
3+
"version": "1.0.11",
4+
"description": "Dancing link + Algorithm X",
5+
"author": {
6+
"name": "guanghechen",
7+
"url": "https://github.com/guanghechen/"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/guanghechen/algorithm.ts.git",
12+
"directory": "packages/dijkstra"
13+
},
14+
"homepage": "https://github.com/guanghechen/algorithm.ts/tree/main/packages/dijkstra#readme",
15+
"keywords": [
16+
"algorithm",
17+
"dijkstra",
18+
"dijkstra + priority-queue",
19+
"shortest path",
20+
"single source shortest path"
21+
],
22+
"main": "lib/cjs/index.js",
23+
"module": "lib/esm/index.js",
24+
"types": "lib/types/index.d.ts",
25+
"source": "src/index.ts",
26+
"license": "MIT",
27+
"engines": {
28+
"node": ">= 14.15.0"
29+
},
30+
"files": [
31+
"lib/",
32+
"!lib/**/*.js.map",
33+
"!lib/**/*.d.ts.map",
34+
"package.json",
35+
"CHANGELOG.md",
36+
"LICENSE",
37+
"README.md"
38+
],
39+
"scripts": {
40+
"build": "cross-env NODE_ENV=production rollup -c ../../rollup.config.js",
41+
"prebuild": "rimraf lib/ && tsc -p tsconfig.src.json --emitDeclarationOnly",
42+
"prepublishOnly": "cross-env ROLLUP_SHOULD_SOURCEMAP=false yarn build",
43+
"test": "cross-env TS_NODE_FILES=true jest --config ../../jest.config.js --rootDir ."
44+
},
45+
"dependencies": {
46+
"@algorithm.ts/priority-queue": "^1.0.11"
47+
}
48+
}

Diff for: packages/dijkstra/src/index.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { createPriorityQueue } from '@algorithm.ts/priority-queue'
2+
3+
export interface DijkstraEdge<T extends number | bigint> {
4+
/**
5+
* The other end of the edge.
6+
*/
7+
to: number
8+
/**
9+
* The cost of walking along this side.
10+
*/
11+
cost: T
12+
}
13+
14+
/**
15+
* The dijkstra algorithm, optimized with priority queue.
16+
*
17+
* @param N the number of nodes in the graph
18+
* @param source the source node
19+
* @param G edges of the graph. G[i] represent the edge list start from node i.
20+
* @param ZERO 0 for number, 0n for bigint.
21+
* @param INF a big number, such as Number.MAX_SAFE_INTEGER
22+
* @returns
23+
*
24+
* @see https://me.guanghechen.com/post/algorithm/graph/shortest-path/dijkstra
25+
*/
26+
export function dijkstra<T extends number | bigint>(
27+
N: number,
28+
source: number,
29+
G: ReadonlyArray<ReadonlyArray<DijkstraEdge<T>>>,
30+
ZERO: T,
31+
INF: T,
32+
): T[] {
33+
const dist: T[] = new Array(N).fill(INF)
34+
const Q = createPriorityQueue<{ pos: number; cost: T }>(
35+
(x, y) => y.cost - x.cost,
36+
)
37+
38+
// eslint-disable-next-line no-param-reassign
39+
dist[source] = ZERO
40+
Q.enqueue({ pos: source, cost: ZERO })
41+
42+
while (Q.size() > 0) {
43+
const { pos, cost } = Q.dequeue()!
44+
if (dist[pos] < cost) continue
45+
for (const e of G[pos]) {
46+
const candidate: T = (dist[pos] as any) + e.cost
47+
if (dist[e.to] > candidate) {
48+
// eslint-disable-next-line no-param-reassign
49+
dist[e.to] = candidate
50+
Q.enqueue({ pos: e.to, cost: dist[e.to] })
51+
}
52+
}
53+
}
54+
return dist
55+
}
56+
57+
export default dijkstra

Diff for: packages/dijkstra/tsconfig.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig",
3+
"include": ["src", "script", "__test__"]
4+
}

Diff for: packages/dijkstra/tsconfig.src.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.settings",
3+
"compilerOptions": {
4+
"declarationDir": "lib/types",
5+
"rootDir": "src"
6+
},
7+
"include": ["src"]
8+
}

Diff for: tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"paths": {
88
"@algorithm.ts/binary-index-tree": ["packages/binary-index-tree/src"],
99
"@algorithm.ts/circular-queue": ["packages/circular-queue/src"],
10+
"@algorithm.ts/dijkstra": ["packages/dijkstra/src"],
1011
"@algorithm.ts/dinic": ["packages/dinic/src"],
1112
"@algorithm.ts/dlx": ["packages/dlx/src"],
1213
"@algorithm.ts/findset": ["packages/findset/src"],

0 commit comments

Comments
 (0)