欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS使用Prim算法和Kruskal算法實(shí)現(xiàn)最小生成樹(shù)

 更新時(shí)間:2019年01月17日 10:51:53   作者:隨風(fēng)丶逆風(fēng)  
這篇文章主要為大家詳細(xì)介紹了JS使用Prim算法和Kruskal算法實(shí)現(xiàn)最小生成樹(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

之前都是看書(shū),大部分也是c++的實(shí)現(xiàn),但是搞前端不能忘了JS啊,所以JS實(shí)現(xiàn)一遍這兩個(gè)經(jīng)典的最小生成樹(shù)算法。

一、權(quán)重圖和最小生成樹(shù)

權(quán)重圖:圖的邊帶權(quán)重

最小生成樹(shù):在連通圖的所有生成樹(shù)中,所有邊的權(quán)重和最小的生成樹(shù)

本文使用的圖如下:

它的最小生成樹(shù)如下:

二、鄰接矩陣

鄰接矩陣:用來(lái)表示圖的矩陣就是鄰接矩陣,其中下標(biāo)表示頂點(diǎn),矩陣中的值表示邊的權(quán)重(或者有無(wú)邊,方向等)。

本文在構(gòu)建鄰接矩陣時(shí),默認(rèn)Number.MAX_SAFE_INTEGER表示兩個(gè)節(jié)點(diǎn)之間沒(méi)有邊,Number.MIN_SAFE_INTEGER表示當(dāng)前節(jié)點(diǎn)沒(méi)有自環(huán)。

代碼如下:

/**
 * 鄰接矩陣
 * 值為頂點(diǎn)與頂點(diǎn)之間邊的權(quán)值,0表示無(wú)自環(huán),一個(gè)大數(shù)表示無(wú)邊(比如10000)
 * */
const MAX_INTEGER = Number.MAX_SAFE_INTEGER;//沒(méi)有的邊
const MIN_INTEGER = Number.MIN_SAFE_INTEGER;//沒(méi)有自環(huán)
 
const matrix= [
  [MIN_INTEGER, 9, 2, MAX_INTEGER, 6],
  [9, MIN_INTEGER, 3, MAX_INTEGER, MAX_INTEGER],
  [2, 3, MIN_INTEGER, 5, MAX_INTEGER],
  [MAX_INTEGER, MAX_INTEGER, 5, MIN_INTEGER, 1],
  [6, MAX_INTEGER, MAX_INTEGER, 1, MIN_INTEGER]
];

這個(gè)鄰接矩陣表示的圖如下:

三、 邊的表示

一個(gè)邊具有權(quán)重、起點(diǎn)、重點(diǎn)三個(gè)屬性,所以可以創(chuàng)建一個(gè)類(lèi)(對(duì)象),實(shí)現(xiàn)如下:

/**
 * 邊對(duì)象
 * */
function Edge(begin, end, weight) {
  this.begin = begin;
  this.end = end;
  this.weight = weight;
}
 
Edge.prototype.getBegin = function () {
  return this.begin;
};
Edge.prototype.getEnd = function () {
  return this.end;
};
Edge.prototype.getWeight = function () {
  return this.weight;
};
 
/*class Edge {
  constructor(begin, end, weight) {
    this.begin = begin;
    this.end = end;
    this.weight = weight;
  }
  getBegin() {
    return this.begin;
  }
  getEnd() {
    return this.end;
  }
  getWeight() {
    return this.weight;
  }
}*/

 PS:JS這門(mén)語(yǔ)言沒(méi)有私有變量的說(shuō)法,這里寫(xiě)get方法純粹是模擬一下私有變量??梢圆挥眠@么寫(xiě),可以直接通過(guò)屬性訪問(wèn)到屬性值。

四、Prim算法

將這個(gè)算法的文章數(shù)不勝數(shù),這里就不細(xì)說(shuō)了。

其大體思路就是:以某頂點(diǎn)為起點(diǎn),逐步找各頂點(diǎn)上最小權(quán)值的相鄰邊構(gòu)建最小生成樹(shù),同時(shí)其鄰接點(diǎn)納入生成樹(shù)的頂點(diǎn)中,只要保證頂點(diǎn)不重復(fù)添加即可。

實(shí)現(xiàn)代碼如下:

/**
 * Prim算法
 * 以某頂點(diǎn)為起點(diǎn),逐步找各頂點(diǎn)上最小權(quán)值的邊構(gòu)建最小生成樹(shù),同時(shí)其鄰接點(diǎn)納入生成樹(shù)的頂點(diǎn)中,只要保證頂點(diǎn)不重復(fù)添加即可
 * 使用鄰接矩陣即可
 * 優(yōu)點(diǎn):適合點(diǎn)少邊多的情況
 * @param matrix 鄰接矩陣
 * @return Array 最小生成樹(shù)的邊集數(shù)組
 * */
function prim(matrix) {
  const rows = matrix.length,
    cols = rows,
    result = [],
    savedNode = [0];//已選擇的節(jié)點(diǎn)
  let minVex = -1,
    minWeight = MAX_INTEGER;
  for (let i = 0; i < rows; i++) {
    let row = savedNode[i],
      edgeArr = matrix[row];
    for (let j = 0; j < cols; j++) {
      if (edgeArr[j] < minWeight && edgeArr[j] !== MIN_INTEGER) {
        minWeight = edgeArr[j];
        minVex = j;
      }
    }
 
    //保證所有已保存節(jié)點(diǎn)的相鄰邊都遍歷到
    if (savedNode.indexOf(minVex) === -1 && i === savedNode.length - 1) {
      savedNode.push(minVex);
      result.push(new Edge(row, minVex, minWeight));
 
      //重新在已加入的節(jié)點(diǎn)集中找權(quán)值最小的邊的外部邊
      i = -1;
      minWeight = MAX_INTEGER;
 
      //已加入的邊,去掉,下次就不會(huì)選這條邊了
      matrix[row][minVex] = MAX_INTEGER;
      matrix[minVex][row] = MAX_INTEGER;
    }
  }
  return result;
}

五、Kruskal算法

介紹這個(gè)算法的文章也很多,這里不細(xì)說(shuō)。

其主要的思路就是:遍歷所有的邊,按權(quán)值從小到大排序,每次選取當(dāng)前權(quán)值最小的邊,只要不構(gòu)成回環(huán),則加入生成樹(shù)。

5.1 鄰接矩陣轉(zhuǎn)成邊集數(shù)組

與Prim算法不同,Kruskal算法是從最小權(quán)值的邊開(kāi)始的,所以使用邊集數(shù)組更方便。所以需要將鄰接矩陣轉(zhuǎn)成邊集數(shù)組,并且按照邊的權(quán)重從小到大排序。

/**
 * 鄰接矩陣轉(zhuǎn)邊集數(shù)組的函數(shù)
 * @param matrix 鄰接矩陣
 * @return Array 邊集數(shù)組
 * */
function changeMatrixToEdgeArray(matrix) {
  const rows = matrix.length,
    cols = rows,
    result = [];
  for (let i = 0; i < rows; i++) {
    const row = matrix[i];
    for(let j = 0 ; j < cols; j++) {
      if(row[j] !== MIN_INTEGER && row[j] !== MAX_INTEGER) {
        result.push(new Edge(i, j, row[j]));
        matrix[i][j] = MAX_INTEGER;
        matrix[j][i] = MAX_INTEGER;
      }
    }
  }
  result.sort((a, b) => a.getWeight() - b.getWeight());
  return result;
}

5.2 Kruskal算法的具體實(shí)現(xiàn)

Kruskal算法的一個(gè)要點(diǎn)就是避免環(huán)路,這里采用一個(gè)數(shù)組來(lái)保存已納入生成樹(shù)的頂點(diǎn)和邊(連線),其下標(biāo)是邊(連線)的起點(diǎn),下標(biāo)對(duì)應(yīng)的元素值是邊(連線)的終點(diǎn)。下標(biāo)對(duì)應(yīng)的元素值為0,表示還沒(méi)有以它為起點(diǎn)的邊(連線)。

連線:表示一條或多條邊前后連接形成的一條線,這條線沒(méi)有環(huán)路。

/**
 * kruskal算法
 * 遍歷所有的邊,按權(quán)值從小到大排序,每次選取當(dāng)前權(quán)值最小的邊,只要不構(gòu)成回環(huán),則加入生成樹(shù)
 * 鄰接矩陣轉(zhuǎn)換成邊集數(shù)組
 * 優(yōu)點(diǎn):適合點(diǎn)多邊少的情況
 * @param matrix 鄰接矩陣
 * @return Array 最小生成樹(shù)的邊集數(shù)組
 * */
function kruskal(matrix) {
  const edgeArray = changeMatrixToEdgeArray(matrix),
    result = [],
    //使用一個(gè)數(shù)組保存當(dāng)前頂點(diǎn)的邊的終點(diǎn),0表示還沒(méi)有已它為起點(diǎn)的邊加入
    savedEdge = new Array(matrix.length).fill(0);
 
  for (let i = 0, len = edgeArray.length; i < len; i++) {
    const edge = edgeArray[i];
    const n = findEnd(savedEdge, edge.getBegin());
    const m = findEnd(savedEdge, edge.getEnd());
    console.log(savedEdge, n, m);
    //不相等表示這條邊沒(méi)有與現(xiàn)有生成樹(shù)形成環(huán)路
    if (n !== m) {
      result.push(edge);
      //將這條邊的結(jié)尾頂點(diǎn)加入數(shù)組中,表示頂點(diǎn)已在生成樹(shù)中
      savedEdge[n] = m;
    }
  }
  return result;
}
 
/**
 * 查找連線頂點(diǎn)的尾部下標(biāo)
 * @param arr 判斷邊與邊是否形成環(huán)路的數(shù)組
 * @param start 連線開(kāi)始的頂點(diǎn)
 * @return Number 連線頂點(diǎn)的尾部下標(biāo)
 * */
function findEnd(arr, start) {
  //就是一直循環(huán),直到找到終點(diǎn),如果沒(méi)有連線,就返回0
  while (arr[start] > 0) {
    start = arr[start];
  }
  return start;
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解webpack 入門(mén)與解析

    詳解webpack 入門(mén)與解析

    這篇文章主要介紹了詳解webpack 入門(mén)與解析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • uni-app如何頁(yè)面?zhèn)鲄?shù)的幾種方法總結(jié)

    uni-app如何頁(yè)面?zhèn)鲄?shù)的幾種方法總結(jié)

    這篇文章主要介紹了uni-app如何頁(yè)面?zhèn)鲄?shù)的幾種方法總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • JavaScript寫(xiě)個(gè)貪吃蛇小游戲(超詳細(xì))

    JavaScript寫(xiě)個(gè)貪吃蛇小游戲(超詳細(xì))

    這篇文章主要介紹了JavaScript寫(xiě)個(gè)貪吃蛇小游戲(超詳細(xì)),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • js驗(yàn)證手機(jī)號(hào)、密碼、短信驗(yàn)證碼代碼工具類(lèi)

    js驗(yàn)證手機(jī)號(hào)、密碼、短信驗(yàn)證碼代碼工具類(lèi)

    這篇文章主要介紹了js驗(yàn)證手機(jī)號(hào)、密碼、短信驗(yàn)證碼代碼工具類(lèi),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • JS中parseInt()和map()用法分析

    JS中parseInt()和map()用法分析

    這篇文章主要介紹了JS中parseInt()和map()用法,結(jié)合實(shí)例形式分析了parseInt()和map()方法的功能、參數(shù)、具體用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2016-12-12
  • JSON.parse 解析字符串出錯(cuò)的解決方法

    JSON.parse 解析字符串出錯(cuò)的解決方法

    程序中很多數(shù)據(jù)是動(dòng)態(tài)拼接而成的json數(shù)據(jù),最近在用json的時(shí)候老是現(xiàn)JSON.parse錯(cuò)誤
    2010-07-07
  • window resize和scroll事件的基本優(yōu)化思路

    window resize和scroll事件的基本優(yōu)化思路

    在項(xiàng)目中使用scroll事件去加載數(shù)據(jù),結(jié)果IE下悲劇了。下面為大家介紹下window resize和scroll事件的基本優(yōu)化思路,需要的朋友可以參考下
    2014-04-04
  • JavaScript實(shí)現(xiàn)拖動(dòng)模態(tài)框

    JavaScript實(shí)現(xiàn)拖動(dòng)模態(tài)框

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)拖動(dòng)模態(tài)框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • js實(shí)現(xiàn)磚頭在頁(yè)面拖拉效果

    js實(shí)現(xiàn)磚頭在頁(yè)面拖拉效果

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)磚頭在頁(yè)面拖拉效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • TypeScript 中的 .d.ts 文件詳解(加強(qiáng)類(lèi)型支持提升開(kāi)發(fā)效率)

    TypeScript 中的 .d.ts 文件詳解(加強(qiáng)類(lèi)型支持提升開(kāi)發(fā)效率)

    .d.ts 文件在 TypeScript 開(kāi)發(fā)中扮演著非常重要的角色,它們讓我們能夠享受到 TypeScript 強(qiáng)大的類(lèi)型系統(tǒng)帶來(lái)的優(yōu)勢(shì),提高代碼質(zhì)量和開(kāi)發(fā)效率,接下來(lái),我們將深入探討如何為 JavaScript 庫(kù)和自定義模塊創(chuàng)建 .d.ts 文件,以及一些最佳實(shí)踐和注意事項(xiàng),一起看看吧
    2023-09-09

最新評(píng)論