180220 튜터링 - 스택
< 숙제 Review >
- 3자리마다 콤마 찍기 (재귀)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> void comma(int n) { int tmp = n/1000; if(tmp > 0) { comma(tmp); printf(",%03d", n%1000); } else { printf("%d", n); } } int main() { int n; scanf("%d", &n); comma(n); return 0; } | cs |
- %03d : 3자리 확보, 빈자리는 0으로..!!
< Tutoring >
- 스택의 정의 : 나중에 들어온 데이터가 먼저 꺼내진다.
>> 그냥 프링글스 ㅎㅎ
- 스택의 ADT 및 구현하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include <stdio.h> #define MAX 100 typedef struct stack { int s[MAX]; int top; } Stack; void init(Stack *s) { s->top = -1; } int isEmpty(Stack *s) { return s->top == -1; } int isFull(Stack *s) { return s->top == MAX-1; } void push(Stack *s, int data) { s->s[++(s->top)] = data; } int peek(Stack *s) { return s->s[s->top]; } void pop(Stack *s) { (s->top)--; } int main() { Stack s; init(&s); isEmpty(&s) ? puts("Empty") : puts("Not Empty"); isFull(&s) ? puts("Full") : puts("Not Full"); push(&s, 1); push(&s, 3); push(&s, 5); push(&s, 7); printf("%d\n", peek(&s)); pop(&s); printf("%d\n", peek(&s)); pop(&s); printf("%d\n", peek(&s)); pop(&s); printf("%d\n", peek(&s)); pop(&s); isEmpty(&s) ? puts("Empty") : puts("Not Empty"); return 0; } | cs |
>> 실행결과
Empty
Not Full
7
5
3
1
Empty
- 연결리스트를 이용한 구현 (coded by ㄱㄱㄷ ㅋㄱ ㄱㅇㄹ)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node * next; } Node; typedef struct listStack { Node * top; } ListStack; void init(ListStack * list) { list->top = NULL; } int isEmpty(ListStack * list) { return list->top == NULL; } int peek(ListStack * list) { if(isEmpty(list)) { printf("빈 스택임"); return -111111; } return (list->top)->data; } void push(ListStack * list, int data) { Node * tmp = (Node*) malloc(sizeof(Node)); tmp->data = data; tmp->next = list->top; list->top = tmp; } void pop(ListStack * list) { Node *tmp = list->top; list->top = tmp->next; free(tmp); } int main() { ListStack s; init(&s); isEmpty(&s) ? puts("Empty") : puts("Not Empty"); push(&s, 1); push(&s, 3); push(&s, 5); push(&s, 7); printf("%d\n", peek(&s)); pop(&s); printf("%d\n", peek(&s)); pop(&s); printf("%d\n", peek(&s)); pop(&s); printf("%d\n", peek(&s)); pop(&s); isEmpty(&s) ? puts("Empty") : puts("Not Empty"); return 0; } | cs |
>>
- 스택의 응용
1) 괄호검사 (coded by ㄱㄱㄷ ㅋㄱ ㄱㅇㄹ) //
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void RightBracket(const char * str) { ListStack l1; init(&l1); int i; for(i=0; str[i]; i++) { if(str[i] == '(') push(&l1, '('); if(str[i] == ')') { if(!isEmpty(&l1)) pop(&l1); else { printf("NO\n"); return; } } } if(isEmpty(&l1)) printf("YES\n"); else printf("NO\n"); } | cs |
>> 5 줄에서 for 문의 조건식을 보자.
>> 문자열의 마지막은 널문자이다. 널문자의 아스키코드값은 0.
>> 0은 조건식에서 거짓이다.
2) 후위식의 계산
- 알고리즘 :
>> 연산자를 만나면 앞에 두 피연산자를 꺼내서 계산,
>> 먼저 꺼낸 피연산자를 우측에 두고 , 나중에 꺼낸 피연산자를 좌측에 두고 연산을 진행한다.
< 소스코드 >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | int exp(const char *eq) { int i; Stack s; init(&s); for(i=0; eq[i]; i++) { if('0' <= eq[i] && eq[i] <= '9') push(&s, eq[i]-'0'); else { int op2 = peek(&s); pop(&s); int op1 = peek(&s); pop(&s); switch(eq[i]) { case '+': push(&s, op1+op2); break; case '-': push(&s, op1-op2); break; case '*': push(&s, op1*op2); break; case '/': push(&s, op1/op2); break; } } } return peek(&s); } int main() { printf("%d\n", exp("234*+")); // 14 printf("%d\n", exp("82/3-32*+")); // 7 return 0; } | cs |
>> 10 : 오른쪽 피연산자가 먼저 꺼내짐에 주의.
>> 7 : 문자열의 끝은 널문자인데, 널문자의 아스키코드값은 0. 0은 조건식에서 거짓. 즉 널물자를 만나면 for 문을 탈출한다.
>> for 문의 조건식에 strlen(eq[i]) 라고 적는것은 안좋다.
for 문의 경우 초기식 - (조건식, 바디, 증감식)의 반복 으로 이루어 지는데, 조건식을 확인할 때마다 strlen() 함수가 작동한다.
O(n) 으로 해결할 것도 O(n^2) 이 되버리는 신기한(?) 일이 발생한다.
3) 중위식을 후위식으로 바꾸기.
- 연산자 우선순위
>> *, / : 제일 크다.
>> +, - : 두번째
>> ( : 제일 작다.
- 규칙
>> 피연산자를 만나면 그대로 출력
>> 연산자를 만나면 스택에 push.
>> 스택의 맨 위 연산자의 우선순위가 새로 추가되는 연산자의 우선순위보다 높거나 같다면 꺼내서 출력
>> ( 가중치에 상관없이 스택에 쌓는다.
>> ) 가 나오면 스택에서 ( 위에 쌓여 있는 모든 연산자를 출력. 그리고 ( 는 꺼내서 버린다.
< 소스코드 >
>> 숙제.
< 숙제 >
- 복습 잘하기
- 올바른 괄호 문자열인지 판단하는 함수( "(", "{", "[" : 세 종류의 괄호 사용)
- 중위식을 후위식으로 만드는 코드.(http://boj.kr/1918)
- 중위식을 후위식으로 만든 다음 그 후위식을 계산하는 코드
- 큐 예습해오기.
'Tutoring > 17-2 Data Structure (Winter)' 카테고리의 다른 글
180301 튜터링 - 큐(Queue) (0) | 2018.03.01 |
---|---|
180301 튜터링 - 스택의 응용 (수정:180514) (0) | 2018.03.01 |
180213 튜터링 - 연결리스트, 양방향, 원형 연결리스트 (0) | 2018.02.14 |
180206 튜터링 - 연결리스트 (0) | 2018.02.06 |
180130 튜터링 - 재귀함수 만들기, 분석하기 (0) | 2018.02.06 |
Week 2 - DFS
<180226>
Week 2 : DFS
[11724] 연결요소의 개수 (http://boj.kr/11724)
1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <stdio.h> int n, m; int g[1001][1001]; int visited[1001]; void dfs(int s) { int i; visited[s] = 1; for(i=1; i<=n; i++) { if(g[s][i] && !visited[i]) dfs(i); } } int main() { int i, ans=0; scanf("%d%d", &n, &m); while(m--) { int a, b; scanf("%d%d", &a, &b); g[a][b] = g[b][a] = 1; } for(i=1; i<=n; i++) { if(!visited[i]) { ans++; dfs(i); } } printf("%d\n", ans); return 0; } | cs |
>> DFS 기본
>> 모든 정점에 대해서 DFS() 실행한 횟수가 컴퍼넌트의 갯수가 된다.
2) 연결리스트를 이용한 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include <stdio.h> #include <stdlib.h> typedef struct node { int v; struct node *next; } Node; typedef struct list { Node *head; } List; void init(List *list) { list->head = NULL; } void addNode(List *list, int v) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->v = v; newNode->next = list->head; list->head = newNode; } int n, m; List g[1001]; int visited[1001]; void dfs(int s) { Node *cur = g[s].head; visited[s] = 1; while(cur != NULL) { int next = cur->v; if(!visited[next]) dfs(next); else cur = cur->next; } } int main() { int i, ans=0; scanf("%d%d", &n, &m); for(i=0; i<=n; i++) init(g+i); while(m--) { int a, b; scanf("%d%d", &a, &b); addNode(g+a, b); addNode(g+b, a); } for(i=1; i<=n; i++) { if(!visited[i]) { ans++; dfs(i); } } printf("%d\n", ans); return 0; } | cs |
[10026] 적록색약
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <stdio.h> int n; char g[101][101]; int visited[101][101]; int di[]={1, 0, -1, 0}; int dj[]={0, 1, 0, -1}; int safe(int i, int j) { return (0 <= i && i < n) && (0 <= j && j < n); } void dfs(int i, int j, char color) { int k; visited[i][j] = 1; for(k=0; k<4; k++) { int ni = i+di[k]; int nj = j+dj[k]; if(safe(ni, nj) && !visited[ni][nj] && g[ni][nj] == color) dfs(ni, nj, color); } } void go() { int i, j, ans=0; for(i=0; i<n; i++) { for(j=0; j<n; j++) { if(!visited[i][j]) { ans++; dfs(i, j, g[i][j]); } } } printf("%d\n", ans); } int main() { int i, j; int rb=0, rgb=0; scanf("%d", &n); for(i=0; i<n; i++) scanf("%s", g[i]); go(); for(i=0; i<n; i++) { for(j=0; j<n; j++) { visited[i][j] = 0; if(g[i][j] == 'G') g[i][j] = 'R'; } } go(); return 0; } | cs |
>>
[2606] 바이러스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include <stdio.h> int n, m; int g[101][101]; int visited[101]; int dfs(int s) { int i, ret=1; visited[s] = 1; for(i=1; i<=n; i++) { if(g[s][i] && !visited[i]) ret += dfs(i); } return ret; } int main() { scanf("%d%d", &n, &m); while(m--) { int a, b; scanf("%d%d", &a, &b); g[a][b] = g[b][a] = 1; } printf("%d\n", dfs(1)-1); return 0; } | cs |
>> 1번 컴퓨터에서 갈 수 있는 컴퓨터의 갯수.
[2583] 영역구하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #include <cstdio> #include <vector> #include <algorithm> using namespace std; int m, n, k; int g[105][105]; int dr[]={1, 0, -1, 0}; int dc[]={0, 1, 0, -1}; bool safe(int r, int c) { return (0 <= r && r < m) && (0 <= c && c < n); } int dfs(int r, int c) { int ret=1; g[r][c] = 1; for(int k=0; k<4; k++) { int nr = r+dr[k]; int nc = c+dc[k]; if(safe(nr, nc) && !g[nr][nc]) ret += dfs(nr, nc); } return ret; } int main() { int r, c, ans=0; scanf("%d%d%d", &m, &n, &k); while(k--) { int x, y, x2, y2; scanf("%d%d%d%d", &x, &y, &x2, &y2); for(r=y; r<y2; r++) for(c=x; c<x2; c++) g[r][c] = 1; } vector<int> v; for(r=0; r<m; r++) { for(c=0; c<n; c++) { if(!g[r][c]) { ans++; v.push_back(dfs(r, c)); } } } sort(v.begin(), v.end()); printf("%d\n", ans); for(int i=0; i<v.size(); i++) printf("%d ", v[i]); puts(""); } | cs |
>> 영역을 칠하는 것이 핵심인데, 이게 좀 헷갈린다.
>> 영역만 구하면 다음에 할일은 컴퍼넌트 갯수 구하기와 동일하다.
[10451] 순열 사이클
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <stdio.h> int n; int g[1001][1001]; int visited[1001]; void dfs(int s) { int i; visited[s] = 1; for(i=1; i<=n; i++) { if(g[s][i] && !visited[i]) dfs(i); } } int main() { int tc; scanf("%d", &tc); while(tc--) { int i, j, k; int ans=0; scanf("%d", &n); for(i=1; i<=n; i++) { scanf("%d", &k); g[i][k] = 1; } for(i=1; i<=n; i++) { if(!visited[i]) { ans++; dfs(i); } } printf("%d\n", ans); for(i=1; i<=n; i++) { visited[i] = 0; for(j=1; j<=n; j++) g[i][j] = 0; } } return 0; } | cs |
>> 그래프 모델링 후 컴퍼넌트 구하는 문제.
[]
'Tutoring > PS' 카테고리의 다른 글
Week 6 - BFS (숨박꼭질) (0) | 2018.03.30 |
---|---|
Week 5 - BFS (0) | 2018.03.24 |
Week 4 - BFS (0) | 2018.03.11 |
Week3 - DFS (0) | 2018.03.07 |
Week 1 - DFS (0) | 2018.02.20 |
170803 - Week 6
Week 6
1. 색종이(넓이)
- 도화지를 2차원 배열(0으로 초기화)로 생각할 수 있고, 색종이를 붙인다는 건
그 도화지에 1을 채우는 것으로 생각할 수 있다.
- 결국 1의 갯수가 색종이가 붙여진 곳의 넓이..!!
2. 색종이2(둘레)
- 000
010
000 에서 1은 1x1 크기의 색종이, 이 색종이의 둘레는 4
- 000
010
000
010
000 에서 도화지에 붙인 색종이의 둘레는 8.
- 즉 1의 상하좌우에 있는 0의 갯수가 둘레가 됨..!!
for(i=0; i<100; i++) {
for(j=0; j<100; j++) {
if(dowhaji[i][j] == 1) {
if(dowhaji[i-1][j] == 0) cnt++; // 상
if(dowhaji[i+1][j] == 0) cnt++; // 하
if(dowhaji[i][j-1] == 0) cnt++; // 좌
if(dowhaji[i][j+1] == 0) cnt++; // 우
}
}
}
3. 통계학 문제
- 빈도수관련 하여 배열을 이용하여 카운트 할 수 있는데,
이 문제에서 값의 범위는 절대값이 4000 이하.
즉 -4000 ~ 4000 까지의 값이 들어오게 되는데,
이 값을 0 ~ 8000 으로 매핑해서 빈도수를 체크할 수 있음.
즉 scanf("%d", &k); cnt[k]++; 이런식으로 가능
- 반올림의 경우 0.5를 더한 후 버림을 하면 구할 수 있음.
4. 동적할당
- #include <stdlib.h> 헤더파일 선언 필요
- 배열의 크기를 그때그때 다르게 하고 싶을때..?
int main() {
…
f(n);
…
}
void f(int n)
{
int arr[n]; // 컴파일 에러 발생
}
int *arr = (int*)malloc(sizeof(int)*n);
>> malloc() 은 주소를 반환. int*로 캐스팅을 하는 이유는
포인터 연산(배열의 인덱싱)을 할 때 자료형의 크기(여기서는 int)만큼
계산되기 위해..
위의 경우 sizeof(int) = 4Bytes * n 크기의 공간이 생성되고,
그 공간의 시작주소를 반환하게 되며 arr에 그 시작주소가 저장됨.
(int*)으로 캐스팅하여 arr[0] ~ arr[n-1]까지 배열처럼 사용가능.
동적할당은 힙 이라는 메모리 영역이 할당되어,
free(arr); 이라는 명령어를 통해 해당 메모리를 따로 해제시켜줘야함.
#include <stdio.h>
char* getName();
int main()
{
char *name;
name = getName();
printf("%s\n", name);
return 0;
}
char* getName()
{
char name[30];
scanf("%s",name);
return name;
}
>> 위 문장은 에러발생. 에러메세지 확인해보기.
>> getName() 의 name은 지역변수..!!
>> 지역변수는 함수가 소멸되면 같이 소멸됨
>> 동적할당으로 해결 가능
'Tutoring > 17-1 C Lang (Summer)' 카테고리의 다른 글
170727 - Week 5 (0) | 2018.02.21 |
---|---|
170720 - Week 4 (0) | 2018.02.21 |
170706 - Week 2 (0) | 2018.02.21 |
170629 - Week 1 (0) | 2018.02.21 |
170727 - Week 5
Week 5
1. 버블정렬(오름차순//내림차순)
for(i=0; i<N-1; i++)
for(j=0; j<N-1-i; j++)
if(a[j] > a[j+1])
swap(a[j], a[j+1]);
2. int strLen(char str[]);
3. void strCpy(char dest[], char src[]); // 널문자 삽입에 주의
4. void strCat(char dest[], char src[]);
(위 함수들 구현하기)
5. 문자열 길이에 따른 정렬.
>> 배열에 저장된 문자열들의 스왑은 어떻게..?
'Tutoring > 17-1 C Lang (Summer)' 카테고리의 다른 글
170803 - Week 6 (0) | 2018.02.21 |
---|---|
170720 - Week 4 (0) | 2018.02.21 |
170706 - Week 2 (0) | 2018.02.21 |
170629 - Week 1 (0) | 2018.02.21 |
170720 - Week 4
Week3
BOJ 3주차 문제 풀기!
파워업 Section 5까지 공부해오기!
Week 4
1. 2차원 배열 char str[5][20]; 에서 str의 타입은?
>> char [20]
2. 메인함수에 2차원 배열의 이름을 전달하면,
전달받는 함수에서의 매개변수는 어떻게 선언??
>> void f(char 배열이름[][10]); //
>> 1차원 배열 전달받을 땐 void f2(char str[]); // 타입 잘 생각해보기..!!
>> 매개변수의 한하여 void f(int *s) 와 void f(int s[])는 같은 표현!!
3. 포인터배열 : int *pArr[10]; // 포인터변수를 배열로..
4. 배열포인터 : int (*ArrP)[10]; // 포인터변수 ArrP의 타입은 int [10]
5. int main(int argc, char *argv[])
>> argc가 가지는 값, argv[i]에 저장되는 문자열들의 정체..!!
'Tutoring > 17-1 C Lang (Summer)' 카테고리의 다른 글
170803 - Week 6 (0) | 2018.02.21 |
---|---|
170727 - Week 5 (0) | 2018.02.21 |
170706 - Week 2 (0) | 2018.02.21 |
170629 - Week 1 (0) | 2018.02.21 |
170706 - Week 2
Week 2
1. 배열의 이름 : 배열의 시작주소(첫번째 요소의 주소)
2. 배열과 포인터와의 관계
- 배열의 이름 : 상수형태의 포인터
- 포인터 : 변수
3. 배열을 포인터처럼, 포인터를 배열처럼 사용 가능
4. 포인터 연산
- 1 증가시 자료형의 크기만큼 주소가 증가
- p++ 이후 *p vs *(p+1)
- *(p+i) == p[i] 와 동일
5. 문자열의 저장 방법
- char str1[] = "Good Morning"; // 배열의 초기화
- char *str2 = "Good Bye!"; // str2에는 문자열의 첫번째 문자의 주소가 저장됨.
- " " : 문자열은 첫 문자의 시작주소
cf) "Hello"[0] 이런 표현 가능
cf) printf(str1); 가능 // 이렇게 쓰지는 말구…!!
6. 변수형태의 문자열 vs 상수형태의 문자열
- str2에 저장된 문자열은 변경 불가능..!!
- 배열의 중간요소에 널문자를 대입하고 출력하면..? // 널문자는 문자열의 끝
- 상수(리터럴)와 변수의 차이..!!
7. 함수에 배열 전달하기.
- sizeof 연산자
'Tutoring > 17-1 C Lang (Summer)' 카테고리의 다른 글
170803 - Week 6 (0) | 2018.02.21 |
---|---|
170727 - Week 5 (0) | 2018.02.21 |
170720 - Week 4 (0) | 2018.02.21 |
170629 - Week 1 (0) | 2018.02.21 |
170629 - Week 1
170629
1. 아스키코드 특징
2. 문자열의 끝은 널문자, 널문자의 아스키값은 0
3. char형은 문자형(1바이트 정수형)
4. 1 vs '1' vs "1" // 메모리에 어떻게 저장이 되는지..
5. -1 은 2진수로 11111111 (1바이트 기준)
6. 문자끼리의 연산 : 'b'-'a'
7. '9'를 9로 만들기
8. 구구단 while()으로 짤 때 주의점.
9. 배열 선언 방법. 배열의 크기.
10. 배열을 사용하는 이유.
11. 배열의 각 공간은 인덱스를 통해서 접근 (for문으로)
12. 배열의 이름은 배열의 시작주소 (그렇다면 함수의 이름은? )
13. 배열의 초기화 (크기만큼 안하면 0으로 채워짐, 문자열 초기화)
14. 문자배열 vs 문자열 // 널문자..!! (2번 확인)
15. 문자열의 길이 구하기 // while()문 혹은 for()문
16. while(str[i++]) { cnt++; } 이해해보기. 조건식에서 참과 거짓의 값
17. 배열에 있는 요소들중에서 최대값 혹은 최소값 구하기.
18. int arr[100] = {0}; // 모든 배열요소 0으로 초기화
19. 배열의 인덱스는 0부터 크기-1 까지..
20. 포인터 선언방법
21. &는 피연산자의 주소를 반환하는 연산자
22. *는 참조 연산자
23. *a = *b; // 포인터 변수 b가 가리키는 공간(변수)에 있는 값을 포인터변수 a가 가리키는 공간(변수)에 대입
24. L-value = R-value : 공간 = 값
25. swap(a, b); vs swap(&a, &b); 지역변수 개념 생각해보기
'Tutoring > 17-1 C Lang (Summer)' 카테고리의 다른 글
170803 - Week 6 (0) | 2018.02.21 |
---|---|
170727 - Week 5 (0) | 2018.02.21 |
170720 - Week 4 (0) | 2018.02.21 |
170706 - Week 2 (0) | 2018.02.21 |
Week 1 - DFS
< 180219 >
Week 1 - DFS
[BOJ/2667] 단지번호 붙이기 (http://boj.kr/2667)
1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <cstdio> #include <algorithm> #define N 101 int n, cnt, a[N][N], s[N]; bool safe(int x, int y) { return (0 <= x && x < n) && (0<= y && y < n); } bool cmp(int a, int b) { return a < b; } void dfs(int x, int y, int k) { int i, dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1}; a[x][y] = k; for(i=0; i<4; i++) if(safe(x+dx[i], y+dy[i]) && a[x+dx[i]][y+dy[i]]==1 ) dfs(x+dx[i], y+dy[i], k); } void solve(void) { int i, j; for(i=0; i<n; i++) for(j=0; j<n; j++) if(a[i][j] == 1) { cnt++; dfs(i, j, cnt+1); } for(i=0; i<n; i++) for(j=0; j<n; j++) if(a[i][j]) s[a[i][j]-2]++; std::sort(s, s+cnt, cmp); } int main(void) { int i, j; scanf("%d", &n); for(i=0; i<n; i++) for(j=0; j<n; j++) scanf("%1d", &a[i][j]); solve(); printf("%d\n", cnt); for(i=0; i<cnt; i++) printf("%d\n", s[i]); return 0; } | cs |
2) 이런식으로도 구현할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #include <cstdio> #include <vector> #include <algorithm> using namespace std; int n, cnt, a[101][101]; vector<int> v; bool safe(int x, int y) { return (0 <= x && x < n) && (0<= y && y < n); } int dfs(int x, int y) { int i, dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1}; int ret = 1; a[x][y] = 0; for(i=0; i<4; i++) if(safe(x+dx[i], y+dy[i]) && a[x+dx[i]][y+dy[i]]==1 ) ret += dfs(x+dx[i], y+dy[i]); return ret; } void solve(void) { int i, j; for(i=0; i<n; i++) { for(j=0; j<n; j++) { if(a[i][j] == 1) { cnt++; v.push_back(dfs(i, j)); } } } sort(v.begin(), v.end()); } int main(void) { int i, j; scanf("%d", &n); for(i=0; i<n; i++) for(j=0; j<n; j++) scanf("%1d", &a[i][j]); solve(); printf("%d\n", cnt); for(i=0; i<cnt; i++) printf("%d\n", v[i]); } | cs |
>> dfs() 의 리턴형이 int 이다. 결국 dfs() 호출된 횟수를 리턴하게 된다.
>> vector 를 사용했다.
[BOJ/1012] 유기농 배추 (http://boj.kr/1012)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include <stdio.h> int r, c; int g[51][51]; int dr[]={1, 0, -1, 0}; int dc[]={0, 1, 0, -1}; int safe(int i, int j) { return (0 <= i && i < r) && (0 <= j && j < c); } void dfs(int r, int c) { int k; g[r][c] = 0; for(k=0; k<4; k++) { int nr = r + dr[k]; int nc = c + dc[k]; if(safe(nr, nc) && g[nr][nc]) dfs(nr, nc); } } int main() { int tc; scanf("%d", &tc); while(tc--) { int i, j, k; int ans = 0; scanf("%d%d%d", &r, &c, &k); while(k--) { int a, b; scanf("%d%d", &a, &b); g[a][b] = 1; } for(i=0; i<r; i++) { for(j=0; j<c; j++) { if(g[i][j]) { ans++; dfs(i, j); } } } printf("%d\n", ans); } return 0; } | cs |
>> 16 : 방문체크 배열을 따로 안만들고, 방문한곳을 0으로 바꿔줌으로써 다시 방문할 수 없도록 하였다.
[BOJ/4963] 섬의 개수 (http://boj.kr/2963)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #include <stdio.h> int r, c; int g[51][51]; int di[]={-1, -1, -1, 0, 0, 1, 1, 1}; int dj[]={-1, 0, 1, -1, 1, -1, 0, 1}; int safe(int i, int j) { return (0 <= i && i < r) && (0 <= j && j < c); } void dfs(int i, int j) { int k; g[i][j] = 0; for(k=0; k<8; k++) { int ni = i+di[k]; int nj = j+dj[k]; if(safe(ni, nj) && g[ni][nj]) dfs(ni, nj); } } int main() { while(1) { int i, j; int ans=0; scanf("%d%d", &c, &r); if(!r && !c) break; for(i=0; i<r; i++) for(j=0; j<c; j++) scanf("%d", &g[i][j]); for(i=0; i<r; i++) { for(j=0; j<c; j++) { if(g[i][j]) { ans++; dfs(i, j); } } } printf("%d\n", ans); } return 0; } | cs |
>> 위와 같은 유형의 문제. 차이점이라면 8방탐색..!!
'Tutoring > PS' 카테고리의 다른 글
Week 6 - BFS (숨박꼭질) (0) | 2018.03.30 |
---|---|
Week 5 - BFS (0) | 2018.03.24 |
Week 4 - BFS (0) | 2018.03.11 |
Week3 - DFS (0) | 2018.03.07 |
Week 2 - DFS (0) | 2018.02.21 |
180213 튜터링 - 연결리스트, 양방향, 원형 연결리스트
< 숙제 review >
1) 팰린드롬인지 판단한는 함수 재귀함수로 구현하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> #include <string.h> int isP(char str[], int s, int e) { if(s >= e) return 1; return str[s]==str[e] ? isP(str, s+1, e-1) : 0; } int main() { char str[100]; scanf("%s", str); isP(str, 0, strlen(str)-1) ? puts("True") : puts("False"); return 0; } | cs |
>> 첫번째 인덱스와 마지막 인덱스의 문자가 같을때, 그 안의 문자열이 팰린드롬이면 팰린드롬이다.
2) 리스트의 내용을 모두 출력하는 함수 만들기
1 2 3 4 5 6 7 8 | void printList(List *list) { Node *p = list->head; while(p != NULL) { printf("%d\n", p->data); p = p->next; } } | cs |
< Tutoring >
< 연결리스트의 전체노드 삭제 >
1 2 3 4 5 6 7 8 9 10 | void deleteList(List *list) { Node *p = list->head; while(p != NULL) { Node *tmp = p; p = p->next; free(tmp); } init(list); } | cs |
>> 위 예제의 응용에 불과하다고 생각함.
>> 동적할당으로 생성된 변수는 free() 를 통해 해제를 시킬 수 있다.
>> 9번은 다 지우고 난 다음 초기화를 다시 해주는 역할 정도..
< 연결리스트에서 특정 노드의 삭제 >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void deleteNode(List *list, int pos) { int k = 1; Node *prev, *cur = list->head; if(pos == 1) { list->head = list->head->next; free(cur); return ; } while(k < pos) { k++; prev = cur; cur = cur->next; } prev->next = cur->next; free(cur); } | cs |
>> 11~14 에서 첫번째 노드가 1번 노드라고 할 때, k번째 노드는 cur 가 가리키게 된다.
>> 하지만 위의 코드는 pos 의 값이 노드의 갯수보다 큰 값이 들어오면 에러가 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | void deleteNode2(List *list, int pos) { int k = 1; Node *prev, *cur = list->head; if(cur == NULL) return ; if(pos == 1) { list->head = list->head->next; free(cur); return ; } while(cur != NULL && k < pos) { k++; prev = cur; cur = cur->next; } if(cur == NULL) { puts("Position does not exist."); return ; } prev->next = cur->next; free(cur); } | cs |
>> 어떤 부분들이 바뀌었는지 비교해봐 얘들아.. ㅎㅎ
>> 그외 List 주머니에 사이즈 관련 변수 하나 만들어서 노드의 개수를 카운트 해나가면서
pos 값이 그 사이즈 안에 있는 값인지 체크하는 방법으로 해도 되고, 상황에 맞게 하면 될듯.!!
< 원형연결리스트의 삽입 (앞에) >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node *next; } Node; typedef struct list { Node *head; } List; void init(List *list) { list->head = NULL; } void CircularListAdd(List *list, int data) { Node *p, *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; if(list->head == NULL) { list->head = newNode; newNode->next = newNode; return ; } p = list->head; while(p->next != list->head) p = p->next; p->next = newNode; newNode->next = list->head; //list->head = newNode; } void printCircularList(List *list) { Node *cur = list->head; if(cur == NULL) return ; do { printf("%d\n", cur->data); cur = cur->next; } while(cur != list->head); } int main() { List list; init(&list); CircularListAdd(&list, 1); CircularListAdd(&list, 3); CircularListAdd(&list, 7); CircularListAdd(&list, 5); printCircularList(&list); return 0; } | cs |
>> 18 : 원형 연결리스트의 데이터를 삽입하는 함수.
>> 38 : 원형 연결리스트의 데이터를 출력하는 함수
>> 60 의 결과는 1, 3, 7, 5
>> 35 의 주석을 해제했을 때, 60의 결과는 5, 7, 3, 1
>> 즉 35가 없으면 뒤로 삽입, 있으면 앞으로 삽입을 하는 결과가 만들어진다.
>> 다만 위의 코드에서는 삽입을 할때, 항상 맨 마지막 노드를 찾아야 하는 번거로움(?)이 있다.
하지만 헤드포인터를 테일이라고 생각하고 코드를 작성하면 그럴필요가 없어진다..!!
1 2 3 4 5 6 7 8 9 10 11 12 13 | void CircularListAdd2(List *list, int data) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; if(list->head == NULL) { list->head = newNode; newNode->next = newNode; return ; } newNode->next = list->head->next; list->head->next = newNode; //list->head = newNode; } | cs |
>> head 를 tail 이라고 생각하면 된다.
>> 12 가 없으면 앞으로 삽입, 12가 있으면 뒤로 삽입하는 코드가 된다.
< 원형연결리스트의 노드들 중 홀수의 갯수 세기 >
- printCircularList() 에서 홀수인지만 판단한는 조건만 추가하고 계산 후 그 값을 리턴하면 된다.
- 그러니 우리는 if(cur->data & 1) 이라는 표현에 대해 알아보자.
>> & 는 비트 연산자로 각 비트별 AND 연산을 수행한다.
>> 참고로 모든 홀수의 마지막 비트(2^0)은 항상 1 이다.
모든 짝수의 마지막 비트는 항상 0 이다.
>> 1 은 8비트 기준 00000001 이다.
>> 그러므로 1과 & 연산을 하면, 마지막 비트를 제외한 비트는 1이든 0이든 AND 연산의 결과는
0이 나온다. 그렇기 때문에 홀수와 1에 대해 &(AND) 연산을 하면 항상 1이 나온다.
>> 조건식에서 1은 참을 의미한다..
cf) -1 은 비트로 표현하면 1로만 구성이 된다. 8비트라면 11111111 이 된다.
>> 1을 더하면 0이 되므로 증명 끝..
>> -2는 -1에서 1을 빼면 된다. 그러므로 -2는 8비트기준 11111110 이 된다.
>> -3은 -1에서 2를 빼면 된다. 그러므로 -3은 8비트 기준 11111101 이 된다.
>> 작은 수들은 굳이 2의 보수 취하지 않고도 이런식으로 구할 수 있다.
< 양방향 연결리스트의 삽입 >
- 우리가 지금까지 했던 연결리스트는 한방향으로만 연결이 되어 있다.
하지만, 양방향 연결리스트는 말 그대로 양방향으로 연결이 되어 있다. 그러므로 prev 같은 노드
포인터가 필요없어진다.
- 노드에 대한 디자인도 새로 해야한다.
1 2 3 4 5 | typedef struct node { int data; struct node *next; struct node *prev; } Node; | cs |
>> 4 : 이전 노드를 가리키기 위한 포인터가 추가되었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void insertNode(List *list, int data) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = NULL; newNode->prev = NULL; if(list->head == NULL) list->head = newNode; else { newNode->next = list->head; list->head->prev = newNode; list->head = newNode; } } | cs |
>> 앞으로 삽입하는 코드.
>> 6, 12번 줄이 추가가 된다.
< 더미노드를 이용한 양방향 연결리스트의 삽입 >
- 더미노드를 사용하면 list->head 가 가리키는 노드가 바뀌는 것에 대한 염려를 안해도 된다.
- 더미노드를 사용하면 init() 함수를 수정해주어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void init(List *list) { list->head = (Node*)malloc(sizeof(Node));; list->head->prev = NULL; list->head->next = NULL; } void insertNode(List *list, int data) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = list->head->next; list->head->next = newNode; newNode->prev = list->head; } | cs |
>> 그리고 삽입코드는 위와 같이 쓸 수 있다.
< 숙제 >
- 복습잘하기
- 이중연결리스트에서 position 값이 주어졌을 때, 해당 위치에 노드를 삭제하는 함수 만들기
- 양의 정수를 입력 받았을 때, 3자리 마다 , 를 찍어서 출력해주는 함수(재귀로)
input : 1000000 >> output : 1,000,000
- 스택, 중위식을 후위식으로 표기하는 방법, 후위식을 계산하는 방법 예습해오기
'Tutoring > 17-2 Data Structure (Winter)' 카테고리의 다른 글
180301 튜터링 - 큐(Queue) (0) | 2018.03.01 |
---|---|
180301 튜터링 - 스택의 응용 (수정:180514) (0) | 2018.03.01 |
180220 튜터링 - 스택 (0) | 2018.02.21 |
180206 튜터링 - 연결리스트 (0) | 2018.02.06 |
180130 튜터링 - 재귀함수 만들기, 분석하기 (0) | 2018.02.06 |
180206 튜터링 - 연결리스트
< 재귀함수 숙제 review >
- 1부터 n까지의 숫자중 홀수/짝수의 합 구하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> int oddSum(int n) { if(n < 1) return 0; if(n%2 == 0) n--; return n + oddSum(n-2); } int evenSum(int n) { if(n < 1) return 0; if(n%2 == 1) n--; return n + evenSum(n-2); } int main() { int n = 100; printf("%d\n", oddSum(n)); printf("%d\n", evenSum(n)); return 0; } | cs |
>> 6번째 줄을 if(!(n%2)) 로 쓸 수도 있다.
>> 13번째 줄은 if(n&1) 로 쓸 수 있다.
- 배열의 요소들 중에서 최대값 구하기.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> int f(int arr[], int n) { int k; if(n == 0) return arr[0]; k = f(arr, n-1); return k > arr[n] ? k : arr[n]; } int main() { int arr[] = {10, 2, 30, 11, 50, 33}; printf("%d\n", f(arr, 5)); return 0; } | cs |
- 배열의 요소들 중에서 두번째 최대값 구하기.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> int max; int f2(int arr[], int n) { int k; if(n == 1) { max = arr[n] > arr[n-1] ? arr[n] : arr[n-1]; return arr[n] > arr[n-1] ? arr[n-1] : arr[n]; } k = f2(arr, n-1); if(arr[n] < k) return k; if(arr[n] > max) { int t=max; max=arr[n]; return t; } return arr[n]; } int main() { int arr[] = {50, 10, 2, 30, 11, 33}; printf("%d\n", f2(arr, 5)); return 0; } | cs |
>> n 은 마지막 인덱스
< 연결리스트 >
- 노드 디자인 하기
1 2 3 4 | typedef struct node { int data; struct node *next; } Node; | cs |
>> 자기참조 구조체
>> typedef 의 역할 // ex) typedef int INT;
>> 구조체는 자료형이다. 사용자 정의 자료형. 즉, 우리가 만든 자료형이다.
- List 주머니(?) 디자인 하기.
1 2 3 | typedef struct list { Node *head; } List; | cs |
- 리스트 생성 후 초기화 하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> typedef struct node { int data; struct node *next; } Node; typedef struct list { Node *head; } List; void init(List *list) { list->head = NULL; } int main() { List list; init(&list); return 0; } | cs |
>> 19 : List 타입의 list 변수 생성
>> 20 : list 를 초기화 하는 코드. C++에서 생성자를 수동으로 실행시킨다고 생각하면 될 것 같다.
- 데이터 삽입하기.
1 2 3 4 5 6 7 8 9 10 11 12 13 | void insertNode(List *list, int data) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = NULL; if(list->head == NULL) list->head = newNode; else { newNode->next = list->head; list->head = newNode; } } | cs |
>> 3 : 동적할당으로 #include <stdlib.h> 헤더파일이 필요하며,
결과 값으로 이름없는 변수의 주소값을 리턴한다. 그리고 그 주소를 newNode 에 저장.
>> 3, 5, 1 순으로 삽입을 하면 (head)->(1)->(5)->(1)->NULL 모양의 리스트가 만들어 진다.
즉, 앞쪽으로 데이터가 삽입이 된다.
(그림은 머릿속으로.....)
>> newNode->data : newNode가 가리키는 변수의 멤버 data (고요속의 외침..ㅎㅎ)
>> 아래와 같이 분기를 하지 않아도 될 것 같다.
1 2 3 4 5 6 7 | void insertNode(List *list, int data) { Node *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = list->head; list->head = newNode; } | cs |
>> 검증은 못해봄. 누가 테스트 해보고 결과좀 알려줘. ㅎ
- 데이터 뒤로 삽입하기.
1) 리스트 주머니 수정하지 않고 해보기.
2) 리스트 주머니를 수정해서 해보기.
1) 의 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void insertNodeBack(List *list, int data) { Node *prev, *newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = NULL; if(list->head == NULL) { list->head = newNode; } else { prev = list->head; while(prev->next != NULL) prev = prev->next; prev->next = newNode; } } | cs |
>> 12-13 : prev 를 통해 맨 마지막 노드를 찾는 코드이다.
>> 14 : 맨 마지막 노드를 찾았으면, 거기에 새 노드를 연결하면 될 듯.
### 자료구조 공부할 때, 특히 포인터등이 헷갈릴때는 그림을 그려가면서 해봐 얘들아 ㅎㅎ
### 숙 제 ###
- 복습 잘하기.
- 어떤 문자열이 주어졌을 때, 해당 문자열이 팰린드롬(회문)인지 판단하는 함수 재귀함수로 만들기
- 리스트의 내용을 모두 출력하는 함수 만들기
- 양방향 연결4리스트, 환형 연결리스트 예습해오기.
'Tutoring > 17-2 Data Structure (Winter)' 카테고리의 다른 글
180301 튜터링 - 큐(Queue) (0) | 2018.03.01 |
---|---|
180301 튜터링 - 스택의 응용 (수정:180514) (0) | 2018.03.01 |
180220 튜터링 - 스택 (0) | 2018.02.21 |
180213 튜터링 - 연결리스트, 양방향, 원형 연결리스트 (0) | 2018.02.14 |
180130 튜터링 - 재귀함수 만들기, 분석하기 (0) | 2018.02.06 |