react源码ReactTreeTraversal.js之数据结构与算法

口试中,常常碰到的一个简朴算法题:查找两个单链表的大众节点
近来在读react源码的时刻发明一个react树中对该算法的应用(见getLowestCommonAncestor函数),在此做简朴的纪录。
git地点

getParent

在react树中猎取当前实例节点的父节点实例

//HostComponent组件对应的DOM,比方App的tag=3, 示意为类组件,其child为tag=5对应div元素。
function getParent(inst) {
  do {
    inst = inst.return;
    // TODO: If this is a HostRoot we might want to bail out.
    // That is depending on if we want nested subtrees (layers) to bubble
    // events to their parent. We could also go through parentNode on the
    // host node but that wouldn't work for React Native and doesn't let us
    // do the portal feature.
  } while (inst && inst.tag !== HostComponent);
  if (inst) {
    return inst;
  }
  return null;
}

getLowestCommonAncestor

猎取节点A与B的近来的大众先人节点

算法题:找到两个链表的大众节点

export function getLowestCommonAncestor(instA, instB) {
  //猎取子节点A在树中的深度
  let depthA = 0;
  for (let tempA = instA; tempA; tempA = getParent(tempA)) {
    depthA++;
  }
    //猎取子节点B在树中的深度
  let depthB = 0;
  for (let tempB = instB; tempB; tempB = getParent(tempB)) {
    depthB++;
  }

  // If A is deeper, crawl up.
  // 假如A的高度高,那末A节点先往上走depthA - depthB个节点,末了同时走,直到父节点是同一个
  while (depthA - depthB > 0) {
    instA = getParent(instA);
    depthA--;
  }

    // 假如B的高度高,那末B节点先往上走depthB - depthB个节点,末了同时走,直到父节点是同一个
  // If B is deeper, crawl up.
  while (depthB - depthA > 0) {
    instB = getParent(instB);
    depthB--;
  }

  // Walk in lockstep until we find a match.
  // 如今,指针所处的位置的高度一致,能够同时往上查找,直到找到大众的节点
  let depth = depthA;
  while (depth--) {
    if (instA === instB || instA === instB.alternate) {
      return instA;
    }
    instA = getParent(instA);
    instB = getParent(instB);
  }
  return null;
}

isAncestor

推断A节点是不是是B节点的先人节点

export function isAncestor(instA, instB) {
  while (instB) {
    if (instA === instB || instA === instB.alternate) {
      return true;
    }
    instB = getParent(instB);
  }
  return false;
}

getParentInstance

对getParent的export封装:

export function getParentInstance(inst) {
  return getParent(inst);
}

traverseTwoPhase

对inst及其以上的树实行冒泡捕捉的操纵,实行fn。相似事宜的冒泡捕捉

export function traverseTwoPhase(inst, fn, arg) {
  const path = [];
  //将inst的父节点入栈,数组末了的为最远的先人
  while (inst) {
    path.push(inst);
    inst = getParent(inst);
  }
  let i;
  //从最远的先人最先向inst节点捕捉实行fn
  for (i = path.length; i-- > 0; ) {
    fn(path[i], 'captured', arg);
  }
    //从inst节点最先向最远的先人节点冒泡实行fn
  for (i = 0; i < path.length; i++) {
    fn(path[i], 'bubbled', arg);
  }
}

traverseEnterLeave

当关注点从from节点移出然后移入to节点的时刻,在from实行实行相似移入移出的操纵,from节点

export function traverseEnterLeave(from, to, fn, argFrom, argTo) {
  const common = from && to ? getLowestCommonAncestor(from, to) : null;
  const pathFrom = [];
  while (true) {
    if (!from) {
      break;
    }
    if (from === common) {
      break;
    }
    const alternate = from.alternate;
    if (alternate !== null && alternate === common) {
      break;
    }
    pathFrom.push(from);
    from = getParent(from);
  }
  const pathTo = [];
  while (true) {
    if (!to) {
      break;
    }
    if (to === common) {
      break;
    }
    const alternate = to.alternate;
    if (alternate !== null && alternate === common) {
      break;
    }
    pathTo.push(to);
    to = getParent(to);
  }
  //以上代码将from节点到from与to节点的近来大众先人节点(不包括大众先人节点)push到pathFrom数组
  //以上代码将to节点到from与to节点的近来大众先人节点(不包括大众先人节点)push到pathTo数组

  // 以下代码用于对pathFrom冒泡,实行fn
  for (let i = 0; i < pathFrom.length; i++) {
    fn(pathFrom[i], 'bubbled', argFrom);
  }
    // 以下代码用于对pathTo捕捉,实行fn
  for (let i = pathTo.length; i-- > 0; ) {
    fn(pathTo[i], 'captured', argTo);
  }
}

    原文作者:lhuanyu
    原文地址: https://segmentfault.com/a/1190000018305774
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞