剑指offer - 分解让复杂问题简单

1.复杂链表的复制

问题描述:

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

解题思路:

按照正常的思路,首先从头到尾遍历链表,拷贝每个节点的 value 和 next 指针。然后从头再次遍历,第二次遍历的目的在于拷贝每个节点的 sibling 指针。

然而即使找到原节点的 sibling 指针,还是得为了找到复制节点对应的 sibling 指针而再遍历一遍。那么对于 n 个要寻找 sibling 指针的节点,复杂度就是 O(N*N)。

显然,为了降低复杂度,必须从第二次遍历着手。这里采用的方法是,在第一次遍历的时候,把 (原节点, 复制节点) 作为映射保存在表中。那么第二次遍历的时候,就能在 O(1) 的复杂度下立即找到原链上 sibling 指针在复制链上对应的映射。

/*function RandomListNode(x){
    this.label = x;
    this.next = null;
    this.random = null;
}*/

function Clone(pHead) {
  // write code here
  if (!pHead || !pHead.next) {
    return pHead;
  }
  const map = new Map();
  let node = pHead;
  const newHead = new RandomListNode(node.label);
  let newNode = newHead;
  map.set(node, newNode);

  while (node.next) {
    newNode.next = new RandomListNode(node.next.label);
    node = node.next;
    newNode = newNode.next;
    map.set(node, newNode);
  }
  newNode = newHead;
  node = pHead;
  while (newNode) {
    newNode.random = map.get(node.random);
    newNode = newNode.next;
    node = node.next;
  }
  return newHead;
}

2.二叉搜索树与双向链表

问题描述:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

解题思路:

  • 中序遍历一遍二叉搜索树,将节点保存在一个数组中。
  • 遍历数组,更改每个节点的 left 和 right
  • 返回数组第一个元素

时间复杂度是 O(N),空间复杂度是 O(N)。相较于方法二,多开辟了 O(N)的数组空间。

/* function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
} */
function Convert(pRootOfTree) {
  // write code here
  if (!pRootOfTree) {
    return null;
  }
  const nodes = [];
  midTravel(pRootOfTree, nodes);
  const len = nodes.length;
  for (let i = 0; i < len; i++) {
    nodes[i].right = nodes[i + 1] || null;
    nodes[i].left = nodes[i - 1] || null;
  }
  return nodes[0];
}

//中序遍历,将所有节点存在nodes中
function midTravel(node, nodes) {
  if (node.left) {
    midTravel(node.left, nodes);
  }
  nodes.push(node);
  if (node.right) {
    midTravel(node.right, nodes);
  }
}

3.字符串的排列

问题描述:

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a,b,c 所能排列出来的所有字符串 abc,acb,bac,bca,cab 和 cba。

输入描述:输入一个字符串,长度不超过 9(可能有字符重复),字符只包括大小写字母。

输出描述:输出是一个数组

解题思路:

abc 的所有组合可以这么理解:

每次取一个字符出来,比如'a',然后剩下的字符组合成'bc','bc'的所有组合可以通过递归来获取,在 bc 的所有组合前面都拼接一个字符'a';

再取出字符'b',剩下的字符拼接成'ac',同样的方法:'ac'的组合可以通过递归,在'ac'的所有组合前面都拼接一个字符'b',

依次类推...

但是要注意:每次取出来的字符不能与前面的字符相同,所有用一个数组 map 来记录每次取出来的值

function Permutation(str) {
  // write code here
  var arr = [];
  if (str.length === 0) return [];
  if (str.length === 1) {
    arr.push(str);
  } else {
    var map = []; //用来判断是不是每次取出来的字符与前面取出来的是否有重复
    for (let i = 0; i < str.length; i++) {
      var s = str[i]; // 索引为i的字符
      if (!map.includes(s)) {
        var st = str.slice(0, i) + str.slice(i + 1); // 剩下的字符拼接成一个新字符
        var a = Permutation(st); // 递归,找出新字符的排列组合
        a.forEach((ele) => arr.push(s + ele));
      }
      map.push(s); //把s加入到map中
    }
  }
  return arr;
}
posted @ 2020-04-17 17:19  木子呆头  阅读(136)  评论(0编辑  收藏  举报