1. 程式人生 > >在一個樹型結構資料中,查詢相鄰有相同屬性的節點的最大數量的方法

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

本文介紹的是一個在一個樹型資料結構中,查詢 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的最長數量是多少。

 

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