- 反向遍历链表
- 翻转链表
- 合并两个排序的链表
0、基础
基本思想,是把规模较大的一个问题,分解成规模较小的多个子问题去解决,而每一个子问题又可以继续拆分成多个更小的子问题。
最重要的一点就是假设子问题已经解决了,现在要基于已经解决的子问题来解决当前问题;或者说,必须先解决子问题,再基于子问题来解决当前问题。
【A】----依赖---->【B】----依赖---->【C】
我们的终极目的是要解决问题A,那么三个问题的处理顺序如下:
开始处理问题A;
由于A依赖B,因此开始处理问题B;
由于B依赖C,开始处理问题C;
结束处理问题C;结束处理问题B;结束处理问题A
总结:栈的核心思想是递归,递归过程返回的顺序是前进顺序的逆序。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
递归多用 if,选择结构。每次都要判断,层层递进,每次都调用这个函数。看起来清晰但消耗空间大
事实上,链表非常适合递归,因为我们在使用链表的时候都是使用指针,而不像数组那样直接使用一个内存块,因为递归的风险,也就是栈溢出可以避免,并且因为链表涉及到大量的指针操作,使用递归可以让我们的代码更加简洁,而且简洁的代码更不容易犯错,毕竟代码量越大,可能犯错的概率也就越大,尤其是操作指针。
int Fun(int n)
{
if(n==1)
{
return 1;
}
else
{
return n+Fun(n-1);
}
}
迭代用while ,循环结构。消耗空间小。
int sum=0,i=1;
while(i<=n)
{
sum+=i;
i++;
}
1、输入一个链表,按链表值从尾到头的顺序返回一个ArrayList
vector<int> printListFromTailToHead(ListNode* head) {//输入是个指针(地址),返回一个容器vector
ListNode* p=head;
vector<int> vec;
if(p!=NULL)//第一步判断当下指针是否为空
{
if(p->next!=NULL)//第二步的下一步指向的指针不是空才能使用递归
{
vec=printListFromTailToHead(p->next);//递归要符合函数定义:有返回容器类型
}
vec.push_back(p->val);//前序遍历的判断,从最后向前执行
}
return vec;//满足函数定义返回类型
}
2、输入一个链表,反转链表后,输出新链表的表头。
ListNode* ReverseList(ListNode* pHead) {
ListNode* p=pHead;
//如果链表为空或者链表中只有一个元素
if(p==NULL||p->next==NULL)
return p;
else{
//先反转后面的链表,走到链表的末端结点
ListNode* pReverseNode=ReverseList(p->next);
//再将当前节点设置为后面节点的后续节点
p->next->next=p;
p->next=NULL;
// 新表头永远指向原链表的表尾
return pReverseNode;
}
}
3、输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
法一:递归
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
//如果其中一个是空链表,返回另外一个链表的头指针
if(pHead1==NULL)
{
return pHead2;
}
else if(pHead2==NULL)
{
return pHead1;
}
ListNode* result=NULL;
if(pHead1->val<pHead2->val)//这里用if还是while???递归应该是一次性的if,pHead1与pHead2
{
result=pHead1;
result->next=Merge(pHead1->next,pHead2);
}
else
{
result=pHead2;
result->next=Merge(pHead1,pHead2->next);
}
return result;
}
};