返回

匠心独运:有序链表巧变平衡二叉树,算法妙招令人叹服

前端

引言

算法的世界犹如浩瀚星空,处处闪烁着智慧的星光。今天,我们聚焦于一道经典的题目:将有序链表转换为平衡二叉搜索树。这道看似复杂的题目,背后却蕴藏着算法设计的精妙与优雅。

转换数组法:直观易懂

转换数组法是一种将有序链表转换为二叉搜索树最直接的方法。其核心思路是:

  1. 将链表中的所有元素依次取出,放入一个数组中。
  2. 对数组进行中序遍历,以递归的方式构建二叉搜索树。
function sortedListToBST(head) {
  const arr = [];
  while (head) {
    arr.push(head.val);
    head = head.next;
  }
  return buildBST(arr, 0, arr.length - 1);
}

function buildBST(arr, start, end) {
  if (start > end) return null;
  const mid = Math.floor((start + end) / 2);
  const node = new TreeNode(arr[mid]);
  node.left = buildBST(arr, start, mid - 1);
  node.right = buildBST(arr, mid + 1, end);
  return node;
}

递归回溯法:简洁高效

递归回溯法是一种更简洁高效的解法。其思路是:

  1. 寻找链表的中间节点,作为二叉搜索树的根节点。
  2. 递归地将中间节点的左半部分转换为左子树,右半部分转换为右子树。
function sortedListToBST(head) {
  if (!head) return null;
  const mid = findMiddle(head);
  const root = new TreeNode(mid.val);
  root.left = sortedListToBST(head);
  root.right = sortedListToBST(mid.next);
  return root;
}

function findMiddle(head) {
  let slow = head;
  let fast = head;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
  }
  return slow;
}

递归回溯与中序遍历:巧妙结合

递归回溯与中序遍历相结合的解法,是一种更具技巧性的方法。其思路是:

  1. 中序遍历链表,将元素依次压入栈中。
  2. 递归地将栈顶元素弹出,作为二叉搜索树的根节点。
  3. 递归地将根节点的左半部分和右半部分分别转换为左子树和右子树。
function sortedListToBST(head) {
  const stack = [];
  inorder(head, stack);
  return buildBST(stack);
}

function inorder(head, stack) {
  if (!head) return;
  inorder(head.left, stack);
  stack.push(head.val);
  inorder(head.right, stack);
}

function buildBST(stack) {
  if (stack.length === 0) return null;
  const root = new TreeNode(stack.pop());
  root.left = buildBST(stack);
  root.right = buildBST(stack);
  return root;
}

总结

将有序链表转换为平衡二叉搜索树的算法,为我们展示了算法设计的多样性和巧妙性。从直观的转换数组法到简洁高效的递归回溯法,再到将递归回溯与中序遍历巧妙结合的解法,每一种方法都体现了算法设计师的智慧与创造力。

无论你选择哪种方法,深入理解背后的思想和原理才是最重要的。只有真正掌握算法的精髓,才能在解决实际问题时游刃有余,将算法的魅力发挥到极致。