LeetCode-- 재귀-개념 요약 및 고전적인 주제 분석 요약 (자바 에디션)

목차

재귀 개념 요약 :

1. 재귀의 기본 개념

2. 재귀 자바 코드 템플릿

3. 재귀 적 사고의 요점 :

주제 1 : 피보나치 수열

주제 2 : 계단 오르기

주제 3 : 이진 트리의 최대 깊이

주제 4 : 이진 검색 트리 확인

주제 5 : 괄호 생성


재귀 개념 요약 :

1. 재귀의 기본 개념

내 이전 블로그 요약

https://blog.csdn.net/yezonghui/article/details/106437881이 재귀 요약을 작성했습니다.

2. 재귀 자바 코드 템플릿

(템플릿은 염두에두고 유연하게 사용해야합니다)

public void recur(int level , int param){
    // terminator
    if(level > MAX_LEVEL){
        return;
    }
    // process current logic
    process(level, param);
    // drill down
    recur(level:level+1 , newParam);
    // restore current status
}

3. 재귀 적 사고의 요점 :

 1) 인간의 육체로 재귀하지 마십시오 (시간이 많이 걸리고 마음도 많이 소모되며 프로그램을 작성하기가 쉽지 않습니다. 재귀 코드를 직접 작성하는 것이 좋습니다. )

 2) 최근 가장 간단한 방법을 찾아서 반복 가능한 문제로 분해 ( 반복되는 하위 문제 )

 3) 수학적 귀납적 사고

주제 1 : 피보나치 수열

(제목 링크 : https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/ )

함수를 작성하고 n을 입력 한 다음 피보나치 수열의 n 번째 항을 찾습니다. 피보나치 수열의 정의는 다음과 같습니다.

F (0) = 0, F (1) = 1
F (N) = F (N-1) + F (N-2), 여기서 N> 1.
피보나치 수열은 0과 1로 시작하고 다음은 다음과 같습니다. 피보나치 수는 앞의 두 수를 더하여 얻습니다.

답은 모듈로 1e9 + 7 (1000000007)이어야합니다. 계산의 초기 결과가 1000000008이면 1을 반환하십시오.

예 1 :

입력 : n = 2
출력 : 1
예 2 :

입력 : n = 5
출력 : 5

방법 1 : 자체 방법 : 재귀 사용

자바 소스 코드 :

class Solution {
    public int fib(int n) {
        return (f(n));

    }
    public static int f(int a){
        if(a ==1){
            return 1;
        }else if(a ==0){
            return 0;
        }
        else{
            return (f(a-1)+f(a-2))%1000000007;
        }
    }
}

[참고 :] 위의 방법이 시간 초과되었습니다. 개선하거나 다른 방법을 찾아야합니다.

방법 2 : 개선 된 재귀 방법 : 위의 재귀는 계산을 반복하므로 일부 계산 된 값을 저장할 수 있습니다.

위와 같은 색상이 반복 계산임을 알 수 있습니다. n이 클수록 계산이 더 많이 반복되므로 맵을 사용하여 계산 된 값을 저장할 수 있습니다.

매번 계산할 때 먼저 맵에 있는지 확인하십시오.있을 경우 맵에서 직접 계산하여 가져온 것입니다. 그렇지 않으면 먼저 계산 한 다음 결과를 맵에 저장하십시오. 계산.

즉 : hashmap 구조를 사용하여 계산 된 값을 hashmap과 함께 저장합니다.

자바 소스 코드 :

import java.util.HashMap;
import java.util.Map;

public class fblx {
    public static void main(String[] args) {
        System.out.println(f(5,new HashMap<>()));
    }

    public static int f(int n, Map<Integer,Integer> map){
        if(n<2){
            return n;
        }
        if(map.containsKey(n)){
            return map.get(n);
        }
        int first = f(n-1,map);
        map.put(n-1,first);
        int second = f(n-2,map);
        map.put(n-2,second);
        int result = (first+second)%1000000007;
        map.put(n,result);
        return result;
    }
}

[참고] : 모든 곳에 반환하는 방법

[참고] : 1) Hashmap은 전역 변수로 정의해야합니다.

                  2) f (n), f (n-1), f (n-2)의 계산 결과는 언제든지 해시 맵에 저장되어야합니다.

class Solution {
    Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    public int fib(int n) {
        if(n==0 ){
            return 0;
        }else if(n==1){
            return 1;
        }else{
            if(map.containsKey(n))
            {
                return map.get(n);
            }
            int first = fib(n-1);
            map.put(n-1,first);
            int second = fib(n-2);
            map.put(n-2,second);
            int result = (first+second)%1000000007;
            map.put(n,result);
            return result;
        }
    }
}

주제 2 : 계단 오르기

(링크 : https://leetcode-cn.com/problems/climbing-stairs/ )

계단을 오르고 있다고 가정합니다. 건물 꼭대기에 도달하려면 n 단계가 필요합니다.

매번 1 ~ 2 걸음 올라갈 수 있습니다. 건물 꼭대기에 오르려면 몇 가지 방법이 필요합니까?

참고 : 주어진 n은 양의 정수입니다.

예 1 :

입력 : 2
출력 : 2
설명 : 건물 꼭대기에 오르는 방법은 두 가지가 있습니다.
1. 1 차 + 1 차
2. 2 차
예 2 :

입력 : 3
출력 : 3
설명 : 건물 꼭대기에 오르는 세 가지 방법이 있습니다.
1. 1 차 + 1 차 + 1 차
2. 1 차 + 2 차
3. 2 차 + 1 차

방법 : 재귀 식 f (n) = f (n-1) + f (n-2) (이해와 암기에주의)

for (int i = 3; i <= n; i ++) {                 f3 = f1 + f2;                 f2 = f3;                 f1 = f2; }



public class climbstair {
    public static void main(String[] args) {
        int n =5;
        int f1 = 1;
        int f2 =2;
        int f3 = 0;
        if(n<=2) {
            System.out.println(n);
        }
        else{
            for(int i =3;i<=n;i++){
                f3 = f1 + f2;
                f2 = f3;
                f1 = f2;
            }
        }
        System.out.println(f3);
    }
}

[참고] : 여기서 for 루프는 메모리로 사용됩니다.

문제 1과 마찬가지로 동일한 코드를 사용하여 문제를 해결할 수 있습니다.

class Solution {
    Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    public int climbStairs(int n) {
        if(n <=2){
            return n;
        }else{
            if(map.containsKey(n)){
                return map.get(n);
            }
            int first = climbStairs(n-1);
            map.put(n-1, first);
            int second = climbStairs(n-2);
            map.put(n-2, second);
            int result = first+second;
            map.put(n,result);
            return result;
        }
    }
}

주제 3 : 이진 트리의 최대 깊이

(링크 : https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ )

이진 트리가 주어지면 최대 깊이를 찾으십시오.

이진 트리의 깊이는 루트 노드에서 가장 먼 리프 노드까지 가장 긴 경로의 노드 수입니다.

설명 : 리프 노드는 하위 노드가없는 노드를 나타냅니다.

예 :
주어진 이진 트리 [3,9,20, null, null, 15,7],

    3
   / \
  9 20
    / \
   15 7
은 최대 깊이 3을 반환합니다.

아이디어 : 인간의 재귀를 사용하지 말고, 수학과 재귀 적 사고의 관점에서 시작하십시오.

          매번 왼쪽과 오른쪽 하위 트리의 깊이를 비교하고 거기에있는 하위 트리의 깊이가 더 크므로 다른 쪽을 선택하십시오.

         이 아이디어를 계속하고 항상 반복하십시오.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int l = 1;
    int r =1;
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        int l = maxDepth(root.left);
        int r = maxDepth(root.right);
        return Math.max(l,r)+1;
    }
    
}

위의 재귀 코드를 기억하십시오. 분할 및 정복이라는 아이디어와 비슷합니다.이 좋은 코드 루틴을 명심하십시오.

주제 4 : 이진 검색 트리 확인

(링크 : https://leetcode-cn.com/problems/validate-binary-search-tree/ )

이진 트리가 주어지면 유효한 이진 검색 트리인지 판단하십시오.

이진 검색 트리에 다음과 같은 특성이 있다고 가정합니다.

노드의 왼쪽 하위 트리에는 현재 노드보다 작은 숫자 만 포함됩니다.
노드의 오른쪽 하위 트리에는 현재 노드보다 큰 숫자 만 포함됩니다.
모든 왼쪽 하위 트리와 오른쪽 하위 트리 자체도 이진 검색 트리 여야합니다.

아이디어 : 순서대로 재귀 아이디어를 따르십시오.

         생각 포인트 : 각 하위 트리도 중간 순서를 충족해야하므로 각 재귀의 결과를 반환해야합니다.

                       즉, 특정 하위 트리가 조건을 충족하지 않는 한 즉시 false를 반환합니다.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    TreeNode prev ;
    public boolean isValidBST(TreeNode root) {
        if (root == null)
        return true;
    //访问左子树
    if(!isValidBST(root.left) ){
        return false;
    }
    //if (!isValidBST(root.left))
    //    return false;
    //访问当前节点:如果当前节点小于等于中序遍历的前一个节点直接返回false。
    if (prev != null && prev.val >= root.val)
        return false;
    prev = root;
    //访问右子树
    if(!isValidBST(root.right) ){
        return false;
    }
    return true;
    //return isValidBST(root.right);
    //if (!isValidBST(root.right))
      //  return false;
    //return true;
   
    }
    
}

또는 유사한 코드

class Solution {
   
    public long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        return inorder(root);
    }
    public boolean inorder(TreeNode root){
        if(root == null){
            return true;
        }
        
        if(!inorder(root.left)){
            return false;
        }
        if(pre >= root.val){
            return false;
        }
        pre=root.val;
        return inorder(root.right);

    }
}

주제 5 : 괄호 생성

링크 : https://leetcode-cn.com/problems/generate-parentheses

숫자 n은 대괄호 생성 로그를 나타냅니다. 가능한 모든 효과적인 대괄호 조합을 생성하는 함수를 설계하십시오.

예:

输入 : n = 3
输出 : [
       "((()))",
       "(() ())",
       "(()) ()",
       "() (())",
       "() () ( ) "
     ]

아이디어 : 각 위치에 대해 두 가지 경우가 있거나 왼쪽 괄호가 생성되지 않으면 오른쪽 괄호가 생성됩니다.

          따라서 재귀의 아이디어를 사용할 수 있습니다

키 : 여는 괄호 생성 가능 : 여는 괄호의 수가 제목에서 요구하는 상한에 도달하지 않은 경우

          오른쪽 괄호를 생성 할 수 있습니까? 오른쪽 괄호의 수가 왼쪽 괄호의 수보다 적을 때.

class Solution {
    List<String> res = new ArrayList<String>();
    public List<String> generateParenthesis(int n) {
        generate1(0,0,n,"");
        return res;

    }
    public void generate1(int left, int right, int n, String s){
        if(left == n && right == n){
            res.add(s);
            //System.out.println(s);
            return;
        }
        if(left <n){
            String s1 = s+"(";
            generate1(left+1,right,n,s1);
        }
        if (right < n && right <left){
            String s2 = s+")";
            generate1(left,right+1,n,s2);
        }
    }
}

[참고] :이 유형의 경우 많은 중간 결과가 생성되며 중간 결과는 최종 결과에 배치되어야합니다.

우리는 일반적으로 최종 결과를 전역 변수로 정의하고 중간 결과를 재귀 함수의 매개 변수로 재귀 함수에 넣습니다.

(또한 중간 결과의 유형에주의하십시오. 후속 재귀가 중간 결과의 동적 변경을 유발하는지 여부에 따라 기본 데이터 유형인지 참조 데이터 유형인지 여부)

주제 6 : 값의 정수 거듭 제곱

(주제 링크 https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/ )

double Power (double base, int exponent) 함수를 구현합니다.

밑의 지수 거듭 제곱을 구합니다. 라이브러리 함수를 사용하지 말고 많은 수의 문제를 고려할 필요가 없습니다.

아이디어 : 재귀 적 사고 채택 : Math.pow (x, n) 필요, 먼저 Math.pow (x, n / 2) 요청

주의해야 할 두 가지 사항 :

1) n은 음수 일 수 있으므로 전처리가 필요합니다 .n이 음수 일 때 x는 1 / x가되고 n은 절대 값을 취합니다.

2) n의 절대 값을 취한 후 홀수와 짝수를 두 가지 경우로 나눌 필요가 있습니다.

class Solution {
    public double myPow(double x, int n) {
        if(n<0){
            x = 1/x;
            n = -n;
        }
        return recur(x,n);
    }
    public double recur(double x, int n){
        if(n==0){
            return 1.00000;
        }
        double half = recur(x,n/2);
        if(n%2 ==0){
            return half * half;
        }else{
            return half*half*(double)x;
        }
    }
}

주제 6 : 2 차원 행렬 검색 ||

(제목 링크 : https://leetcode-cn.com/problems/search-a-2d-matrix-ii/ )

m x n 행렬에서 목표 값 목표를 검색하는 효율적인 알고리즘을 작성하십시오. 매트릭스에는 다음과 같은 특성이 있습니다.

각 행의 요소는 왼쪽에서 오른쪽으로 오름차순으로 정렬됩니다.
각 열의 요소는 위에서 아래로 오름차순으로 정렬됩니다.
 


입력 : 행렬 = [[1,4,7,11,15], [2,5,8,12,19], [3,6,9,16,22], [10,13,14,17, 24], [18,21,23,26,30]], target = 5
출력 : true

아이디어 : 검색 범위를 지속적으로 줄이는 방법을 사용합니다. 즉, 큰 문제를 작은 문제로 천천히 전환합니다.

테이블의 가장 왼쪽 발 (현재 요소)에서 시작하여 요소가 목표 값보다 클 때

현재 요소를 위쪽 줄로 이동합니다. 반대로 현재 요소가 대상 값보다 작 으면 현재 요소를 다음으로 이동합니다.

오른쪽으로 한 열 이동합니다.

class Solution {
    boolean flag = false;
    public boolean searchMatrix(int[][] matrix, int target) {
        search(matrix,target,matrix.length-1,0);
        return flag;
    }
    public void search(int[][] matrix, int target,int x,int y) {
        if(x>=0 &&x< matrix.length && y>=0 && y< matrix[0].length){
            if(matrix[x][y] == target){
                flag = true;
            }else if(matrix[x][y] > target){
                search(matrix,target,x-1,y);
            }else if(matrix[x][y] < target){
                search(matrix,target,x,y+1);
            }
        }  
    }
}

[참고 1] : 플래그가 참이면 플래그 플래그를 설정할 수 있습니다. 그렇지 않으면 플래그 값이 변경되지 않습니다.

[주 2] : 사고의 오해 : 2 차원 배열 배열 [x] [y]과 행렬 [x, y]는 동일하지 않은 좌표에 해당합니다! ! !

                                     예를 들어 가장 왼쪽 아래 2 차원 배열은 array [array.length] [0]를 의미하지만 행렬로 표현됩니다.

                                     행렬 [0, 배열 [0] .length-1]

 

추천

출처blog.csdn.net/yezonghui/article/details/112055544