1. 用链表表示带权图
- 1.1 对链表头节点的描述
/** 节点 */
function V(data) {
if (!(this instanceof V)) {
return new V(data);
}
this._id = uuid(10, 10); //随机生成的 uuid
this.data = data; //头节点的数据
this.children = null; //指向链表的其它节点
this.index = null; //节点在数组中的位置(数组下标)
this.d = Infinity; //从源节点到当前节点的最短距离的估计指(初始位无穷大)
}
- 1.2 对链表的其它节点的描述
function E() {
if (!(this instanceof E)) {
return new E();
}
this.next = null; //边指向下一个 E 类型的jie'dian
this.index = null; // 边指向的节点在 Adj 中的下标 例如边 (u,v) 则 index 指向v
this.w = null; //边的权重
}
- 1.3 图
function G() {
if (!(this instanceof G)) {
return new G();
}
this.Adj = []; //构成图 G 的邻接链表
}
G.prototype = {
constructor: G,
// 添加边
addEdge: function (u, v, w) {
let e = E();
e.index = v.index;
e.w = w;
let next = u.children;
if (next == null) {
u.children = e;
} else {
while (true) {
if (next.next == null) {
next.next = e;
break;
} else {
next = next.next;
}
}
}
},
//获取节点 u 的所有邻接节点
adj: function (u) {
let next = u.children;
let list = [];
while (true) {
if (next == null) {
break;
} else {
list.push(this.Adj[next.index]);
next = next.next;
}
}
return list;
},
// 权重函数
w: function (u, v) {
let next = u.children;
while (next != null) {
if (this.Adj[next.index]._id == v._id) {
return next.w;
}
next = next.next;
}
},
// 添加节点
addVertex: function (v) {
if (v.index != null) {
return;
}
let index = this.Adj.push(v) - 1; //该节点在数组中的位置
v.index = index;
},
//松弛
relax: function (u, v) {
if (v.d > u.d + this.w(u, v)) {
v.d = u.d + this.w(u, v);
return true;
}
return false;
}
}
- 1.4 简单的 uuid
/** 生成一个简单的uuid len 表示长度, sys 表示进制 可以位 2, 8, 16 等等*/
function uuid(len, sys) {
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [], i;
sys = sys || chars.length;
if (len) {
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * sys];
} else {
let r;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
2. Dijkstra 算法
Dijkstra 算法的思想是将图中的所有节点划分到两个集合中,集合Q中存放的是最短路径未确定的节点,集合S中存放的是已经确定最短路径距离的节点,初始时,所有的节点都在Q中,源节点s的最短路径距离初始时为0,集合Q内部通过最小优先队列来组织节点关系
- 从最小优先队列Q中取出最短估计路径值最小的节点u
- 对u的所有邻接节点进行松弛操作,并更新Q中u的邻接节点的最短估计路径值
- 重复执行步骤1直至Q为空集
- 2.1 松弛操作
松弛操作是对于节点 v
,如果从源节点到v的最短估计路径距离为 v.d
,v.d
为无穷大或为某一具体数,u
也是从源节点可达的节点,并且边(u, v)
存在且距离是w
,如果v.d > u.d + w(u, v)
, 则 v.d = u.d + w(u, v)
- 2.2 最小优先队列
最小优先队列参照 二叉堆、堆排序、优先队列、topK问题详解及js实现 这里直接给出实现代码
function MinBinaryHeap(key) {
if (!(this instanceof MinBinaryHeap))
return new MinBinaryHeap(key);
this.key = key; //key表示用来排序的字段
this.size = 0; //堆大小 这里堆大小和数组大小一致
this.list = []; //用于存放堆元素 存放的是对象
}
MinBinaryHeap.prototype = {
constructor: MinBinaryHeap,
//获取某个节点的父节点
parent: function (i) {
let p = Math.floor((i - 1) / 2);
if (i > this.size - 1 || p < 0) return null;
return p; //这里返回的 p 是在数组中的下标,数组是从0开始的
},
//获取某个节点的左孩子
left: function (i) {
let l = 2 * i + 1;
if (l > this.size - 1) return null;
return l;
},
//获取某个节点的右孩子
right: function (i) {
let r = 2 * i + 2;
if (r > this.size - 1) return null;
return r;
},
minHeapify: function (i) {
let list = this.list;
let key = this.key;
let l = this.left(i);
let r = this.right(i);
let smallest = null;
if (l != null) { //左孩子为空则右孩子一定为空
if (r == null) smallest = l;
else smallest = list[l][key] < list[r][key] ? l : r;
if (list[i][key] <= list[smallest][key]) return;
else {
let t = list[i];
list[i] = list[smallest];
list[smallest] = t;
this.minHeapify(smallest);
}
}
},
//元素上浮 对下标为i的元素进行向上调整,使堆保持其性质
increase: function (i) {
let list = this.list;
let p = this.parent(i);
while (i > 0 && list[p][this.key] > list[i][this.key]) { //i > 0 一定能保证 p != null
let t = list[i];
list[i] = list[p];
list[p] = t;
i = this.parent(i);
p = this.parent(i);
}
},
//构建堆
buildHeap: function (a) {
this.list = a;
this.size = a.length;
for (let i = Math.floor(a.length / 2) - 1; i > -1; i--) {
this.minHeapify(i);
}
},
//堆排序 由大到小
heapSort: function (a) {
this.buildHeap(a);
for (let i = this.size - 1; i > 0; i--) {
let t = this.list[0];
this.list[0] = this.list[i];
this.list[i] = t;
this.size--;
this.minHeapify(0);
}
return this.list;
},
//更新排序
fresh: function() {
this.size = this.list.length;
for (let i = Math.floor(this.size / 2) - 1; i > -1; i--) {
this.minHeapify(i);
}
}
}
//最小优先队列
function MinPriorityQueue(key, a) {
if (!(this instanceof MinPriorityQueue)) {
return new MinPriorityQueue(key, a);
}
if (a == null) {
a = [];
}
this.minBinaryHeap = MinBinaryHeap(key);
this.minBinaryHeap.buildHeap(a);
this.key = key;
}
MinPriorityQueue.prototype = {
constructor: MinPriorityQueue,
insert: function (x) { //加入一个元素
this.minBinaryHeap.size++;
this.minBinaryHeap.list[this.minBinaryHeap.size - 1] = x;
//向上调整
this.minBinaryHeap.increase(this.minBinaryHeap.size - 1);
},
//remove 表示获取后是否删除 true 删除 false 不删除
min: function (remove) { //获取最小元素
let min = this.minBinaryHeap.list[0];
if (remove) this.removeMin();
return min;
},
removeMin: function () { //移除最小元素
let list = this.minBinaryHeap.list;
let size = this.minBinaryHeap.size;
let min = list[0];
list[0] = list[size - 1];
list.shift(size - 1); //删除
this.minBinaryHeap.size--;
this.minBinaryHeap.minHeapify(0);
return min;
},
update: function (i, x) { //更新元素
this.minBinaryHeap.list[i] = x;
this.minBinaryHeap.minHeapify(i);
this.minBinaryHeap.increase(i);
},
fresh: function() {
this.minBinaryHeap.fresh();
}
}
- 2.3 Dijkstra算法
function dijkstra(g, s) {
let minPriorityQueue = MinPriorityQueue('d', null);
s.d = 0; //初始s的最短路径距离
g.Adj.forEach(v => {
minPriorityQueue.insert(v);
});
let u = minPriorityQueue.removeMin(); //取出 d 值最小的节点
while (u != null) {
let adj = g.adj(u); //获取节点u的所有邻接节点
adj.forEach(v => { //对u的所有邻接节点进行松弛操作
let isRelax = g.relax(u, v);
if(isRelax) {
minPriorityQueue.fresh(); //刷新最小优先队列
}
});
u = minPriorityQueue.removeMin();
}
}
3. 测试及整体实现
- 3.1 测试代码
let g = G();
let v1 = V('v1');
let v2 = V('v2');
let v3 = V('v3');
let v4 = V('v4');
let v5 = V('v5');
g.addVertex(v1);
g.addVertex(v2);
g.addVertex(v3);
g.addVertex(v4);
g.addVertex(v5);
g.addEdge(v1, v2, 10);
g.addEdge(v1, v3, 5);
g.addEdge(v2, v4, 1);
g.addEdge(v2, v3, 2);
g.addEdge(v3, v4, 9);
g.addEdge(v3, v5, 2);
g.addEdge(v3, v2, 3);
g.addEdge(v4, v5, 4);
g.addEdge(v5, v4, 6);
g.addEdge(v5, v1, 7);
let a = dijkstra(g, v1);
console.log(g);
- 3.2 整体代码及实现
function G() {
if (!(this instanceof G)) {
return new G();
}
this.Adj = [];
}
G.prototype = {
constructor: G,
// 添加边
addEdge: function (u, v, w) {
let e = E();
e.index = v.index;
e.w = w;
let next = u.children;
if (next == null) {
u.children = e;
} else {
while (true) {
if (next.next == null) {
next.next = e;
break;
} else {
next = next.next;
}
}
}
},
adj: function (u) {
let next = u.children;
let list = [];
while (true) {
if (next == null) {
break;
} else {
list.push(this.Adj[next.index]);
next = next.next;
}
}
return list;
},
// 权重函数
w: function (u, v) {
let next = u.children;
while (next != null) {
if (this.Adj[next.index]._id == v._id) {
return next.w;
}
next = next.next;
}
},
// 添加节点
addVertex: function (v) {
if (v.index != null) {
return;
}
let index = this.Adj.push(v) - 1; //该节点在数组中的位置
v.index = index;
},
//松弛
relax: function (u, v) {
if (v.d > u.d + this.w(u, v)) {
v.d = u.d + this.w(u, v);
return true;
}
return false;
}
}
/** 节点 */
function V(data) {
if (!(this instanceof V)) {
return new V(data);
}
this._id = uuid(10, 10);
this.data = data;
this.children = null;
this.index = null; //节点在数组中的位置
this.d = Infinity;
}
/** 边 */
function E() {
if (!(this instanceof E)) {
return new E();
}
this.next = null;
this.index = null; // 边指向的节点 在Adj中的下标 例如边(u,v) 则 index 指向v
this.w = null;
}
/** 生成一个简单的uuid */
function uuid(len, sys) {
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [], i;
sys = sys || chars.length;
if (len) {
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * sys];
} else {
let r;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
function MinBinaryHeap(key) {
if (!(this instanceof MinBinaryHeap))
return new MinBinaryHeap(key);
this.key = key; //key表示用来排序的字段
this.size = 0; //堆大小 这里堆大小和数组大小一致
this.list = []; //用于存放堆元素 存放的是对象
}
MinBinaryHeap.prototype = {
constructor: MinBinaryHeap,
//获取某个节点的父节点
parent: function (i) {
let p = Math.floor((i - 1) / 2);
if (i > this.size - 1 || p < 0) return null;
return p; //这里返回的 p 是在数组中的下标,数组是从0开始的
},
//获取某个节点的左孩子
left: function (i) {
let l = 2 * i + 1;
if (l > this.size - 1) return null;
return l;
},
//获取某个节点的右孩子
right: function (i) {
let r = 2 * i + 2;
if (r > this.size - 1) return null;
return r;
},
minHeapify: function (i) {
let list = this.list;
let key = this.key;
let l = this.left(i);
let r = this.right(i);
let smallest = null;
if (l != null) { //左孩子为空则右孩子一定为空
if (r == null) smallest = l;
else smallest = list[l][key] < list[r][key] ? l : r;
if (list[i][key] <= list[smallest][key]) return;
else {
let t = list[i];
list[i] = list[smallest];
list[smallest] = t;
this.minHeapify(smallest);
}
}
},
//元素上浮 对下标为i的元素进行向上调整,使堆保持其性质
increase: function (i) {
let list = this.list;
let p = this.parent(i);
while (i > 0 && list[p][this.key] > list[i][this.key]) { //i > 0 一定能保证 p != null
let t = list[i];
list[i] = list[p];
list[p] = t;
i = this.parent(i);
p = this.parent(i);
}
},
//构建堆
buildHeap: function (a) {
this.list = a;
this.size = a.length;
for (let i = Math.floor(a.length / 2) - 1; i > -1; i--) {
this.minHeapify(i);
}
},
//堆排序 由大到小
heapSort: function (a) {
this.buildHeap(a);
for (let i = this.size - 1; i > 0; i--) {
let t = this.list[0];
this.list[0] = this.list[i];
this.list[i] = t;
this.size--;
this.minHeapify(0);
}
return this.list;
},
//更新排序
fresh: function() {
this.size = this.list.length;
for (let i = Math.floor(this.size / 2) - 1; i > -1; i--) {
this.minHeapify(i);
}
}
}
//最小优先队列
function MinPriorityQueue(key, a) {
if (!(this instanceof MinPriorityQueue)) {
return new MinPriorityQueue(key, a);
}
if (a == null) {
a = [];
}
this.minBinaryHeap = MinBinaryHeap(key);
this.minBinaryHeap.buildHeap(a);
this.key = key;
}
MinPriorityQueue.prototype = {
constructor: MinPriorityQueue,
insert: function (x) { //加入一个元素
this.minBinaryHeap.size++;
this.minBinaryHeap.list[this.minBinaryHeap.size - 1] = x;
//向上调整
this.minBinaryHeap.increase(this.minBinaryHeap.size - 1);
},
//remove 表示获取后是否删除 true 删除 false 不删除
min: function (remove) { //获取最小元素
let min = this.minBinaryHeap.list[0];
if (remove) this.removeMin();
return min;
},
removeMin: function () { //移除最小元素
let list = this.minBinaryHeap.list;
let size = this.minBinaryHeap.size;
let min = list[0];
list[0] = list[size - 1];
list.shift(size - 1); //删除
this.minBinaryHeap.size--;
this.minBinaryHeap.minHeapify(0);
return min;
},
update: function (i, x) { //更新元素
this.minBinaryHeap.list[i] = x;
this.minBinaryHeap.minHeapify(i);
this.minBinaryHeap.increase(i);
},
fresh: function() {
this.minBinaryHeap.fresh();
}
}
function dijkstra(g, s) {
let minPriorityQueue = MinPriorityQueue('d', null);
s.d = 0;
g.Adj.forEach(v => {
minPriorityQueue.insert(v);
});
let u = minPriorityQueue.removeMin();
while (u != null) {
let adj = g.adj(u);
adj.forEach(v => {
let isRelax = g.relax(u, v);
if(isRelax) {
minPriorityQueue.fresh();
}
});
u = minPriorityQueue.removeMin();
}
}
let g = G();
let v1 = V('v1');
let v2 = V('v2');
let v3 = V('v3');
let v4 = V('v4');
let v5 = V('v5');
g.addVertex(v1);
g.addVertex(v2);
g.addVertex(v3);
g.addVertex(v4);
g.addVertex(v5);
g.addEdge(v1, v2, 10);
g.addEdge(v1, v3, 5);
g.addEdge(v2, v4, 1);
g.addEdge(v2, v3, 2);
g.addEdge(v3, v4, 9);
g.addEdge(v3, v5, 2);
g.addEdge(v3, v2, 3);
g.addEdge(v4, v5, 4);
g.addEdge(v5, v4, 6);
g.addEdge(v5, v1, 7);
let a = dijkstra(g, v1);
console.log(g);
- 3.3 运行结果
将以上代码复制粘贴到浏览器控制台运行后结果为
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。