LeetCode0021

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4

输出:1->1->2->3->4->4

利用哑节点,简单的合并两链表:

/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function (l1, l2) {
if (!l1) return l2;
if (!l2) return l1; let dummy = new ListNode();
let result = dummy; do {
if (l1.val < l2.val) {
dummy.next = l1;
l1 = l1.next;
} else {
dummy.next = l2;
l2 = l2.next;
}
dummy = dummy.next;
} while (l1 && l2); if (l1) dummy.next = l1;
if (l2) dummy.next = l2; return result.next;
};

但是结果:

执行用时 :80 ms, 在所有 JavaScript 提交中击败了46.30%的用户

内存消耗 :36 MB,在所有 JavaScript 提交中击败了10.12%的用户

看来大家都不利用哑节点提交嘛,我们试试递归。

/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function (l1, l2) {
if (!l1) return l2;
if (!l2) return l1; if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
} else {
l2.next = mergeTwoLists(l1, l2.next);
} return l1.val < l2.val ? l1 : l2;
};

结果:

执行用时 :72 ms, 在所有 JavaScript 提交中击败了81.92%的用户

内存消耗 :36 MB,在所有 JavaScript 提交中击败了11.17%的用户

l1和l2的空间都是已有的,感觉空间复杂度已经无法再减小了。

LeetCode0023

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:[ 1->4->5, 1->3->4, 2->6 ]

输出: 1->1->2->3->4->4->5->6

简单的思考一下可以先排前俩个,然后排序好后的数组再跟下一个排,也就是调用k-1次mergeTwoLists,假如每个数组长度固定为n的话,比较第一个和第二个的时间是O(2n),合并好后成为2n长度的链表,再跟第3个数组merge的时候,时间复杂度则为O(2n+n),也就是O(3n),比较到第K个数组的时候就要O(k*n)的时间。那么每次至少比较2n+3n+4n+...+k*n,当k=n的时候至少是O(n3/2)的时间复杂度。

/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function (lists) {
if (lists === undefined || lists.length === 0) return null;
let result = lists[0];
for (let i = 1, lens = lists.length; i < lens; i++) {
result = mergeTwoLists(result, lists[i]);
}
return result;
};

结果:

执行用时 :792 ms, 在所有 JavaScript 提交中击败了7.48%的用户

内存消耗 :38.6 MB,在所有 JavaScript 提交中击败了63.16%的用户

这个执行时间不是很理想,我们接着尝试。

思路:

  • 假设一开始就让第一个链表成为结果链表;
  • 对于其后的每个链表,遍历每个节点,将节点插入到结果链表里;
  • 如果节点一直比结果链表第一个小,则将节点插入到头节点;
  • 假如节点比结果链表最后一个节点还大,则直接将整个链表插入到结果链表后面即可;
  • 如果找到合适的插入位置了,则将节点插入,并且因为是有序链表,后续的节点值只要从这里开始往后进行比较即可。
  • 遇到新链表了则初始化比较位置。
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function (lists) {
if (lists === undefined || lists.length === 0) return null;
let result = lists[0];
if (lists.length > 1) {
for (let i = 1, lens = lists.length; i < lens; i++) {
let row = lists[i];//有序列表
if (row) {
let pos = result;//记录当前队列比较到哪个位置了,初始值为最开始的节点
let posParent = null;
do {
//找到一个pos使得pos.val>=row.val,此时将row插入到pos前面
while (pos && row.val > pos.val) {
posParent = pos;
pos = pos.next;
} if (posParent === null) {
//一直比第一个小
pos = row;
row = row.next;
pos.next = result;
result = pos;
}
else {
if (pos === null) {
//当前这个节点比已经存在的链表内所有节点都大,直接将当前队列追加上即可比较下一个链表了
posParent.next = row;
pos = row;
break;
}
else {
//找到某个节点使得它比当前节点大
posParent.next = row;
row = row.next;
posParent = posParent.next;
posParent.next = pos;
}
} } while (row);
}
}
}
return result;
};

结果:

执行用时 :300 ms, 在所有 JavaScript 提交中击败了29.06%的用户

内存消耗 :37.3 MB,在所有 JavaScript 提交中击败了97.74%的用户

看这样子我们的执行时间还是很长,但我们几乎没有使用额外的内存,考虑到对上面这个算法的改进,时间应该主要消耗在每次都将比较位置初始化为第一个上了,这样挨个比较节点并且还要把他们进行准确链接很慢。我们现在的内存基本上不是我们的设计瓶颈。

我们新增一个数组,就专门用来存每个单独的节点,然后我们用快速排序将节点按值有序排好,然后再将节点链接起来即可。

  • 遍历所有链表得到所有节点;
  • 快排;
  • 组合节点输出。
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function (lists) {
if (!lists || lists.length === 0) return null;
let result = [];
let lens = lists.length;
let head;
for (let i = 0; i < lens; i++) {
head = lists[i];
while (head && head.val !== null) {
result.push(head);
head = head.next;
}
} let lens2 = result.length;
QuickSort(result, 0, lens2 - 1);
let dummyNode = new ListNode();
head = dummyNode;
while (lens2 > 0) {
dummyNode.next = result.shift();
dummyNode = dummyNode.next;
dummyNode.next = null;
lens2--;
} return head.next;
}; function QuickSort(arr, i, j) {
if (i < j) {
let m = partition(arr, i, j);
QuickSort(arr, i, m - 1);
QuickSort(arr, m + 1, j);
}
} function partition(arr, i, j) {
let pivot = arr[i].val;
let m = i;
for (let k = i + 1; k <= j; k++) {
if (arr[k].val < pivot) {
m++;
swap(arr, k, m);
}
}
swap(arr, i, m);
return m;
} function swap(arr, i, j) {
let tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}

结果:

执行用时 :104 ms, 在所有 JavaScript 提交中击败了78.27%的用户

内存消耗 :38.6 MB,在所有 JavaScript 提交中击败了64.66%的用户

其实还可以进一步优化,快排最重要的是pivot的取值,我们知道我们合并的链表都是有序数组,那么我们的基准值可以不找第一个,而是取中间值,以此我们修正一下partition函数。

function partition(arr, i, j) {
if (i < j) {
let mid = Math.floor((i + j) / 2);
swap(arr, mid, i);
let pivot = arr[i].val;
let m = i;
for (let k = i + 1; k <= j; k++) {
if (arr[k].val < pivot) {
m++;
swap(arr, k, m);
}
}
swap(arr, i, m);
return m;
}
//i===j或者i>j都不需要做任何处理
return -1;
}

结果快了4ms。

LeetCode Day 11的更多相关文章

  1. leetcode笔记11 First Unique Character in a String

    题目描述: Given a string, find the first non-repeating character in it and return it's index. If it does ...

  2. LeetCode:11. ContainerWithWater(Medium)

    原题链接:https://leetcode.com/problems/container-with-most-water/description/ 题目要求:给定n个非负整数a1,a2,...,an  ...

  3. 2017-3-13 leetcode 4 11 15

    ji那天居然早起了,惊呆我了,眼睛有点儿疼,一直流泪....继续保持 ========================================================== leetco ...

  4. 【LeetCode】11. 盛最多水的容器

    题目 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两 ...

  5. 【LeetCode】11. Container With Most Water 盛最多水的容器

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:盛水,容器,题解,leetcode, 力扣,python ...

  6. 【LeetCode】11. Container With Most Water

    题目: Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, a ...

  7. leetcode problem 11 Container With Most Water

    Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). ...

  8. leetcode第11题--Container With Most Water

    Problem: Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate ...

  9. LeetCode OJ 11. Container With Most Water

    Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai).  ...

随机推荐

  1. 提高js性能的方法

    1.文档瘦身 (1)删除注释(版权及法律声明部分应保留),运行时不需要注释. (2)删除制表符.空格和换行符,这些只是为了便于程序的维护,但是与执行无关. (3)替换长的变量名为短的变量名. (4)使 ...

  2. Element.shadowRoot

    Element.shadowRoot http://www.zhuyuntao.cn/shadow-dom的样式/ Shadow DOM的样式 我们已经可以使用原生的操作DOM的方式和使用模板的方式来 ...

  3. 更改php.ini配置

    vi /etc/php.ini #编辑修改 @ini_set('memory_limit',        '64M');                                      / ...

  4. IDA解析so文件异常(Binary data is incorrect maximum possible value is xx)

    错误信息 Binary data is incorrect maximum possible value is 0 错误原因 so文件损坏 或者ida换成32 解决办法 重新获得so文件,或者调整id ...

  5. Python3中bytes和HexStr之间的转换

    1 Python3中bytes和HexStr之间的转换 ByteToHex的转换 def ByteToHex( bins ): """ Convert a byte st ...

  6. h5集成环信在线客服自定义窗口

    自定义客服窗口从底部弹出 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...

  7. mongodb的常见使用命令行

    由于cms工程要连接mongodb所以需要在在cms服务端工程添加如下依赖:项目使用spring data mongodb操作mongodb数据库 <dependency> <gro ...

  8. Python中的encode和decode

    原文地址:http://www.cnblogs.com/tingyugetc/p/5727383.html 1.Python3中对文本和二进制数据进行了比较清晰的区分,文本总是 unicode ,由  ...

  9. teminal / console / shell

    console从应用程序角度看的(控制台是管理员用的,唯一的) teminal从用户角度看的(终端是用户用的) 应用程序与console交互 用户与teminal交互 teminal可以不存在 tem ...

  10. Python实现自动处理表格,让你拥有更多的自由时间!

    相信有不少朋友日常工作会用到 Excel 处理各式表格文件,更有甚者可能要花大把时间来做繁琐耗时的表格整理工作.最近有朋友问可否编程来减轻表格整理工作量,今儿我们就通过实例来实现 Python 对表格 ...