合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
方法一:
使用vector数组存多个链表的所有节点,进行从小到大的排序,完成后再进行元素的指向,从第一个元素指向最后一个元素
bool cmp(ListNode *l1, ListNode *l2) { return l1 -> val < l2 -> val;
}ListNode* mergeKLists(vector<ListNode*>& lists) { vector<ListNode *> node_vec;for (int i = 0;i < lists.size(); i) { while(lists[i]){ node_vec.push_back(lists[i]);lists[i] = lists[i] -> next;}}if (node_vec.size() == 0) { return NULL;}//从小到大排序std::sort(node_vec.begin(), node_vec.end(), cmp);for(int i = 1;i < node_vec.size(); i) { node_vec[i-1] -> next = node_vec[i];}//将最后一个元素的next指针指向空node_vec[node_vec.size() - 1] -> next = NULL;return node_vec[0];
}
时间复杂度:
设有k个链表,平均每个链表有n个节点
kNlogkN kN = O(kNlogkN)
方法二:
分治法,即将所有链表分治合并为两个链表,再将两个链表合并为一个有序链表
实现如下:
//合并两个链表
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode new_head(0);ListNode *pre_new = &new_head;while(l1 && l2) { if (l1 -> val < l2 -> val) { pre_new -> next = l1;l1 = l1 -> next;} else { pre_new -> next = l2;l2 = l2 -> next;}pre_new = pre_new -> next;}if (l1) { pre_new -> next = l1;} if (l2) { pre_new -> next = l2;}return new_head.next;
}ListNode* mergeKLists(vector<ListNode*>& lists) { if (lists.size() == 0) return NULL;if (lists.size() == 1) return lists[0];if (lists.size() == 2) { return mergeTwoLists(lists[0],lists[1]);}int mid = lists.size() / 2;vector<ListNode *> sub_list1;vector<ListNode *> sub_list2;for (int i = 0;i < mid; i ) { sub_list1.push_back(lists[i]);}for (int i = mid;i < lists.size(); i) { sub_list2.push_back(lists[i]);}/*分治链表节点,两两合并*/ListNode *l1 = mergeKLists(sub_list1);ListNode *l2 = mergeKLists(sub_list2);return mergeTwoLists(l1,l2);
}
时间复杂度:
设有k个链表,平均每个链表有n个节点
第1轮,进行k/2次,每次处理2n个数字;
第2轮,进行k/4次,每次处 理4n个数字;…;
最后一次,进行k/(2logk)次,每次处理2logk*N个值。
2N*k/2 4N * k/4 8N * k/8 … 2^logk * N * k/(2^logk) =Nk Nk … Nk = O(kNlogk)
方法三:
使用队列,将K个链表插入队列,两两合并后放入队尾,直到队列中只有一个元素
实现如下:
ListNode* mergeKLists(vector<ListNode*>& lists) { if (lists.size() == 0) { return NULL;}if (lists.size() == 1) { return lists[0];}//将vector转为队列queue<ListNode*> waiting(deque<ListNode*>(lists.begin(), lists.end()));while(waiting.size() > 1) { ListNode *l1 = waiting.front();waiting.pop();ListNode *l2 = waiting.front();waiting.pop();ListNode *p = mergeTwoLists(l1,l2);//见如方法二中的实现waiting.push(p);}return waiting.front();
}
时间复杂度:
(k-1)*n (k - 2)*n … n = n(k-1)*k/2
O(nk^2)
题目:链表中倒数第k个节点 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。 示例: 给定一个链表: 1->2->3->4->5, 和 k =...
题目:从尾到头打印链表 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 示例 1: 输入:head = [1,3,2] 输出:[2,3,1] 限制: 0 <= 链表长度 <= 10000 题目: /*** Definition for singly-linked list.* struct Lis...
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例:输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 解法一:双指针法 时间复杂度:O(a+b) 循环比较两个子问题的次数为 a+b a,b为两个子问题的长度空间复杂度:O(1) 双指针,常数级别复杂度...
这是一道经典的面试题,下面是我的研究和举一反三,特整理如下: 分为三种情形: (1)删除有序链表的重复节点,重复节点一个都不留 (2)删除有序链表的重复节点,重复节点只留一个 (3)删除无序链表的重复节点,重复节点只留一个 下面是相关节点的定义: typedef struct ListNode {int val;struc...
下面是后面链表相关题目中需要用到的链表结点的定义和相关操作函数,参见下面的list.h文件: 注意链表结点的定义采用cpp的定义方式,它会被cpp的文件调用。比如后面删除链表重复结点的文件del_repeated_list.cpp中的编译方式: g++ -g del_repeated_list.cpp -o del_repeate...