在一個樹型結構數據中,查找相鄰有相同屬性的節點的最大數量的方法

  • 2019 年 10 月 3 日
  • 筆記

本文介紹的是一個在一個樹型數據結構中,查找 type 屬性均為 a 的相鄰節點的最長鏈路的節點數量,如果中間有任何其他節點插在其中,那這個長度就結束了,必須是相鄰的。一個最簡單的示例就是在一個樹型結構中,有兩個節點要進行連接,但是連接的節點如果都是 type 為 a 的話,則最長不能超過5個,超過五個則節點不能進行連接。

如下圖:

 

 

  在進行節點之間的連接時,如過相鄰的節點 type 屬性均為 a 的超過五個就不能連接。假設上圖的節點都有 type 屬性,且都為 a ,則在連接時 2.1 節點是可以和 3.1 節點連接的,而 2.3 是不能和 3.3 進行連接的,因為連接後,最長的到了 6.1 節點,長度超過了 5 個。5.1後面也不能來連接其他 type 為 a 的節點了。那要怎麼判斷在進行連接時這個長度是否超過 5 個,這就是本文要介紹的內容,一個演算法,但是是有前提條件的。

  首先這些節點上要有這些資訊:這個節點的 ID、它父節點的 ID、它子節點的 ID;每個節點都有 type 屬性,標記這個節點是什麼節點;有方法可以獲取所有節點的資訊,方便通過 ID 進行查詢。

有人會說,那該怎麼獲取所有節點的資訊呢?其實只要在創建時,把節點的資訊保存在一個對象中即可,以 ID 作為 key,以其資訊作為 key值;

如下對象中表示:

const nodes = {    ... //還有其他節點資訊    id21: {      nodeId: id21,      type: 'a',      parentNodeIds: {        id11: true,        id12: true,      },      childNodeIds: {        id31: true,        id32: true,      },    },    ... //還有其他節點資訊  }

nodes 里保存著在創建時每個節點的一些資訊,在有改動(連線、斷開連線、刪除)操作時,同時進行數據的修改,則可以通過 nodes 來很方便的獲得所有節點的連接資訊、type 資訊及其他保存在其中的資訊。之後再連接節點時候就可以直接獲取到節點的 type 類型了。

  只有查找相鄰的 type 屬性均為某一值的節點總數的時侯才能用這個方法,當然,查找的思路也可以用到別的地方,看各位的腦洞了。

  先說下思路,在連接時,我們可以先判斷所連接的兩個節點是不是都是 type 為 a,只有都是為 a 的情況下,才有進行後續計算的必要;之後分為向上查詢所連接的相鄰的 type 為 a 的節點組成的最長鏈路層數是幾;還有就是向下查詢所連接的相鄰的 type 為 a 的節點組成的最長鏈路層數是幾;之後通過這兩個值來判斷長度是否有超過 5 個。這就是進行編寫程式碼的一個思路了。

  向上與向下查詢所用的思路是一樣的,都是先查找該節點的所有子節點的類型 type,將 type 為 a 的節點 ID 記錄一下,再將這些子節點的所有子節點都取出來,以作下次運算。這裡要用到遞歸了,遞歸的結束條件就是子節點的 type 類型沒有一個是 a,則遞歸結束了。

直接上偽程式碼:

function statisticalNodeNumber(node1, node2) {    const type1 = node1.type;    const type2 = node2.type;    if (type1 === 'a' && type2 === 'a') {      const beforeNum = statisticalBeforeNodeNumber([node1.nodeId], 0);      const nextNum = statisticalNextNodeNumber([node2.nodeId], 0);      return beforeNum + nextNum > 5;    }  }    function statisticalNextNodeNumber(nodeIds, num) {    let total = num;    let nodeTypeIsA = [];    let childNodeIds = [];    const nodes = nodes; // 獲取所有節點資訊的方法或對象    nodeIds.forEach((val) => {      const type = nodes[val].type;      if (type === 'a') {        // 如果type為a的話,則保存到數組 nodeTypeIsA 中,之後判斷數組 nodeTypeIsA 是否長度為0        // 如果不為0則表示這一層級中有節點 type 為 a,則總數可加一,再取出所有type為a的節點的子        // 節點進行下一輪計算,直到該層級中沒有 type 為 a 的節點為止        nodeTypeIsA.push(val);        const n = nodes[val].childNodeIds ? Object.keys(nodes[val].childNodeIds) : [];        childNodeIds = [...childNodeIds, ...n];      }    });    if (nodeTypeIsA.length === 0) {      return total;    }    ++total;    return statisticalNextNodeNumber(childNodeIds, total);  }    function statisticalBeforeNodeNumber(nodeIds, num) {    let total = num;    let nodeTypeIsA = [];    let beforeNodeIds = [];    const nodes = nodes; // 獲取所有節點資訊的方法或對象    nodeIds.forEach((val) => {      const type = nodes[val].type;      if (type === 'a') {        nodeTypeIsA.push(val);        const n = nodes[val].parentNodeIds ? Object.keys(nodes[val].parentNodeIds) : [];        beforeNodeIds = [...beforeNodeIds, ...n];      }    });    if (nodeTypeIsA.length === 0) {      return total;    }    ++total;    return statisticalNextNodeNumber(beforeNodeIds, total);  }

上面兩個函數最重要想法就是按照層級去查找,只有該層級中有 type 為 a 時,則數量加 1,並只對 type 為 a 的節點的子節點進行後續的計算。所以就可以查出在兩個節點相連時,相鄰的節點type為a的最長數量是多少。

 

以上就是這次分享的內容,希望對大家有所幫助。