데이터 구조 및 알고리즘의 미국 연구 노트 : 세션 텐

첫째, 개방 문제

위원회에 추천 등록 다시 나는 당신이 당신에게 익숙하지한다고 생각이 함수? 이제 응용 프로그램의 많은이 기능이 있습니다. 이 기능은, 사용자 A는 사용자 B, 사용자 B를 등록 할 것을 권장하고, 사용자 C는 등록 할 것을 권장한다. 우리는 사용자 C의은 사용자 A,은 "권장 최종"고 말할 수있는
사용자 A에 대한 또한 사용자 B "최종 추천"와, 사용자 A가 전혀 없다 "최종 심판."

일반적으로, 우리는 데이터베이스를 통해이 관계를 기록하는 것이 좋습니다. 데이터베이스 테이블에서, 우리는 권장 ID를 나타냅니다 referrer_id, actor_id 사용자 ID를 나타내는 두 개의 ⾏ 데이터를 기록 할 수 있습니다.

이러한 배경을 바탕으로, 내 질문은, 사용자 ID 부여 방법 찾기 위해 사용자의 "최종 추천"? 이 문제로, 우리는 내 오늘 배울 내용, 재귀 (재귀)

둘째, 어떻게 재귀를 이해하기

1, 영화 케이스

주말에, 당신은, 우리가 지금 아 처음 몇 행에 앉아 그의 여자 친구, 여자 친구 물어와 함께 영화를 보러? 너무 어두운 영화가 아닌 수를 확인하기 위해 내부, 어떻게 당신이 지금해야합니까?

처음 몇 행에 문제에 대한 재귀 솔루션을 사용하여 2,

당신이 프로그래머 잊지 마세요, 이것은 재귀 편리 뗏목을 시작, 당신을 이길 수 없습니다 :

  1. 당신은 그들이 무엇에 왔다는 것을 알고 그만큼 당신이 그에게 번호를 추가하고 싶은대로하는 사람의 처음 몇 행의 맨 앞줄을 물어 그래서.
  2. 그러나, 나는 아, 볼 수없는 사람들의 앞에, 그래서 그는 사람들 앞에서 그에게 물었다. 행과 행을 물어 전달 그래서, 사람들의 첫 번째 행까지 물어, 나는 첫 번째 행에서 말
  3. 이러한 숫자는 다시 통과 한 후 행에 의해 행. 그는 몇 행 어디 당신 앞에있는 사람이 당신에게 때까지, 당신은 답을 알고 있습니다.

이 문제를 해결 꽤 표준 재귀 분해 과정이다 :

"배달"이라는 프로세스에 다시 프로세스는 "정상화"라고합니다 . 기본적으로 모든 재귀 재귀 식을 나타내는 데 사용될 수있다. 그냥이 세상의 예는, 우리는 다음과 같이 표현된다 재귀 공식을 사용 :

F (N) = F (N-1) +1이고, F (1) = 1

세 가지 조건을 충족 셋째, 재귀 필요

이 단지 재귀의 아주 전형적인 예입니다, 당신은 재귀를 사용할 수있는 어떤 종류의 문제를 해결하기 위해? 나는만큼 당신이 해결하기 위해 재귀를 사용하여, 다음과 같은 세 가지 조건을 충족, 세 가지 조건을 요약.

문제 1. 솔루션은 문제의 여러 하위 솔루션으로 분해 될 수있다

문제의 아이는 무엇입니까?

하위 문제는 작은 문제의 데이터 크기입니다. 예를 들어, 영화의 앞의 예로 말하면,

당신은 "자신의 행에있는"문제는 이러한 문제 아이 "행의 앞줄에 사람이"로 분해 될 수있다, 알고 싶어요.

정확히 같은 사고를 해결하는 데이터의 다른 크기뿐만 아니라 분해 후 하위 문제 2. 문제,

예를 들어, 영화관 그 예를 들어, 당신은 "자신의 행이있는"아이디어가 완전히 동일 해결하기 위해 생각하고, 사람들이 줄 앞에 "하는 자신의 행을"해결했다.

재귀 종료 조건 3. 존재

서브 - 서브 문제로 분해 한 후 소 문제 핸들 질문에 문제는 층 분해에 의해 층이 계속 종료 조건을 필요 무한한 루프가 아니다.

또는 영화의 예는, 첫 번째 행이 사람을 물어 계속할 필요가 없습니다, 그들은 종료 조건입니다 행, F (1) = 1, 재귀가 뭔지 알아.

넷째, 어떻게 재귀 코드를 작성하는

1, 계단의 사건을 맡아

가 N 단계의 수는, 때마다 당신은 단계 또는 두 단계,이 n 개의 단계 얼마나 많은 방법을 물어 가서 교차하는 경우? 일곱 단계가있는 경우 수행 할 수 있습니다
이처럼 2,2,2,1, 당신은 또한이 같은 1,2,1,1,2 최대, 짧은에, 법이 많이있다, 그것을 사용하는 방법 프로그래밍 얼마나 많은 종류의 총을 얻을 수 있습니다 법을?

1, 작은 문제가 큰 문제의 법칙

우리는 방법의 첫 번째 단계에 따라 신중하게 낮은 사실, 모든 방법은 두 가지 범주로 나눌 수 있습니다 싶어

  • 첫 번째 범주는 한 단계를 갈 수있는 첫 번째 단계입니다,
  • 다른 하나는 두 단계를 갈 수있는 첫 번째 단계입니다.

그래서 방법 단계 이후에, N-2 및 거리 스텝 번호 N-1 차 이동 동일하고, n-1 개의 2 차 이동과 함께 번째 단계로 이동한다. 이 배합

F (N) = F (N-1) + F (N-2)

2 기록 재귀 수식

우리는 표현을 걸어, 종료 조건으로 두 단계를 F (2) = 2를 넣을 수 산보 마무리 개의 이동하고, 한 단계 또는 두 단계가있다.

3, 고의적 종료 조건,

따라서, 재귀 종료 조건은 F (1) = 1, F이다 (2) = 2. 이 시간, 당신은 탈환 N = 3, N = 4 종료 조건이 적절하고 정확한지 확인 할 수 있습니다.
우리는 재귀 그냥 함께 넣어 얻을 재귀 공식 종료 조건이 있습니다 :

F (1) = 1;
F (2) = 2;
F (N) = F (N-1) + F (N-2)

4, 재귀 수식과 코드 종료 조건으로 번역했다.

이 공식, 우리는 재귀 코드로 변환이 훨씬 간단합니다. 다음과 같이 최종 재귀 코드는 다음과 같습니다

INT의 F (INT 않음) {
  경우 (N == 1) 창 1;
  경우 (N == 2) 창 2;
  리턴 F (N-1) + F (N-2);
}

나는 요약 :

  1. 재귀 코드를 작성하는 것이 핵심이 쓰기 재귀 공식에 따라 법의 작은 문제가 큰 문제를 분해하고 방법을 찾는 것입니다,
  2. 그리고 조사의 종료 조건
  3. 마지막으로, 번역 종료 및 조건 코드로 공식 재귀

2, 생각의 오류는 재귀

난 그냥 말할 첫 번째 예와 같이, 인간의 뇌는 단계별로 전체 "배달"와 "정상화"공정 단계를 넣어 거의 방법입니다, 우리는 취소합니다.

1, 컴퓨터는 그래서 재귀 긍정적 인 식욕의 반복적 인 일을 잘합니다.
(2)는, 인간의 두뇌와 우리는 사고의 간단한 방법을 선호합니다. 우리가 재귀를 볼 때, 우리는 항상 재귀, 타일 확장하려는
3, 그의 마음의 뜻을주기는 한 단계 아래로 이동 한 다음 한 단계, 반환
컴퓨터의 모든 단계에서 그림을 시도 4, 이 주변에 이동하기 쉽고, 그래서 어떻게 수행 할 수 있습니다.

재귀 코드의 경우 즉, 전체 납품 관행과 실제로 생각하는 오류를 입력 정상화 과정을 취소했습니다. 많은 경우에, 우리는 그들이이 질환의 자신의 이해를 만들기 위해 자신의 주로하기 때문에, 더 어려운 이해해야한다.

3, 재귀 적 사고의 올바른 이해

즉, 무엇을 생각하는 올바른 방법이 될 것인가?

문제가 몇 가지 단어 문제 B, C, D,로 분해 될 수있는 경우에 1,
2, 당신이 그 하위 문제 B를 가정 할 수있다, C, D가 해결되었습니다, A.에 근거하여 문제를 해결하는 방법에 대한 생각 또한, 당신은 단지 문제 및 하위 메뉴 문제 B 사이의 관계에 대해 생각해야, C, D이,
3 층 및 하위 하위 하위 문제 문제 아래로 생각하지 않아도, 서브 서브 서브 서브 서브 - 문제 문제의 관계. 재귀 세부 사항은 다음과 같이 이해하기 훨씬 쉽게 마스크.

따라서, 재귀 코드를 작성하는 열쇠는 긴 만남의 재귀로, 우리가 말했듯이, 재귀 공식에 추상화 레이어 사이의 관계를 호출하지 않고, 재귀 뇌를 사용의 각 단계를 분해하려고하지 않는다는 것입니다.

V. 재귀 코드는 경계 스택 오버플로 될 수 있습니다

1 스택 오버플 위험

스택 오버 플로우가됩니다 전신 붕괴의 원인 , 결과는 매우 심각 할 것

2. 왜 재귀 코드는 오버 플로우 그것을 스택을 일으킬?

  • 함수 호출 스택은 임시 변수를 저장하는 데 사용됩니다.
  • 함수에 대한 각 호출은 스택 메모리로 가압되는 임시 변수 스택 프레임에 캡슐화 될
  • 기능, 실행 반환 및 기타 스택 전에 완료된다.

시스템 스택 또는 가상 머신 스택 공간은 일반적으로 크지 않다. 대규모 데이터의 재귀 용액 깊은 레벨을 호출하는 경우, 스택으로 푸시 된 스택 오버 플로우의 위험이

우리가 시스템 또는 1킬로바이트에 JVM 스택 크기를 적층하는 경우와 같은 앞에서 언급 한 영화의 예로는, 다음 스택 해결 F (19999)에가 주어질 것이다 :

스레드에서 예외 "주요"java.lang.StackOverflowError의

3, 어떻게 오버 플로우 그것은 우리가 스택을 방지합니까?

우리는이 문제를 해결하는 코드의 최대 깊이 방식으로 재귀 호출을 제한 할 수 있습니다.

  • 재귀 호출이 특정 깊이를 (예 : 1000)를 초과 한 후, 우리는 반복적으로 아래로 계속, 직접 오류를 반환 할 수 없습니다.
  • 영화 예를한다는 또는, 우리는이 같은 다음으로 변환 할 수있다, 당신은 스택 오버 플로우를 방지 할 수 있습니다.
  • 그러나, 나는 코드를 쓴 의사 코드, 간결 코드입니다, 일부 경계 조건은 같은 X <= 0로 간주되지 않습니다.
// 전역 변수는 재귀의 깊이를 나타냅니다.
INT 깊이 = 0;

INT의 F (INT 않음) {
  ++ 깊이;
  (깊이> 1,000) 투사 예외 경우;
  
  경우 (N == 1) 창 1;
  리턴 F (N-1) + 1;
}

최대 현재 스레드 스택 공간 나머지에 대해 재귀 깊이를 허용하기 때문에,이 방법으로 문제가 해결되지 않습니다,

그것은 사전에 계산 될 수 없다. 실시간 계산, 코드가 너무 복잡하면 코드의 가독성에 영향을 미칠 것입니다.

최대 깊이 비교 효율 등 10, 50, 당신은이 방법을 사용할 수 있습니다 경우에 따라서, 그렇지 않으면이 방법은 매우 실용적되지 않습니다.

여섯째는, 재귀 코드는 이중 계산주의해야합니다

1 나타내는 이중 계산 사례

재귀를 사용하는 경우 또한, 이중 계산이 될 것입니다. 우리는 다음과 같이 아래로 전체 순환 과정 나누기를 넣으면 그냥, 이야기 한 재귀 코드의 첫 번째 예는 다음이 있습니다 :

도면에서 우리는 시각적 계산 F (4), F (3) 계산 된 F (3) 및 (4) 필요 F 계산 따라서, F (에 필요한, F (5)를 산출 할 볼 수 3) 여러 번 계산되었다 이 이중 계산의 문제이다.

2, 어떻게 이중 계산을 피하기 위해

이중 카운팅을 방지하기 위해 (예를 들면, 해시 테이블로서) 데이터 구조에 의해 해결했다 이미 F (k)를 저장할 수있다.

경우 이전에 이미 해결 여부에 보이는 다음의 재귀 호출하면 F (K). 그래서, 프로세스가 직접 해시 테이블 값에서 반환하는 경우, 필요, 반복 계산하지

그래서처럼 문제에 대해 이야기 방지 할 수 있습니다.

위의 아이디어에 따라, 우리는 단지 코드를 변환 보면 :

공개 INT의 F (INT N) {
  경우 (N == 1) 창 1;
  경우 (N == 2) 창 2;
  
  // hasSolvedList가지도하는 것으로 이해 될 수 있고, 키는 값 f를, n은 (N)
  경우 (hasSolvedList.containsKey (N)) {
    창 hasSolvedList.get (N);
  }
  
  INT RET = F (N-1) + F (N-2);
  hasSolvedList.put (N, RET);
  권리를 반환;
}

스택 오버 플로우,이 두 가지 일반적인 이중 계산 문제뿐만 아니라. 다른 많은 문제가 재귀 코드 있음.

  • 시간의 측면에서 효율성이 많이 더 코드 재귀 함수 호출, 때이 함수 호출 많은 수의, 그것은 상당한 시간 비용에 축적됩니다.
  • 공간의 복잡성에서, 재귀 호출은 메모리 스택에 시간 필드 데이터를 절약 할 수 있기 때문에,
  • 따라서 재귀 코드 공간의 복잡성을 분석 할 때, 우리가 이전에 영화 재귀 코드에 대해 이야기 등이 섹션의 추가 오버 헤드에 대한 필요성은 공간 복잡도 O (1)되지 고려하지만, O (N).

세븐, 어떻게 코드가 편리 재귀 재귀 코드에서 온다 재 작성?

1, 장점과 재귀 코드의 단점

1 장점

  1. 표현 재귀 코드를 쓰기에 매우 간단하고 매우 강하다

2, 단점

  1. 우주의 복잡성이 높다
  2. 위험 스택 오버 플로우
  3. 중복 계산
  4. 너무 많은 함수 호출 및 기타 문제는 더 많은 시간이 소요됩니다

2, 어떻게 비 재귀 재귀 코드에 대한 코드를 재 작성?

그건에게 우리는 비 재귀 재귀 코드의 코드를 다시 작성할 수 있다면? 영화관 단지 예로서, 우리는 오직 (X) = F (X-1) +1이 재귀 식 F를 제외하고 장면을 넣어. 우리는이 재 작성보고 :

INT의 F (INT 않음) {
  INT RET = 1;
  위한 (INT 나 2 =; I <= N; ++ i)는 {
    오른쪽 = K + 1;
  }
  권리를 반환;
}

마찬가지로, 제 2 예는 비 재귀 적 구현을 ​​변경할 수있다.

INT의 F (INT 않음) {
  경우 (N == 1) 창 1;
  경우 (N == 2) 창 2;
  
  INT RET = 0;
  INT 사전 = 2;
  INT 방지 = 1;
  대해 INT (I = 3, I <= N; ++ i)는 {
    RET = + prepro하는 단계;
    사전 예방 =;
    RET;
  }
  권리를 반환;
}

3, 모든 재귀 코드는 비 재귀 쓰기 그것의 반복주기를 변경할 수 있습니다되지 않는 이유는 무엇입니까?

  1. 일반적으로, 예. 원조 자체가 달성하기 위해 재귀 스택이지만, 우리는 시스템 스택 또는 가상 머신 자체를 사용하고 있기 때문에, 우리가하지 인식 아무것도하지 않고 제공.
  2. 우리는 어떤 재귀 코드가 같이 재 작성 될 수 있도록 자신의 구현 스택 메모리 힙, 수동 시뮬레이션 스택, 스택 과정은 재귀 코드가없는 경우
  3. 그러나이 아이디어는 실제로 재귀 "수동"재귀로 변경, 자연은 변경되지 않은 구현의 복잡성을 초청, 앞서 언급 한 몇 가지 문제를 해결하지 않습니다도 있지만,

여덟, 대답은 시작된다

어떻게 "궁극적 권장"을 찾을 : 지금까지 재귀에 관한 기본 지식은 이미 문제에서 살펴 보자이 시작 완료? 내 솔루션이 있습니다 :

긴 findRootReferrerId (긴 actorId) {
  롱 referrerId는 [표]의 선택을 referrer_id = 여기서 actor_id = actorId;
  (referrerId == NULL) 창 actorId 경우;
  창 findRootReferrerId (referrerId);
}

그것은 매우 간단하지 않다? 3 줄의 코드로 얻을 수 있지만 실제 프로젝트에서 위의 코드는 작동하지 않는 이유는 무엇입니까? 그리고 두 가지 문제가있다.

  1. 먼저, 깊은 재귀 경우, 스택 오버 플로우 문제가있을 수 있습니다.
  2. 더티 데이터 ⾥ 데이터베이스가있는 경우 둘째, 우리는 또한 무한 재귀 발생하는 문제를 처리해야합니다. 예를 들어 데모 데이터베이스 환경의 경우, 테스트 엔지니어는 인위적으로 일부 데이터를 삽입, 테스트를 용이하게하기 위해, 더티 데이터가있을 것입니다. A가 B 인 경우에는 추천 추천 B는 C를, C는 상기 사용자 A를 추천이다 무한 루프 발생되도록 .
  1. 나는 이미 그들에게 대답 한 첫 번째 질문은 해결하기 위해 재귀의 깊이를 제한 할 수 있습니다.
  2. 두 번째 문제는 재귀 수준 제한으로 해결 될 수있다.
  3.  그러나, 더 진보 된 방법은 자동으로 ABCA 존재의 "링"을 감지하는 것입니다있다.

어떻게 반지 그것의 존재를 감지? 나는이 설명하지 않습니다, 당신은 우리가 다음 장에 대해 이야기합니다, 다음에 대해 생각할 수 있습니다.

IX 요약

모두 완료 경우에도 여기에 재귀의 지식. 저를 정리해 보자.

  1. 재귀는 코딩 기술은 매우 효율적이고 간단하다. "세 가지 조건"의 문제가 만족되는 한 그것은 재귀 코드에 의해 해결 될 수있다.
  2. 그러나 재귀 코드는 이해하기 어려운, 쓰기 어렵습니다. 주위에 자신을 이동하지 재귀 코드를 작성하는 것이 중요합니다, 올바른 자세는 종료의 조건을 찾기 위해 재귀 공식을 작성하는 것입니다, 다음 재귀 코드로 변환.
  3. 비록 재귀 코드는 간단하고 효율적이지만 많은 단점 재귀 코드가있다. 재귀 코드를 작성할 때 예를 들어, 스택 오버 플로우, 이중 계산, 시간이 많이 걸리는 함수 호출, 공간 복잡도 초안 등 그래서, 우리는 이러한 부작용을 제어해야합니다.

열, 방과 후 사고

우리는 일반적으로이 디버그 모드를 사용하는 것은 거의 불가능하다 재귀 재귀 코드의 비교적 큰, 깊은 수준과 같은 단일 단계의 추적 기능처럼 IDE의 디버그 코드를 사용합니다. 재귀 코드의 경우, 당신은 그렇게 할 수있는 좋은 방법을 디버깅 할 수 있나요?

  1. 인쇄 로그 재귀 값을 발견했다.
  2. 조건부 중단 점 디버깅과 결합.
  3. 기본값은 998 층 파이썬 재귀입니다

추천

출처www.cnblogs.com/luoahong/p/11819283.html