算法(1)----递归

前序:

递归解决的特点:大问题里面有子问题,子问题里面有子问题,找到重复子问题是关键

对于递归问题的解决,一定要先关注于宏观层面的解决,利用问题的解决

一:汉诺塔

题目:有三个柱子,分别为A,B,C,起初A柱子上有着按照大小顺序堆放的盘子,编写一个代码使得A柱子上的盘子全部转移到C柱子上。

规则:(1)大盘子必须在小盘子的下方

           (2)一次只能移动一只盘子

分析(令盘子的数量为N):

若N = 1,直接将盘子从A移动到C即可

若N = 2,先把一个盘子移动到B,然后把最大的盘子移动到C,最后将B上的盘子移动到C

若N = 3,先把两个盘子移动到B,再将最大的盘子移动到C,最后再将B上的所有盘子移动到C

......

若N = n,先把n-1个盘子 移动到B,再将最大的盘子移动到C,最后再将B上的n-1个盘子移动到C

代码实现(该代码主要使用栈):

首先,应该分为总体分为三个部分,函数头(重复的递归子问题),函数体(重复子问题在做什么),以及递归出口(递归的结束)

函数头:void psf(vector<int>& a, vector<int>& b, vector<int>& c,int n)

(要将A,B,C三个柱子传过去,以及盘子的个数)

函数体的实现:

void psf(vector<int>& a, vector<int>& b, vector<int>& c, int n)
{
    if (n == 1)
    {
        c.push_back(a.back());
        a.pop_back();
        return;
    }
    psf(a, c, b, n - 1);
    c.push_back(a.back());
    a.pop_back();
    psf(b, a, c, n - 1);

}

递归出口:

 if (n == 1)
 {
     c.push_back(a.back());
     a.pop_back();
     return;
 }

二:合并两个有序链表

题目:将两个有序链表合并为一个有序链表

规则:不可以改变每个节点的地址,即不可以开创新节点

分析:

令两个链表的节点分别为m和n

先将两个链表的首节点进行比较,然后再将其后面的进行比较接在比较出来的节点后面

重复子问题:不断将两个链表中后面的节点进行比较以及合并

函数头:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)

函数体如下

typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;


//递归解决合并链表
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    if (list1 == nullptr)
        return list2;
    if (list2 == nullptr)
        return list1;
    if (list1->val >= list2->val)
    {
        list2->next = mergeTwoLists(list1, list2->next);//将后面n-1个节点进行比较和合并
        return list2;
    }
    else
    {
        list1->next = mergeTwoLists(list1->next, list2);
        return list1;
    }
}

递归出口:当节点为空指针,结束

三:反转链表

题目:将一个链表的所有节点进行反转

规则:不可以创建新节点,所有节点的地址不变

分析(令节点个数为n):

重复子问题:不断将前一个节点与后面的节点进行倒置

第一个节点保持不变,将后面的n-1个节点进行倒置,返回值用新节点接受,之后要将头结点连接进链表

函数头:ListNode* reverseList(ListNode* head)

函数体如下:

ListNode* reverseList(ListNode* head) {
    if (head == nullptr || head->next == nullptr)
        return head;
    ListNode* newhead = reverseList(head->next);
    head->next->next = head;
    head->next = nullptr;
    return newhead;
}

递归出口:当节点为空

四:两两交换链表中的节点

题目:两两交换一个链表中的相邻两个节点

规则:只可以节点进行交换

分析:

重复子问题:不断相邻的两个节点进行交换

可以从n-2个开始进行交换,然后再单独对于前两个节点进行交换,最后再将这两个部分连接起来即可

函数头:ListNode* swapPairs(ListNode* head)

函数体的实现:

ListNode* swapPairs(ListNode* head) {
    if (head == nullptr || head->next == nullptr)
        return head;
    ListNode* newlist = swapPairs(head->next->next);
    ListNode* pure = head->next;
    head->next = newlist;
    pure->next = head;
    return pure;
}

递归出口:若两个相邻节点其中有任意一个为空

五:Pow(x,n)快速幂

题目:实现X的N次幂快速计算

分析:

重复子问题:后一个数就等于前一个的X倍

先计算2/n个数的结果,然后根据数据个数是否为偶数,分别进行计算即可,在计算过程中还要考虑n是否为负

函数头:double myPow(double x, int n)        double pow(double x, long long n)

函数体实现:

double myPow(double x, int n) {
    return n>0?pow(x,n):1/pow(x,-(long long)n);
    }
    double pow(double x,long long n)
    {
        if(n==0)
        return 1.0;
        double tmp = pow(x,n/2);
        return n%2==0?tmp*tmp:tmp*tmp*x;
    }

递归出口:n==0

总结:

对于递归一类的题目,要将递归函数具体的实现看做一个黑盒子,默认他一定会完成我们宏观上规定的任务,然后直接进行书写即可。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值