Skip to content

Commit

Permalink
added graph and tree
Browse files Browse the repository at this point in the history
  • Loading branch information
zAlweNy26 committed Oct 5, 2023
1 parent f73e937 commit 912a2f7
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 7 deletions.
53 changes: 53 additions & 0 deletions src/structures/graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { GraphStructure } from "./interfaces"

export class Graph<K> extends GraphStructure<K, K> {
addEdge(v1: K, v2: K) {
const list = this.map.get(v1)
if (list) {
list.push(v2)
const edge = this.map.get(v2)
if (edge?.includes(v1)) throw new Error('Edge already present')
else if (edge) edge.push(v1)
else this.map.set(v2, [v1])
} else throw new Error('First vertex not found')
return this
}

removeEdge(v1: K, v2: K) {
const list = this.map.get(v1)
if (list) {
const index = list.indexOf(v2)
if (index != -1) list.splice(index, 1)
else throw new Error('Edge not found')

const edge = this.map.get(v2)
if (edge) {
const index = edge.indexOf(v1)
if (index != -1) edge.splice(index, 1)
}
} else throw new Error('Vertex not found')
return this
}

removeVertex(vertex: K) {
if (this.map.delete(vertex)) {
for (const list of this.map.values()) {
const index = list.indexOf(vertex)
if (index != -1) list.splice(index, 1)
}
} else throw new Error('Vertex not found')
return this
}

getEdges(vertex: K) {
const list = this.map.get(vertex)
if (!list) throw new Error('Vertex not found')
return [...list] as readonly K[]
}

isAdjacent(v1: K, v2: K) {
const list = this.map.get(v1)
if (!list) throw new Error('First vertex not found')
return list.includes(v2)
}
}
63 changes: 62 additions & 1 deletion src/structures/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Matrix } from "./matrix"

export interface Structure {
clear: () => void
space: number
Expand All @@ -6,8 +8,67 @@ export interface Structure {
isFull: boolean
}

export interface List<T> extends Structure {
export interface ListStructure<T> extends Structure {
size: number
peek: () => T | undefined
items: readonly T[]
}

export abstract class GraphStructure<K, V> {
protected map = new Map<K, V[]>()

constructor(vertex: K) {
this.map.set(vertex, [])
}

abstract addEdge(v1: K, v2: K): this

abstract removeEdge(v1: K, v2: K): this

abstract getEdges(vertex: K): readonly K[]

abstract isAdjacent(v1: K, v2: K): boolean

abstract removeVertex(vertex: K): this

getPath(v1: K, v2: K) {
const start = this.map.get(v1), end = this.map.get(v2)
if (!start || !end) throw new Error('One or both the vertices could not be found')
const visited = new Set<K>()
const queue: [K, K[]][] = [[v1, []]]
while (queue.length) {
const [vertex, path] = queue.shift()!
if (visited.has(vertex)) continue
visited.add(vertex)
const currentPath = [...path, vertex]
if (vertex == v2) return currentPath as readonly K[]
const edges = this.getEdges(vertex)
for (const edge of edges) {
queue.push([edge, currentPath])
}
}
throw new Error('Path not found')
}

hasVertex(vertex: K) {
return this.map.has(vertex)
}

get asMatrix() {
const matrix: number[][] = []
for (const vertex of this.vertices) {
const edges = this.getEdges(vertex)
const row = this.vertices.map(v => edges.includes(v) ? 1 : 0)
matrix.push(row)
}
return Matrix.from(matrix)
}

get vertices() {
return [...this.map.keys()] as readonly K[]
}

get size() {
return this.map.size
}
}
6 changes: 3 additions & 3 deletions src/structures/queue.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { List } from "./interfaces"
import { ListStructure } from "./interfaces"

export class Queue<T> implements List<T> {
export class Queue<T> implements ListStructure<T> {
protected _data: T[] = []
protected _head = 0
protected _tail = 0
Expand Down Expand Up @@ -44,7 +44,7 @@ export class Queue<T> implements List<T> {

get items() {
if (this.isEmpty) return []
return [...this._data.filter(v => v != undefined)]
return [...this._data.filter(v => v != undefined)] as readonly T[]
}

get space() {
Expand Down
6 changes: 3 additions & 3 deletions src/structures/stack.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { List } from "./interfaces"
import { ListStructure } from "./interfaces"

export class Stack<T> implements List<T> {
export class Stack<T> implements ListStructure<T> {
protected _data: T[] = []
protected _capacity = 0
size = 0
Expand Down Expand Up @@ -37,7 +37,7 @@ export class Stack<T> implements List<T> {
}

get items() {
return [...this._data]
return [...this._data] as readonly T[]
}

get space() {
Expand Down
88 changes: 88 additions & 0 deletions src/structures/tree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export class TreeLeaf<T> {
leaves: TreeLeaf<T>[] = []

constructor(public data: T, leaves?: TreeLeaf<T>[]) {
this.leaves = leaves ?? []
}

push(data: T, ...datas: T[]) {
let leaf = new TreeLeaf(data, [])
this.leaves.push(leaf)
for (const d of datas) {
leaf = new TreeLeaf(d, [])
this.leaves.push(leaf)
}
return leaf
}

get children() {
return this.leaves.map(l => l.data)
}

get height(): number {
return this.leaves.length > 0 ? 1 + Math.max(0, ...this.leaves.map(c => c.height)) : 0
}
}

const preOrder = <T>(node: TreeLeaf<T>, list: T[]) => { // root -> leaves
list.push(node.data)
for (const child of node.leaves) preOrder(child, list)
}

const postOrder = <T>(node: TreeLeaf<T>, list: T[]) => { // leaves -> root
for (const child of node.leaves) postOrder(child, list)
list.push(node.data)
}

const inOrder = <T>(node: TreeLeaf<T>, list: T[]) => { // first half -> root -> second half
if (node.leaves.length == 0) list.push(node.data)
else {
const n = node.leaves.length, nd = Math.round(n / 2)
for (let i = 0; i < nd; i++) {
inOrder(node.leaves[i], list)
}
list.push(node.data)
for (let i = nd; i < n; i++) {
inOrder(node.leaves[i], list)
}
}
}

const heightOrder = <T>(node: TreeLeaf<T>, list: T[], first = true) => { // leaves for each height from top to bottom
if (first) list.push(node.data)
list.push(...node.leaves.map(l => l.data))
for (const child of node.leaves) heightOrder(child, list, false)
}

export class Tree<T> {
root!: TreeLeaf<T>

constructor(data: T) {
this.root = new TreeLeaf(data)
}

traverse(order: "post" | "pre" | "in" | "height" = "pre") {
const result: T[] = []

if (order == "pre") preOrder(this.root, result)
else if (order == "post") postOrder(this.root, result)
else if (order == "in") inOrder(this.root, result)
else if (order == "height") heightOrder(this.root, result)

return result
}

search(value: T) {
const queue: TreeLeaf<T>[] = [this.root]
while (queue.length > 0) {
const node = queue.shift()!
if (node.data == value) return node
queue.push(...node.leaves)
}
return undefined
}

get depth() {
return this.root.height
}
}
66 changes: 66 additions & 0 deletions src/structures/weighted_graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { GraphStructure } from "./interfaces"

type Weight = number
type Edge<V> = [V, Weight]

export class WeightedGraph<K> extends GraphStructure<K, Edge<K>> {
addEdge(v1: K, v2: K, weight = 0) {
const list = this.map.get(v1)
if (list) {
list.push([v2, weight])
const edge = this.map.get(v2)
if (edge?.map(e => e[0])?.includes(v1)) throw new Error('Edge already present')
else if (edge) edge.push([v1, weight])
else this.map.set(v2, [[v1, weight]])
} else throw new Error('First vertex not found')
return this
}

removeEdge(v1: K, v2: K) {
const list = this.map.get(v1)
if (list) {
const index = list.findIndex(e => e[0] == v2)
if (index != -1) list.splice(index, 1)
else throw new Error('Edge not found')

const edge = this.map.get(v2)
if (edge) {
const index = edge.findIndex(e => e[0] == v1)
if (index != -1) edge.splice(index, 1)
}
} else throw new Error('Vertex not found')
return this
}

removeVertex(vertex: K) {
if (this.map.delete(vertex)) {
for (const list of this.map.values()) {
const index = list.findIndex(e => e[0] == vertex)
if (index != -1) list.splice(index, 1)
}
} else throw new Error('Vertex not found')
return this
}

getEdges(vertex: K) {
const list = this.map.get(vertex)
if (!list) throw new Error('Vertex not found')
return [...list.map(e => e[0])] as readonly K[]
}

getWeight(v1: K, v2: K, ...vn: K[]) {
const list = this.map.get(v1)
if (!list) throw new Error('First vertex not found')
const edge = list.find(e => e[0] == v2)
if (!edge) throw new Error('Second vertex not found')
let weight = edge[1]
if (vn.length) weight += this.getWeight(v2, vn[0], ...vn.slice(1))
return weight
}

isAdjacent(v1: K, v2: K) {
const list = this.map.get(v1)
if (!list) throw new Error('First vertex not found')
return list.map(e => e[0]).includes(v2)
}
}

0 comments on commit 912a2f7

Please sign in to comment.