文章目录
- 一、面试题 08.06. 汉诺塔问题
- 1.题目简介
- 2.解题思路
- 3.代码
- 4.运行结果
- 二、21. 合并两个有序链表
- 1.题目简介
- 2.解题思路
- 3.代码
- 4.运行结果
- 三、206. 反转链表
- 1.题目简介
- 2.解题思路
- 3.代码
- 4.运行结果
- 总结
一、面试题 08.06. 汉诺塔问题
1.题目简介
面试题 08.06. 汉诺塔问题
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
2.解题思路
首先,通过分析可以知道,这个题目有三种可能出现的情况:
- 当A上的元素个数n为1时,直接将A上的元素转移到C;
- 当A上的元素个数n为2时,先将A上最上面的1个元素转移到B,再将A上剩余的1个元素转移到C,最后将B上的元素转移到A;
- 当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.解题思路
递归思路:
将两个链表头结点中较小的那个作为最终的头结点进行返回,剩余的节点交给递归。(相当于排除被选中的头结点以后,将新的两个链表进行合并)。
迭代思路:
- 先设定一个新的链表头结点 list3 ,用 l3 遍历 list3
- 分别用指针 l1 和 l2 去遍历 list1 和 list2 ;
- 如果 l1->val < l2->val ,让 l3->next=l1;l1=l1->next;l3=l3->next;
否则,让 l3->next=l2;l2=l2->next;l3=l3->next; - 直到将两个链表中的一个遍历完,再将另一个链表剩余节点全部拼接到l3的next。
- 最终返回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这个位置)
迭代思路:
- 双指针法(准确来说是三指针法,两个指针进行逆转,一个指针进行遍历)
- 用prev指向原链表中cur的前一个节点,用next指向原链表中cur的后一个节点。
- 用prev和cur进行逆转链表,用next遍历链表避免越界(每逆转一次,将prev指向cur,将cur指向next,将next指向next -> next,即往后遍历一个节点)
- 直到将链表遍历结束,即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,小伙伴们可以点击题目简介中的链接进行练习。
如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!