这道题要求将k个已排序的链表合并,刚开始看到这个题,首先想到是它上一个题将两个有序链表合并(主要通过一个while循环判断两个链表元素值中哪个大,然后就链接那个链表元素),我接着想直接定义一个链表让它与每一个链表通过调用函数合并,最后就能得到结果。
224ms
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
struct ListNode* head = NULL;
struct ListNode* ans = NULL;
if(l1 == NULL && l2 == NULL) {
return ans;
}
else if(l1 != NULL && l2 == NULL) {
return l1;
}
else if(l2 != NULL && l1 == NULL) {
return l2;
}
if(l1->val <= l2->val) {
head = l1;
l1 = l1->next;
}
else {
head = l2;
l2 = l2->next;
}
ans = head;
while(l1 != NULL && l2 != NULL) {
if(l1->val <= l2->val) {
head->next = l1;
head = head->next;
l1 = l1->next;
}
else {
head->next = l2;
head = head->next;
l2 = l2->next;
}
}
while(l1 != NULL){
head->next = l1;
head = head->next;
l1 = l1->next;
}
while(l2 != NULL){
head->next = l2;
head = head->next;
l2 = l2->next;
}
return ans;
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize) {
struct ListNode* ans = NULL;
int i = 0;
if(listsSize == 0) {
return NULL;
}
else if(listsSize == 1) {
return lists[0];
}
else {
ans = mergeTwoLists(lists[0], lists[1]);
}
for(i=2; i<listsSize; i++) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
直接运行通过,还是很高兴,但是看到运行时间居然是两百多毫秒,这让我这个强迫症患者如何能接受得了,我就开始优化代码。首先肯定找执行次数最多的地方—for循环里面的语句,每次循环就调用mergeTwoLists函数,接着我想或许这个函数有问题,通过一段时间仔细查看,终于发现冗余的地方,那就是函数末尾两个while循环,它本来处理当其中一个链表结束,另一个剩余链表元素。但是这项工作只需运行一次就能实现,而无需重复多次运行,最后我将while改成if,果然大大减少了运行时间。
124ms
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
struct ListNode* head = NULL;
struct ListNode* ans = NULL;
if(l1 == NULL && l2 == NULL) {
return ans;
}
else if(l1 != NULL && l2 == NULL) {
return l1;
}
else if(l2 != NULL && l1 == NULL) {
return l2;
}
if(l1->val <= l2->val) {
head = l1;
l1 = l1->next;
}
else {
head = l2;
l2 = l2->next;
}
ans = head;
while(l1 != NULL && l2 != NULL) {
if(l1->val <= l2->val) {
head->next = l1;
head = head->next;
l1 = l1->next;
}
else {
head->next = l2;
head = head->next;
l2 = l2->next;
}
}
if(l1 != NULL) {
head->next = l1;
}
if(l2 != NULL) {
head->next = l2;
}
return ans;
}
虽然已经减少一半了,但是还没到最好的8ms,对于我这个强迫症患者还是不舒服斯基,于是我继续代码优化,首先我认为mergeTwoLists函数已经没法优化了,那就只能优化主函数,当我实在没办法的时候,我看了一下leetcode后面解决思路,一下子明白问题出在哪,我这个程序以ans链表为主,使其不断与其它链表合并,带来的后果是ans链表越来越长,for循环越到后面,mergeTwoLists函数会花费越来越多时间。而替换方案是,每两个链表合并,得到一组新链表,然后再次两两合并,这样循环下去。两种方案虽然mergeTwoLists函数调用次数无太大区别,但是前一种每次循环就增加时间,而第二种是每次所有链表两两合并后增加时间,比前一种减少了大量累加时间。
8ms
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
struct ListNode* head = NULL;
struct ListNode* ans = NULL;
if(l1 == NULL && l2 == NULL) {
return ans;
}
else if(l1 != NULL && l2 == NULL) {
return l1;
}
else if(l2 != NULL && l1 == NULL) {
return l2;
}
if(l1->val <= l2->val) {
head = l1;
l1 = l1->next;
}
else {
head = l2;
l2 = l2->next;
}
ans = head;
while(l1 != NULL && l2 != NULL) {
if(l1->val <= l2->val) {
head->next = l1;
head = head->next;
l1 = l1->next;
}
else {
head->next = l2;
head = head->next;
l2 = l2->next;
}
}
if(l1 != NULL) {
head->next = l1;
}
if(l2 != NULL) {
head->next = l2;
}
return ans;
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize) {
struct ListNode* ans = NULL;
int i = 0;
if(listsSize == 0) {
return NULL;
}
else if(listsSize == 1) {
return lists[0];
}
else {
while(listsSize > 1) {
for(i=0; i<listsSize/2; i++) {
lists[i] = mergeTwoLists(lists[listsSize-i-1], lists[i]);
}
if(listsSize % 2 != 0) {
lists[i-1] = mergeTwoLists(lists[i-1], lists[i]);
}
listsSize = listsSize / 2;
}
ans = lists[0];
}
return ans;
}