js解leetcode刷题记录(24)-中等

1.二叉树的后序遍历

题目:

给定一个二叉树,返回它的 后序 遍历

思路:后序遍历,就是先遍历左节点,然后遍历右节点,然后遍历根结点。

时间复杂度:O(n),空间复杂度:O(n)

递归:


/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function(root) {
  const res = [];
  const search = (root) => {
    if (!root) return;
    search(root.left);
    search(root.right);
    res.push(root.val);
  };
  search(root)
  return res;
};

 迭代:这里说下迭代。因为是后序遍历,所以要保证根结点在叶子节点后面推入数组,但是正常的遍历肯定是先遍历根结点的。所以第一次遇到有子节点的节点的时候,先剪除叶子节点,然后把根结点推入栈内等待下一次遍历,然后把子节点推入栈。只有在某个节点没有子节点的时候,才推入结果

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function(root) {
  const stack = [root];
  const res = [];
  while (stack.length) {
    const item = stack.pop();
    if (!item) continue;
    if (item.left || item.right) {
      const left = item.left;
      const right = item.right;
      item.left = null;
      item.right = null;
      stack.push(item);
      stack.push(right);
      stack.push(left);
    } else {
      res.push(item.val);
    }
  }
  return res;
};

2.摆动排序II

题目:给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

思路:可以先对数组进行排序,然后将前半段和后半段分开,将后半段穿插进入前半段。因为前半段和后半段有可能是相同的数字,为了避免相同的数字相邻,按照数字从大到小处理

时间复杂度:O(nlogn),空间复杂度O(n)


/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var wiggleSort = function(nums) {
  const l = nums.length;
  nums.sort((a, b) => b - a);
  const left = nums.slice(0, ~~(l / 2));
  const right = nums.slice(~~(l / 2));
  for (let i = 0; i < l; i++) {
    if (i % 2) {
      nums[i] = left.shift();
    } else {
      nums[i] = right.shift();
    }
  }
  return nums;
};

来看看怎么优化。时间复杂度方面,nlogn是因为排序带来的,我们主要是要获取前后两段数组。那么,需要对全部的数字进行排序吗?不需要,我们只需要找到中位数,将数组分成大于中位数、小于中位数的两个子数组即可。

时间复杂度O(n),空间复杂度O(n)

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
const getMid = (left, right, nums) => {
  const l = nums.length;
  let mid = nums[~~(l / 2)];
  const leftL = [],
    rightL = [];
  let v = 0;
  for (const n of nums) {
    if (n > mid) {
      rightL.push(n);
    } else if (n < mid) {
      leftL.push(n);
    } else {
      v++;
    }
  }
  if (Math.abs(left + leftL.length - right - rightL.length) <= v) return mid;
  if (left + leftL.length > right + rightL.length) {
    return getMid(left, rightL.length + right + v, leftL);
  } else {
    return getMid(left + leftL.length + v, right, rightL);
  }
};
var wiggleSort = function (nums) {
  const l = nums.length;
  const mid = getMid(0, 0, nums.slice());
  const left = [],
    right = [];
  let v = 0;
  for (const n of nums) {
    if (n > mid) {
      right.push(n);
    } else if (n < mid) {
      left.push(n);
    } else {
      v++;
    }
  }
  const c = left.length - right.length;
  if (c == 0) {
    left.splice(0, 0, ...new Array(Math.ceil(v / 2)).fill(mid));
    right.push(...new Array(~~(v / 2)).fill(mid));
  } else if (c > 0) {
    v -= c;
    left.splice(0, 0, ...new Array(Math.ceil(v / 2)).fill(mid));
    right.push(...new Array(~~(v / 2) + c).fill(mid));
  } else {
    v += c;
    left.splice(0, 0, ...new Array(Math.ceil(v / 2) - c).fill(mid));
    right.push(...new Array(~~(v / 2)).fill(mid));
  }
  for (let i = 0; i < l; i++) {
    if (i % 2) {
      nums[i] = right.shift();
    } else {
      nums[i] = left.shift();
    }
  }
  return nums;
};

3.奇偶链表

题目:

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

思路:可以将奇数节点放在一个链表,偶数节点放在一个链表,然后两个链表相连

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var oddEvenList = function(head) {
      const pre = new ListNode();
  const ev = new ListNode();
  let res = pre;
  let odd = ev;
  let v = 1;
  while (head) {
    if (v % 2) {
      res.next = head;
      head = head.next;
      res = res.next;
      res.next = null;
    } else {
      odd.next = head;
      head = head.next;
      odd = odd.next;
      odd.next = null;
    }
    v++
  }
  res.next = ev.next;
  return pre.next;
};

4.验证二叉树的前序序列化

题目:

序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。

     _9_
    /   \
   3     2
  / \   / \
 4   1  #  6
/ \ / \   / \
# # # #   # #
例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中 # 代表一个空节点。

给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。

每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 '#' 。

你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 "1,,3" 。

思路:根据逗号分割,每个成员都是一个节点,要么是null要么是正常的节点。对于正常的节点,总节点数+2,而空节点和正常的节点都会消耗一个位置。所以如果某个时刻剩余位置为0,那么结果为false

时间复杂度:O(n),空间复杂度O(n)

/**
 * @param {string} preorder
 * @return {boolean}
 */
var isValidSerialization = function(preorder) {
  const list = preorder.split(",");
  let v = 1;
  for (const n of list) {
    if (v == 0) return false;
    if (n !== "#") {
      v++;
    } else {
      v--;
    }
  }
  return !v;
};

5.重新安排行程

题目:

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。

 

提示:

如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
所有的机场都用三个大写字母表示(机场代码)。
假定所有机票至少存在一种合理的行程。
所有的机票必须都用一次 且 只能用一次。
思路:根据起点和重点,建立map,记录每个起点能到的终点,并排序,深度优先遍历

时间复杂度:O(n),空间复杂度:O(m)。n是总的路过的城市数量,m是行程数量

/**
 * @param {string[][]} tickets
 * @return {string[]}
 */
var findItinerary = function(tickets) {
  const map = {};
  for (const [from, to] of tickets) {
    if (map[from]) {
      map[from].push(to);
    } else {
      map[from] = [to];
    }
    if (!map[to]) {
      map[to] = [];
    }
  }
  for (const key of Object.keys(map)) {
    map[key].sort();
  }
  const res = [];
  const dfs = (city, res) => {
    if (!map[city]) return;
    const arr = map[city];

    while (arr.length) {
      dfs(arr.shift(), res);
    }
    res.unshift(city);
  };
  dfs("JFK", res);
  return res;
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值