每天努力多一点,打开刷题,谁tm过情人节啊。
有什么比把快乐带给别人更快乐的呢hahaha
递归别扯三部曲,没有用,比如:什么条件返回应该是本能,而不是靠背过程去找。
这个题,先抽出一个最简单的动作,就是每次找一个最小的数。
给个例子:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
1.我们要做的是排序+存储
1.先进行从小到大排序,[1,1,2,3,4,4],排完一个位置就把这个位置放入一个类似栈的地方,先放着,等完全排完,再进行存储。
2.现在全部进栈了,先拿出4,再拿下一个4,就是这种。
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(list1 == nullptr)
return list2;
else if(list2 == nullptr)
return list1;
else if(list1->val < list2->val) {
list1->next = mergeTwoLists(list1->next,list2);
return list1;
}
else {
list2->next = mergeTwoLists(list1,list2->next);
return list2;
}
}
};
凡是一个while能完成的,理论上递归都可以,所以这道题,so easy。
只稍微说一下这个指针的问题,比如{1,2,3,3,4,5,6}删除的数为val = 4,到了下标4这里指针的next为{5,6},如果node->val == val那么就返回{5,6}给下标3的指针,如果node->val != val 那么就返回{4,5,6}这不就没删除嘛。(我言语不清晰haha)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head == nullptr) return NULL;
head->next = removeElements(head->next,val);
return head->val == val?head->next:head;
}
};
类似上一道题1->2->3->4->5->首先5空过,4与5对调1->2->3->4<-5,3和4对调,1->2->3<-4<-5,如此往复最后变成1<-2<-3<-4<-5。
是不是挺简单的,反正递归进行的时候每一个栈已经找到了每一个结点(我不会说哈,描述有大问题,我去评论区找了给个大佬的描述作者:NoColor96)
class Solution {
/*
第一轮出栈,head为5,head.next为空,返回5
第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,
把当前节点的子节点的子节点指向当前节点
此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
此时链表为1->2->3->4<-5
返回节点5
第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
此时链表为1->2->3<-4<-5
返回节点5
第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
此时链表为1->2<-3<-4<-5
返回节点5
第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
此时链表为1<-2<-3<-4<-5
返回节点5
出栈完成,最终头节点5->4->3-2->1
*/
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode* dududu = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return dududu;
}
};
有手就行吧。
class Solution {
public:
bool isPowerOfTwo(int n) {
if(n == 0) return false;
else if(n == 1) return true;
else if(n%2 == 1) return false;
return isPowerOfTwo(n/2);
}
};
一模一样的题型:
class Solution {
public:
bool isPowerOfThree(int n) {
if(n==0) return false;
else if(n == 1) return true;
else if(n%3 == 1||n%3==2) return false;
return isPowerOfThree(n/3);
}
};
2.342. 4的幂
class Solution {
public:
bool isPowerOfFour(int n) {
if(n==0) return false;
else if(n==1) return true;
else if(n%4 == 1 || n%4 == 2||n%4==3) return false;
return isPowerOfFour(n/4);
}
};
递归可以让链表倒着输出,所以也可以让链表倒着比较。这答案这不手到擒来。
原理就是先用递归把跑到尾,然后从尾一个个退出来,同时头一个个向后走。
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode* end = head;
return equle_head_end(end,head);
}
bool equle_head_end(ListNode* end,ListNode* &head){
if(end == nullptr) return true; //这个条件是出递归的条件,我们放入递归使用的是end,所以也是end当作出递归的条件。
bool answer = equle_head_end(end->next,head);
answer = answer && end->val == head->val;
head = head->next;
return answer;
}
};
很经典。
class Solution {
public:
int fib(int n) {
if(n < 2) return n;
return fib(n-1) + fib(n-2);
}
};
是倒着输出的题哦。
class Solution {
public:
vector<int> answer;
vector<int> reversePrint(ListNode* head) {
if(head == nullptr) return answer;
reversePrint(head->next);
answer.push_back(head->val);
return answer;
}
};
题挺难的,打个比方:
`{A,B,C,D,E},每次死第3个.
康康这个D,每次就是跟着加3除当前总数啊,所以答案显而易见啦。
class Solution {
public:
int lastRemaining(int n, int m) {
if(n == 1) return 0;
int answer = (lastRemaining(n-1,m) + m)%n;
return answer;
}
};
类似题目:
class Solution {
public:
int become(int n,int k){
return n==1?0:(become(n-1,k)+k)%n;
}
int findTheWinner(int n, int k) {
return become(n,k)+1;
}
};
这是一个憨批递归题呢,其实直接C = A直接可以出结果,haha
其实这个题有技术含量的。
我说一下大家对于递归可能就是精通了。
咱们先找一个问题的最简单的解决方法,比如把A的前n-1个放入B,然后把第n个放入C,然后把B中的放入C就好了。 小问题找到了,小问题还要实现的是A的前n-1个放入B,B中放入C。 我们惊奇的发现小问题想要实现的是另外一个类似的小问题哎,这样就等于重复的做一个工作啦,好耶!!!这就是递归啦。
class Solution {
public:
void move(vector<int>& start, vector<int>& free, vector<int>& target,int n){
if(n == 1) {
target.push_back(start.back());
start.pop_back();
return;
}
//A前n-1个放入B
move(start,target,free,n-1);
//A第n个放入C
move(start,free,target,1);
//B的n-1个放入C
move(free,start,target,n-1);
}
void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
move(A,B,C,A.size());
}
};
无非就是十进制加减法,因为他数是倒着的,所以用递归先算最后一个数。
class Solution {
public:
ListNode* sum(ListNode* l1, ListNode* l2,int n){
if(l1 == nullptr && l2 == nullptr && n !=0){
ListNode* k = new ListNode(n);
return k;
}
else if(l1 == nullptr && l2 == nullptr) return l1;
else if(l1 == nullptr && l2 != nullptr){
ListNode* k = new ListNode(l2->val);
l1 = k;
l1->val = l1->val + n;
n = l1->val/10;
l1->val = l1->val %10;
l1->next = sum(l1->next,l2->next,n);
return l1;
}
else if(l1 !=nullptr && l2 == nullptr){
l1->val = l1->val + n;
n = l1->val/10;
l1->val = l1->val %10;
l1->next = sum(l1->next,l2,n);
return l1;
}
else {
l1->val +=l2->val;
l1->val = l1->val + n;
n = l1->val/10;
l1->val = l1->val %10;
l1->next = sum(l1->next,l2->next,n);
return l1;
}
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
l1 = sum(l1,l2,0);
return l1;
}
};
很简单的mid,助我早点摸鱼的题haha
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode* temp = head->next;
head->next = swapPairs(head->next->next);
ListNode* temp1 = head;
head = temp;
head->next = temp1;
return head;
}
};
这个题难点不在于递归,而在于愚蠢的样例。用一个快速幂的小技巧,就是25 = 21*22*22,26=23*23。
建议这个题能理解理解,理解不了背过。
class Solution {
public:
double myPow(double x, int n) {
if(n == 0) return 1.0;
else if(n == -1) return 1/x;
else if(n == 1) return x;
double divisor = myPow(x,n/2);
double remainder = myPow(x,n%2);
return divisor*divisor*remainder;
}
};
类似题型:
class Solution {
public:
long long mod = (int)1e9+7;
int countGoodNumbers(long long n) {
return int(pow(5,(n+1)/2)*pow(4,n/2)%mod);
}
long long pow(int x,long long n){
if(n == 0) return 1;
else if(n == 1) return x;
if(n%2 == 1) return (x*pow(x,n-1))%mod;
long long half = pow(x,n/2);
return half*half%mod;
}
};
这道题这个递归就是逊啦,我的思路是用递归弄一个从后向前排列,另外一个链从前往后排列,每次递归都把最后一个摘下来,插了前面,用i跟j判断是到中间了。(用递归实现慢很正常)
class Solution {
public:
void reorderList(ListNode* head) {
int i = 0;
int j = -1;
ListNode* more = head;
while(more){
j++;
more = more->next;
}
recursive(head,head,i,j);
}
void recursive(ListNode* &start,ListNode* end,int &i,int &j){
if(end == nullptr || end -> next == nullptr) return;
recursive(start,end -> next,i,j);
if(i >= j) return ;
i++;
j--;
ListNode* temp = end->next;
end->next = nullptr;
temp->next = start->next;
start->next = temp;
start = temp->next;
}
};
有分治的思想。递归每次是到了符号左右分两边看,最后是纯数字的时候再开始出栈。
class Solution {
public:
vector<int> diffWaysToCompute(string expression) {
vector<int> temp;
for(int i = 0;i<expression.length();i++){
if(expression[i] == '+' ||expression[i] == '-'||expression[i] == '*'){
vector<int> left = diffWaysToCompute(expression.substr(0,i));
vector<int> right = diffWaysToCompute(expression.substr(i+1));
for(auto l:left)
for(auto r:right){
switch(expression[i]){
case '+':temp.push_back(l+r);break;
case '-':temp.push_back(l-r);break;
case '*':temp.push_back(l*r);break;
}
}
}
}
if(temp.empty()) temp.push_back(stoi(expression));
return temp;
}
};
死抓一个点,到[进入递归,到]出递归。
class Solution {
public:
string decodeString(string s){
string answer="";stack<char> d;int num;
for(int i = 0;i<s.length();i++){
if(s[i]>='a'&&s[i]<='z') answer+=s[i];
else if(s[i] >='0' && s[i]<='9'){
int j = i,num = 0 ;
while(s[j] >='0' && s[j]<='9') j++;
num = stoi(s.substr(i,j-i));i = j-1;
}
else if(s[i] == '['){
d.push('[');
string ddd = decodeString(s.substr(i+1));
for(int l = 0;l<num-1;l++) answer +=ddd;
}
else if(s[i] == ']')
if(d.empty()) return answer;
else d.pop();
}
return answer;
}
};
其实这是道dp,不过我的博客是个递归专题,也就不写记忆啊啥的。因为B每次都选最利于自己的结果,所以当b选择的时候一定要A必胜才能返回true所以用&&,A选择的时候有一个能赢就行,所以用||。
class Solution {
public:
bool buibuibui(vector<int> nums,int left,int right,int game1,int game2,bool num){
if(left > right) return game1 >= game2;
if(num)
return buibuibui(nums,left+1,right,game1+nums[left],game2,0) ||
buibuibui(nums,left,right-1,game1+nums[right],game2,0);
else
return buibuibui(nums,left+1,right,game1,game2+nums[left],1) &&
buibuibui(nums,left,right-1,game1,game2+nums[right],1);
}
bool PredictTheWinner(vector<int>& nums) {
return buibuibui(nums,0,nums.size() - 1,0,0,1);
}
};
这题毛毛雨啦,有手就行。可以看到第n行是第n-1行加上第n-1行的逆置组成。
那就好办了,递归到第二行,01一判断就好啦。
class Solution {
public:
int kthGrammar(int n, int k) {
if(n == 1) return 0;
int length = 2<<n-1;
if(k > length/2) return kthGrammar(n-1,k - length/2) == 0 ? 1:0;
else return kthGrammar(n-1,k);
}
};
类似的题:
class Solution {
public:
char findKthBit(int n, int k) {
if(n==1) return '0';
int mid = 1<<n-1;
if(k == mid) return '1';
else if(k > mid) return findKthBit(n-1,2*mid - k) == '0'?'1':'0';
else return findKthBit(n-1,k);
}
};
递归一定要考虑的是大问题怎么分解,不是上来想分解以后怎么做,先分解大问题,大问题分解完再想小问题。
树是天生可递归的结构,大家如果某些题想用递归可以先画树。
class Solution {
public:
vector<TreeNode*> allPossibleFBT(int n) {
vector<TreeNode*> answer;
if(n == 1){
TreeNode* node = new TreeNode(0);
answer.push_back(node);
}
else if(n%2==0) return answer;
for(int i = 1;i< n;i = i+2)
for(TreeNode* l : allPossibleFBT(i))
for(TreeNode* r: allPossibleFBT(n - 1 - i)){
TreeNode* node = new TreeNode(0,l,r);
answer.push_back(node);
}
return answer;
}
};
首先这个题被各位大数学家证明,一定会出[1111,0001,1110。。。]这样的数据。
我就不给证明了,就简单说一下,让大家理解(市面上的垃圾书就是给大家简单说说,从来不给证明,也就是说我就是垃圾haha)
比如:p = 3
那么结果有:[111,110,101,100,011,010,001]-》
格局放大,除了111剩下的最低位有3个1第二位3个1,最大位有3个1,最小的数是001所以是由3个001与3个110组成。
公式:
补充一句,递归过不了你tm就别写了递归题里。(也可能我菜),大家可以写一下递归试试。
class Solution {
public:
int mod = (int)1e9+7;
int minNonZeroProduct(int p) {
long long max = ((1LL << p) - 1) % mod ;
long long n = (1LL <<(p - 1)) - 1;
long long a = ((1LL << p) - 2) % mod;
for(;n;n >>= 1){
if(n & 1) max = max * a % mod;
a = a * a % mod;
}
return max;
}
};
都刷到这里了,这道题不有手就行吗。
class Solution {
public:
int multiply(int A, int B) {
if(A == 0) return 0;
else if(A == 1) return B;
else if(A==-1) return -B;
int half = multiply(A/2,B);
int rect = multiply(A%2,B);
return half + half + rect;
}
};
别不能用递归的题放了递归分类好吗?不过,小学生题吧。
class Solution {
public:
int sumNums(int n) {
return n*(n+1)/2;
}
};
网友acvv_菜的做法:
class Solution {
public:
int sumNums(int n) {
bool a[n][n+1];
return sizeof(a)>>1;
}
};
树是天生的可递归的,模拟树去做就行,这个左大右小,后是左右根。咱们只要确定左<根,右>根就好了
class Solution {
public:
bool dd(vector<int> postorder,int left,int right){
if(left >= right) return true;
int m = left;
while(postorder[right] > postorder[m]) m++;
int n =m;
while(postorder[m] > postorder[right]) m++;
return m == right && dd(postorder,n,right-1) && dd(postorder,left,n-1);
}
bool verifyPostorder(vector<int>& postorder) {
return dd(postorder,0,postorder.size() - 1);
}
};
困难题不定时更新
毕竟很多都是脑筋急转弯,挺耗时间的。