【递归、搜索与回溯算法练习】day1


一、面试题 08.06. 汉诺塔问题

1.题目简介

面试题 08.06. 汉诺塔问题
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
在这里插入图片描述

2.解题思路

首先,通过分析可以知道,这个题目有三种可能出现的情况:

  1. 当A上的元素个数n为1时,直接将A上的元素转移到C;
  2. 当A上的元素个数n为2时,先将A上最上面的1个元素转移到B,再将A上剩余的1个元素转移到C,最后将B上的元素转移到A;
  3. 当A上的元素个数n大于2时,先将A上最上面的n - 1个元素转移到B,再将A上剩余的1个元素转移到C,最后将B上的n - 1个元素转移到A。
    将A上剩余的1个元素转移到C时,此时B上的元素可以放A/C上,相当于将n个元素的问题化简为n-1个元素的问题(只需要将A和B柱子的位置进行交换,就能实现)。

因此,我们可以将问题转化为先将A上n-1个元素转移到B,再将A上剩余的1个元素转移到C,最后将B上的n-1个元素转移到C。

3.代码

class Solution {
    
    
public:
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
    
    
      dfs(A, B, C, A.size());//这个函数的逻辑就是将A上的元素转移到C上
    }
    void dfs(vector<int>& A, vector<int>& B, vector<int>& C, int n)
    {
    
    
      if(n == 1)//A上面只有一个元素的话,将A的元素直接放到C位置
      {
    
    
        C.push_back(A.back());
        A.pop_back();
        return;
      }
      dfs(A, C, B, n - 1);//将A上的n-1个元素先转移给B
      //将A上最后一个元素直接移到C上
      C.push_back(A.back());
      A.pop_back();
      dfs(B, A, C, n - 1);//将B上的n-1个元素移动到C
    }
};

4.运行结果

在这里插入图片描述

二、21. 合并两个有序链表

1.题目简介

21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
在这里插入图片描述

在这里插入图片描述

2.解题思路

递归思路:
将两个链表头结点中较小的那个作为最终的头结点进行返回,剩余的节点交给递归。(相当于排除被选中的头结点以后,将新的两个链表进行合并)。
迭代思路:

  1. 先设定一个新的链表头结点 list3 ,用 l3 遍历 list3
  2. 分别用指针 l1 和 l2 去遍历 list1 和 list2 ;
  3. 如果 l1->val < l2->val ,让 l3->next=l1;l1=l1->next;l3=l3->next;
    否则,让 l3->next=l2;l2=l2->next;l3=l3->next;
  4. 直到将两个链表中的一个遍历完,再将另一个链表剩余节点全部拼接到l3的next。
  5. 最终返回list3->next即可。

3.代码

递归:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    
    
        if(list1 == nullptr) return list2;
        if(list2 == nullptr) return list1;
        if(list1 -> val < list2 -> val) //当l1的当前节点小于l2时,我们就返回l1的这个节点并遍历l1(可能会修改它的next节点)
        {
    
    
            list1 -> next = mergeTwoLists(list1 -> next, list2);
            return list1;

        }
        else//同理
        {
    
    
            list2 -> next = mergeTwoLists(list1, list2 -> next);
            return list2;
        }
    }
};

迭代:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    
    
        ListNode* list3 = new ListNode;
        ListNode* l1 = list1, *l2 = list2, *l3 = list3;
        while(l1 && l2)
        {
    
    
            if(l1 -> val < l2 -> val)
            {
    
    
                l3 -> next = l1;
                l1 = l1 -> next;
                l3 = l3 -> next;
            }
            else
            {
    
    
                l3 -> next = l2;
                l2 = l2 -> next;
                l3 = l3 -> next;
            }
        }
        if(l1)
        {
    
    
            l3 -> next = l1;
        }
        else
        {
    
    
            l3 -> next = l2;
        }
        return list3 -> next;
    }
};

4.运行结果

递归:
在这里插入图片描述
迭代:
在这里插入图片描述

三、206. 反转链表

1.题目简介

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.解题思路

递归思路
将当前节点之后的链表节点进行逆转,再将当前节点放置到逆转后的链表之后(即,head -> next -> next = head,因为head->next经过逆转此时是逆转后链表的最后一个节点,因此可以直接将head放置到head -> next -> next这个位置)

迭代思路

  1. 双指针法(准确来说是三指针法,两个指针进行逆转,一个指针进行遍历)
  2. 用prev指向原链表中cur的前一个节点,用next指向原链表中cur的后一个节点。
  3. 用prev和cur进行逆转链表,用next遍历链表避免越界(每逆转一次,将prev指向cur,将cur指向next,将next指向next -> next,即往后遍历一个节点)
  4. 直到将链表遍历结束,即next为nullptr。

注意:由于每次逆转都是在循环体内进行,因此当next为空时,cur还有最后一次逆转未完成,因此需要补上

3.代码

递归:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    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;
    }
};

迭代:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        if(head == nullptr || head -> next == nullptr) return head;
        ListNode* prev = nullptr;
        ListNode* cur = head;
        ListNode* next = head -> next;
        while(next)
        {
    
    
            cur -> next = prev;
            prev = cur;
            cur = next;
            next = next -> next;
        }
        cur -> next = prev;
        return cur;
    }
};

4.运行结果

递归:
在这里插入图片描述
迭代:
在这里插入图片描述


总结

今天是递归、搜索与回溯算法练习的第1天。
良好的开端是成功的一半,加油。
文中题目均来源于Leetcode,小伙伴们可以点击题目简介中的链接进行练习。
如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

猜你喜欢

转载自blog.csdn.net/xjjxjy_2021/article/details/132012200
今日推荐