- 작성일 2019 년 5 월 29 일
70. 계단 오르기
① 제목 설명
- 계단을 오르고 있다고 가정합니다. 건물 꼭대기에 도달하려면 n 단계가 필요합니다.
- 매번 1 ~ 2 걸음 올라갈 수 있습니다. 건물 꼭대기에 오르려면 몇 가지 방법이 필요합니까?
- 참고 : 주어진 n은 양의 정수입니다.
- 예 1 :
입력 : 2
출력 : 2
설명 : 건물 꼭대기에 오르는 방법은 두 가지가 있습니다.
- 1 차 + 1 차
- 2 단계
- 예 2 :
입력 : 3
출력 : 3
설명 : 건물 꼭대기에 오르는 방법은 세 가지가 있습니다.
- 1 차 + 1 차 + 1 차
- 1 차 + 2 차
- 2 차 + 1 차
② 재귀 솔루션 ( Time Limit Exceeded
)
- 는 n 번째 단계까지 전에, 하나 개 또는 두 단계를 이동으로 인해 시간, N 층 단계 이동을 요구에 대해 재귀 적 사고를 사용하려면, 상기 유지해야
n - 1
이전 수준 또는n - 2
무대. 따라서 f (n)을 사용하여 n 단계의 이동을 나타냅니다. 그때,
f ( n ) = f ( n - 1) + f ( n - 2 )
f ( 1 ) = 1,f ( 2 ) = 2
- 마법 같은 것을 발견했습니다. 이것은 피보나치 수열 (Fibonacci 수열)입니다.
- 폭력적이며 재귀를 사용하여 작성하십시오. 그러나 그것은 37 시로 밝혀졌습니다
Time Limit Exceeded
! - 시간 복잡도 : 트리 다이어그램, O (2n) O (2 ^ n)O ( 2n )。
- 코드 쇼 :
public int climbStairs(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return climbStairs(n - 2) + climbStairs(n - 1);
}
③ 재귀 적 최적화
- 으로
f(4)
, 예를 들어, 우리는 각각 해결해야f(3)
하고f(2)
, 해결f(3)
,시에 지점을 해결하기 위해 필요f(2)
하고f(1)
사실,f(2)
이미 이전에 해결되었다.
- 최적화는
memoization
기술 이라고하는 재귀를 다시 입력하지 않고 직접 사용할 때 수요를 유지 한 솔루션을 찾는 것 입니다. - 이 기술은 이전 재귀 방법의 최적화에도 사용됩니다!
- 코드 쇼 :
public int climbStairs(int n) {
HashMap<Integer, Integer> map = new HashMap<>();
return climbStairsN(n, map);
}
public int climbStairsN(int n, HashMap<Integer, Integer> map) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
int f1 = 0;
if (map.containsKey(n - 1)) {
f1 = map.get(n - 1);
} else {
f1 = climbStairsN(n - 1, map);
map.put(n - 1, f1);
}
int f2 = 0;
if (map.containsKey(n - 2)) {
f2 = map.get(n - 2);
} else {
f2 = climbStairsN(n - 2, map);
map.put(n - 2, f2);
}
map.put(n, f1 + f2);
return f1 + f2;
}
④ 어레이 사용
f[]
피보나치 수열을 저장할 새 배열 을 만들고 초기화f[1] = 1, f[2] = 2;
한 다음f[i] = f[i - 1] + f[i - 2]
.- 공간 복잡도는 O (n) O (n) 이지만O ( n ) , 그러나 시간 복잡도는O (n) O (n)O ( n ) 이므로 실행 시간은
0ms
입니다. - 코드 쇼 :
public int climbStairs(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
int[] f = new int[n + 1];
f[1] = 1;
f[2] = 2;
for (int i = 3; i <= n; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[n];
}
72. 거리 수정
② 제목 설명
- 두 단어를 감안
word1
하고word2
계산하는word1
변환word2
사용 작업의 최소 수입니다. - 단어에 대해 다음 세 가지 작업을 수행 할 수 있습니다
. 문자 삽입, 문자
삭제, 문자
바꾸기 - 예 1 :
입력 : word1 = "horse", word2 = "ros"
출력 : 3
설명 :
horse-> rorse ( '
h '를 'r'로 대체 ) rorse-> rose (delete'r ') rose-
> ros (delete' 이자형')
- 예 2 :
입력 : word1 = "intention", word2 = "execution"
출력 : 5
설명 : intention-> inention (delete't ')
inention-> enention (
replace'i'with'e ') enention-> exention (change'
n '을' x '로 대체) exention-> exection (replace'n'with'c ')
exection-> 실행 ( insert'u ')
② 동적 프로그래밍
dp[i][j]
문자열word1[ 0, i )
(word1은 0 번째에서 i-1 자) 및word2[ 0, j - 1)
최소 편집 거리의 경우.- 상태 전이 방정식 :
① 만약word1[i -1] = word2[j - 1]
,dp[i][j] = dp[i - 1][j - 1]
캐릭터가 변경되지 않았기 때문
②word1[i -1] != word2[j - 1]
,,dp[i][j] = Math.min(dp[i][j - 1], Math.min(dp[i - 1][j], dp[i - 1][j - 1])) + 1
단어 1이 각각 추가, 삭제, 대체되면 문자가 단어 2가됩니다. - 초기화 :
①dp[0][0]=0
, 수단 모두의 길이가 0이며, 최소 편집 거리가 0 인 것을,
②로부터i = 1
시작dp[i][0] = i
,, 삭제 문자에 필요한 단계 의미 에서 빈 word2에 단어 1을,
③로부터j = 1
시작dp[0][j] = j
,, 즉, 문자 빈 문자에서 단어 1 변경 word2에 필요한 단계.
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
int[][] dp = new int[m + 1][n + 1];
// word1有非空变为空所需要的删除步骤
for (int i = 1; i <= m; i++) {
dp[i][0] = i;
}
// word1由空变成 word2所需要的添加的步骤
for (int j = 1; j <= n; j++) {
dp[0][j] = j;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// word1的下标i-1.其实表示第i个字符,也就是我们当前要求解的字符
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
// min( add, delete, change)+1
dp[i][j] = Math.min(dp[i][j - 1], Math.min(dp[i - 1][j], dp[i - 1][j - 1])) + 1;
}
}
}
return dp[m][n];
}
75. 색상 분류
① 제목 설명
- 빨강, 흰색 및 파랑을 포함하는 n 개의 요소 배열이 주어지면 동일한 색상의 요소가 인접하고 빨강, 흰색 및 파랑 순서로 정렬되도록 정렬합니다.
- 이 질문에서는 정수 0, 1, 2를 사용하여 각각 빨간색, 흰색 및 파란색을 나타냅니다.
- 참고 : 이 문제를 해결하기 위해 코드베이스에서 정렬 기능을 사용할 수 없습니다.
- 예:
입력 : [2,0,2,1,1,0]
출력 : [0,0,1,1,2,2 ]
- 많은:
직관적 인 해결책은 분류를 계산하기 위해 2 단계 스캐닝 알고리즘을 사용하는 것입니다.
먼저 요소 수 0, 1, 2를 반복적으로 계산 한 다음 0, 1, 2의 순서로 현재 배열을 다시 씁니다.
일정한 공간만을 사용하는 원 패스 스캐닝 알고리즘을 생각할 수 있습니까?
② 더블 포인터 (자신의 생각)
- 배열이 정렬 된 후의
0,...,0 1,...,1 2,...,2
경우 여야하며 , 각각 0, 1, 2의 숫자 인 세 부분으로 생각할 수 있습니다. - 따라서 외부 루프는 총 3 회이며 각 패스에서 모든 0을 첫 번째 세그먼트에, 모두 1을 두 번째 세그먼트에, 모두 세 번째 세그먼트에 넣지 마십시오.
- 내부 루프는 이중 포인터
start指针
를 사용 하여 삽입 할 번호의 위치를 나타내며 삽입cur指针
할 수있는 번호를 찾기 위해 계속 뒤로 이동합니다. - 각 메모리 루프에서
nums[start]
i 일 경우 시작점을 업데이트해야하며 시작은 경계를 넘을 수 없습니다. 삽입 할 번호의 위치를 결정한 후 curstart+1
은 삽입 할 수있는 번호를 찾기 위해 배열 을 처음부터 뒤로 이동합니다. - 번호를 삽입 할 수 없으며 cur은 다음 번호를 가리 킵니다. 그렇지 않으면 start와 cur은 동시에 다음 번호를 가리 킵니다.
- 코드 쇼 :
public void sortColors(int[] nums) {
if (nums.length == 0 || nums.length == 1) {
return;
}
int start = 0;
for (int i = 0; i < 3; i++) {
while (start < nums.length && nums[start] == i) {
start++;
}
int cur = start + 1;
while (cur < nums.length) {
// cur向后遍历,直到遍历完数组
if (nums[cur] == i) {
// 找到了待插入的数字,交换cur和start指向数字
int temp = nums[start];
nums[start] = nums[cur];
nums[cur] = temp;
start++; // start+1指向下一个待插入数字的位置
}
cur++;
}
}
}
③ 0과 1의 수를 세고 0,1,2의 해당 수를 배열에 더합니다.
- 코드 쇼 :
public void sortColors(int[] nums) {
if (nums.length == 0 || nums.length == 1) {
return;
}
int zero_count = 0;
int one_count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
zero_count++;
}
if (nums[i] == 1) {
one_count++;
}
}
// 将对应位置的数字改为对应数目的0,1,2
for (int i = 0; i < nums.length; i++) {
if (zero_count > 0) {
nums[i] = 0;
zero_count--;
} else if (one_count > 0) {
// 只有当0的个数加满后,才能添加1
nums[i] = 1;
one_count--;
} else {
nums[i] = 2;
}
}
}