[c++/java]递归系列

本系列是根据个人的做题总结出来,或许有不对之处,望给位大佬指出。同时这个系列也是长期的一个系列,每遇到一个优秀的递归题目,我都会添加上去。

基本递归思路

递归之结束条件

个人认为在写递归之前应该首先考虑什么时候递归结束,或者是递归收敛于什么条件。这样既可以减少递归陷入死循环的几率,也能提供写递归的一个方向。这个条件通常为因为递归而不断变化的值。

递归之变化条件

递归算法通常会使某个值或者数组发生持续性的变化,而在写递归之前我们应该首先清楚这个值的变化有哪些可能。

递归之判断条件

这里的判断条件是针对于递归变化条件而考虑的,而通常来讲递归也往往发生在判断条件当中。

例题

八皇后

递归的经典题目,问题就不细说了

在进行分析前介绍一个八皇后的小技巧:

通常而言对于这种棋盘问题来说,首先要做的就是创建一个棋盘的初始化,一般的棋盘都是二维数组,但对于这道题来说设置一维数组即可,一维数组的默认值也就是下标值当作棋盘的行,数组的内容当作棋盘的列

int map[4] = {2,3,1};//分别指的是第一行第四列,第二行第三列,以此类推

首先是根据上文进行思路分析

int main(){
	check(1);
	return 0;
} 

1.递归之结束条件

八皇后的基本思路是依次安放每一行的皇后,当安放完最后一行时,即可结束递归

void check(int n){
	if(n == Max + 1){
		print();
		return;
	}
}

2.递归之变化条件

递归影响的是每一行皇后列所在的位置,即每一次递归完成后,皇后所在的列会发生变化,而我们的目的是取得所有满足的值,因此需要建立一个for循环,来使皇后的列发生改变

	for(int i = 1; i <= Max; i++){
		array[n] = i;
}

3.递归之判断条件

这里有两个判断条件:

    1.  第n个皇后是否和前面的n-1个皇后在同一列

    2.表示判断第n个皇后是否和第i皇后是否在同一斜线

基于如上考虑首先少不了一个for循环;

然后对于条件一,可以采用依次循环前面数组的值来判断;

而对于条件二可以在坐标中想:

首先 i,n 代表横坐标,array[i],array[n]代表纵坐标,只要两者形成的斜率的绝对值不等于1,那么就满足,可以结合下面代码来看

	for(int i = 1; i <= Max; i++){
		array[n] = i;
		if(juge(n)){
			check(n+1);
		}
	} 
bool juge(int n){
	for(int i = 1; i < n; i++){
		if(array[i] == array[n] || abs(n - i) == abs(array[n] - array[i])){
			return false;
		}
	}
	return true;
}

完整代码

c++

#include<iostream>
#include<stdlib.h>
using namespace std;
const int N = 10;
int Max = 8;
int array[N];
void print(){
	for(int i = 1; i <= Max; i++){
		cout<<array[i]<<" ";
	} 
	cout<<endl;
}
bool juge(int n){
	for(int i = 1; i < n; i++){
		if(array[i] == array[n] || abs(n - i) == abs(array[n] - array[i])){
			return false;
		}
	}
	return true;
}
void check(int n){
	if(n == Max + 1){
		print();
		return;
	}
	for(int i = 1; i <= Max; i++){
		array[n] = i;
		if(juge(n)){
			check(n+1);
		}
	} 
}
int main(){
	check(1);
	return 0;
} 

 java

public class Queue8 {

	//定义一个max表示共有多少个皇后
	int max = 8;
	//定义数组array, 保存皇后放置位置的结果,比如 arr = {0 , 4, 7, 5, 2, 6, 1, 3} 
	int[] array = new int[max];
	static int count = 0;
	static int judgeCount = 0;
	public static void main(String[] args) {
		//测试一把 , 8皇后是否正确
		Queue8 queue8 = new Queue8();
		queue8.check(0);
		System.out.printf("一共有%d解法", count);
		System.out.printf("一共判断冲突的次数%d次", judgeCount); // 1.5w
		
	}
	
	
	
	//编写一个方法,放置第n个皇后
	//特别注意: check 是 每一次递归时,进入到check中都有  for(int i = 0; i < max; i++),因此会有回溯
	private void check(int n) {
		if(n == max) {  //n = 8 , 其实8个皇后就既然放好
			print();
			return;
		}
		
		//依次放入皇后,并判断是否冲突
		for(int i = 0; i < max; i++) {
			//先把当前这个皇后 n , 放到该行的第1列
			array[n] = i;
			//判断当放置第n个皇后到i列时,是否冲突
			if(judge(n)) { // 不冲突
				//接着放n+1个皇后,即开始递归
				check(n+1); //  
			}
			//如果冲突,就继续执行 array[n] = i; 即将第n个皇后,放置在本行得 后移的一个位置
		}
	}
	
	//查看当我们放置第n个皇后, 就去检测该皇后是否和前面已经摆放的皇后冲突
	private boolean judge(int n) {
		judgeCount++;
		for(int i = 0; i < n; i++) {
			if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i]) ) {
				return false;
			}
		}
		return true;
	}
	
	//写一个方法,可以将皇后摆放的位置输出
	private void print() {
		count++;
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i] + " ");
		}
		System.out.println();
	}

}

反转链表

反转链表

题目

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点

数据范围

链表长度 [0,30]。 

样例

输入:1->2->3->4->5->NULL

输出:5->4->3->2->1->NULL

 思路

首先基本的思路是,将旧链表反转到一个新的链表中,并且最后旧链表为空

结束条件

当旧链表为空时结束递归

变化条件

链表中每个节点指向会随着递归发生变化

判断条件

这里不需要

特殊条件

因为链表发生了反转,就需要一个节点指向旧链表的尾节点,这里就是使用递归的方式遍历到了旧链表的尾节点

c++/递归

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

java/非递归

public static void reverse(HeroNode head){
    if(head.next == null || head.next.next == null){
        return;
    }
    HeroNode cur = head.next;
    HeroNode next = null;//指向cur的下一个节点
    HeroNode reversehead = new HeroNode(0, "", "");
    while(cur!= null){
        next = cur.next;//先将cur的下一个节点保存下来
        cur.next = reversehead.next;//将cur的下一个节点指向新的链表的最前端
        reversehead.next = cur;//将cur链接到新的链表上
        cur = next;
    }
    head.next = reversehead.next;
}

=========================================================================

接下来的几道题都是着重于处理递归的变化条件

递归实现指数型枚举

题目:

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式:

输入一个整数 n

输出格式:

每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 11 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围

1≤n≤15 

输入样例

输出样例


3
2
2 3
1
1 3
1 2
1 2 3

 代码:

#include <iostream>
using namespace std;

int n;
bool vis[20];
// u是当前枚举到的数,state是二进制数记录哪些数被选
void dfs(int u, int state) {
    if (u == n) {
        for (int i = 0; i < n; i ++)
            if (state >> i & 1)
                cout << i + 1 << " ";
            cout << endl;
        return ;
    }

    dfs (u + 1, state);  // 不用u这个数
    dfs (u + 1, state | (1 << u)); // 用u这个数
}

int main() {
    cin >> n;
    dfs(0, 0);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值