《leetcode》第一阶段汇总(21-25题)

递归算法

如果深入探讨递归,会有非常多的东西。在做这几个题的时候,使用递归能大大简化代码复杂度。在旧认知篇里面,自己提到了递归和迭代是等价的,更准确的说**尾递归和迭代是等价的。**然后做了以下几个题之后,我发现我似乎没有真正理解递归的精髓。本篇也是冰山一角,以后有时间再深入研究。

21. Merge Two Sorted Lists

已知mergeTwoLists返回两个链表合成后链表的头结点,则可以不断划分原问题成更小的子问题,充分利用上面已知的条件,可以写出下面的递归调用。虽然该递归非尾递归,但依然可以用迭代的方式实现。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL)
            return l2;
        if(l2 == NULL)
            return l1;
            
        if(l1->val > l2->val){
            l2->next=mergeTwoLists(l1,l2->next);
            return l2;
            
        }
        else{
            l1->next=mergeTwoLists(l1->next,l2);
            return l1;
        }
        
    }
};

23. Merge k Sorted Lists

该题官网有很多其他的解法,但本文针对类似上题合并两个数的思路进行递归实现:

//未优化版本,和上一题的套路一样
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int record,i,val=99999;
        for(i=0;i<lists.size();++i){
            if(lists[i] != NULL && lists[i]->val <val){
                record=i;
                val=lists[i]->val;
            }
        }
        if(val == 99999)
            return NULL;
        ListNode* tmp=lists[record];
        lists[record]=lists[record]->next;
        tmp->next=mergeKLists(lists);
        return tmp;
    }
};

上面代码虽然AC,但是运行时间过长,可以进行优化:具有相同最小值的节点可以在一趟递归调用中进行处理:

//经过优化后的版本
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int record,i,val=99999;
        for(i=0;i<lists.size();++i){
            if(lists[i] != NULL && lists[i]->val <val){
                record=i;
                val=lists[i]->val;
            }
        }

        if(val == 99999)
            return NULL;
        ListNode* head=lists[record],* pre=NULL;
        for(i=0;i<lists.size();++i){
            if(lists[i] != NULL && lists[i]->val ==val){
                if(pre != NULL)
                    pre->next=lists[i];
                pre=lists[i];
                lists[i]=lists[i]->next;
            }
        }
        pre->next=mergeKLists(lists);
        return head;
    }
};

如果借助其他数据结构,比如优先级堆可以进一步优化上面的时间复杂度,但是会增加空间消耗,以上两个代码的运行时间结果如下:
这里写图片描述

第二种只是做了一点点的优化,可以使运行时间提高10倍以上。与其他用户提交的代码相比较:
这里写图片描述

24. Swap Nodes in Pairs

已经swapPairs返回交换后链表的头结点,如何编写递归取决于如何划分子问题,本题中以2个节点为处理单元,递归调用解决:

class Solution {
public: 
    ListNode* swapPairs(ListNode* head) {
        if(head == NULL|| head->next == NULL)
            return head;
        ListNode* tmp=head->next;
        head->next=swapPairs(head->next->next);
        tmp->next=head;
        return tmp;
    }
};

25. Reverse Nodes in k-Group

与上面相同的思路,以k个节点为处理单元,并递归调用:

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(head == NULL || k==0)
            return head;
        int count=0;
        ListNode* tmp,* pre,* save;
        for(tmp=head;tmp!= NULL && count< k ;tmp=tmp->next,++count);
        if(count != k)
            return head;
        count=0;
        for(tmp=head,pre=NULL;count< k;++count){
            save=tmp->next;
            tmp->next=pre;
            pre=tmp;
            tmp=save;
        }   
        head->next=reverseKGroup(tmp,k);
        return pre;
    }
};

小结

使用递归解决问题颇有动态规划的感觉,如何编程实现取决于如何拆分问题。21题和24题以两个节点拆分问题,23题和25题以k个节点拆分问题。**不论怎么拆分问题,函数的返回结果一直是当前子问题的答案,这点很重要。**再之后的82题、206题等都可以使用递归解决,到以后遇到的时候再次回顾。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值