배열과 구조체
배열의 정의
C에서 배열
•
배열의 선언
int list[5];
*plist[5];
C
복사
•
배열의 구현
◦
list[0]의 주소 = a
◦
list[1]의 주소 = a + sizeof(int)
◦
배열을 인자로 전달 → 배열의 시작 주소가 복사
▪
Call - by - Reference와 동일한 효과
list + i = &list[i]
*(list + i) = list[i]
C
복사
•
배열 ADT
Array Create(j, list)
// j 차원의 배열을 반환, list는 j-tuple로서 i번째 원소의 값은 i번째 차원의 크기를 나타냄
Item Retrieve(A, i)
Array Store(A, i, x)
C
복사
배열의 동적 할당
•
1차원 배열 동적 할당
int *A = (int *)malloc(sizeof(int) * 100);
int *B = (int *)calloc(100, sizeof(int));
A = (int *)realloc(A, sizeof(int) * 200);
free(A);
C
복사
•
2차원 배열 동적 할당
int **C = (int **)malloc(sizeof(int *) * 10);
for (int i = 0; i < 10; i++) {
C[i] = (int *)malloc(sizeof(int) * 20);
}
C
복사
구조체(Structure)
•
하나 이상의 기본 자료형을 기반으로, 사용자 정의 자료형을 만들 수 있는 문법 요소
•
배열과의 차이점
◦
배열 : 동일한 자료형
◦
구조체 : 다양한 자료형
// 구조체
struct humanBeing {
char name[10];
int age;
double salary;
};
typedef struct humanBeing human_being;
C
복사
•
구조체의 내용이 동일한 지를 검사하는 방법
◦
memcmp 사용
int humans_qual(human_geing person1, human_being person2) {
if (strcmp(person1.name, person2.name))
return FALSE;
if (person1.age != person2.age)
return FALSE;
if (person1.salary != person2.salary)
return FALSE;
return TRUE;
}
// memcmp(&person1, &person2, sizeof(human_being))
C
복사
•
Memory Alignment를 고려
◦
구조체 4btye 배수씩 시작한다
•
자기 참조 구조체 (Self-Referential Structure)
◦
구조체의 속성 중 하나가 스스로를 가리키는 구조체
struct list {
char data;
struct list *link;
};
C
복사
Union
•
Union의 필드들은 메모리를 공유
struct human {
enum {female, male} gender;
union {
int children;
int beard;
} u;
} person1, person2;
person1.gender = female;
person1.u.children = 4;
person2.gender = male;
person2.u.beard = FALSE;
C
복사
•
C는 union 필드들의 적절한 사용을 검사하지 않음
다항식의 표현
•
순서리스트 (Ordered list)
◦
데이터들의 순서가 유지되는 집합
◦
ex) {일, 월, 화, 수, 목, 금, 토}
•
순서 리스트에 적용 가능한 연산들
◦
리스트의 길이 n의 계산
◦
리스트의 모든 데이터들을 왼쪽→오른쪽으로 읽기
◦
리스트의 i번째 데이터를 검색, 대체 (0 ≤ i < n)
◦
리스트의 i번째 위치에 새로운 데이터 추가 → 연결리스트 Good
◦
리스트의 i번째 데이터 삭제
•
다항식
◦
순서화 리스트를 구현하는 두가지 방법
▪
배열 → i번째 데이터를 배열 인덱스 i에 저장
▪
연결리스트
→ 순서화된 <> 쌍들의 집합으로 표현
계수 (coefficient)
지수 (exponent)
Polynomial Zero()
Boolean IsZero(poly)
Coefficient Coef(poly, expon)
Exponent Lead_Exp(poly)
Polynomial Attach(poly, coef, expon)
Polynomial Remove(poly, expon)
Polynomial SingleMult(poly, coef, expon)
Polynomial Add(poly1, poly2)
Polynomial Mult(poly1, poly2)
C
복사
C 다항식 구현 (1)
1) 모든 지수의 계수들을 내림차순으로 저장
#define MAX_DEGREE 101
typedef struct {
int degree;
float coef[MAX_DEGREE];
} polynomial;
// 2x^3 + x^2 -1. => [3, (2, 1, 0, -1]
C
복사
→ x^100 + 1
⇒ 계수가 0인 것을 계속 저장 해야함
C 다항식 구현 (2)
2) 지수와 계수를 모두 저장
#define MAX_TERMS 100
typedef struct {
float coef;
int expon;
} polynomial;
polynomial terms[MAX_TERMS];
int avail = 0;
C
복사
⇒ {(1, 100), (1, 0)}
→ 모든 다항식을 저장하기 위한 전역변수 terms[] 사용
→ 각 다항식은 <start, end>의 쌍으로 표현
다항식 합
void padd (int startA, int finishA, int startB, int finishB, int *startD, int *finishD) {
float coefficient;
*startD = avail;
while (startA <= finishA && startB <= finishB)
switch (COMPARE(terms[startA].expon, terms[startB].expon)) {
case -1: // a.expon < b.expon
attach(terms[startB].coef, terms[startB].expon);
startB++;
break;
case 0:
coefficient = terms[startA].coef + terms[startB].coef;
if (coefficient)
attach(coefficient, terms[startA].expon);
startA++;
startB++;
break;
case 1: // a.expon > b.expon
attach(terms[startA].coef, terms[startA].expon);
startA++;
}
for (; startA <= finishA; startA++)
attach(terms[startA].coef, terms[startA].expon);
for (; startB <= finishB; startB++)
attach(terms[startB].coef, terms[startB].expon);
*finishD = avail - 1;
}
C
복사
void attach(float coefficient, int exponent) {
// 다항식에 새로운 항을 추가하는 함수
if (avail >= MAX_TERMS) {
fprintf(stderr, "Too many terms in the polynomial\n");
exit(1);
}
terms[avail].coef = coefficient;
terms[avail++].expon = exponent;
}
C
복사
희소 행렬 (Sparse Matrix)
•
보통 행렬의 표현 : a[MaxRows][MaxCols]
◦
희소 행렬 → 0이 많이 포함될 경우
C에서 희소 행렬의 구현
<row, column, value>의 쌍을 저장
•
빠른 전치를 위하여 row의 오름차순으로 저장
typedef struct {
int row;
int col;
int value;
} term;
term a[MAX_TERMS];
Java
복사
희소 행렬 ADT
Sparse_Matrix Create(rows, cols)
// rows x cols개의 항목을 저장할 수 있는 행렬 반환
Sparse_Matrix Transpose(a)
// a[i][j] = v 일때, c[j][i] = v 인 전치 행렬 c를 반환
Sparse_Matrix Add(a, b)
// if (a와 b의 dimension이 동일)
// c[i][j] = a[i][j] + b[i][j]인 c 반환
// else return error
Sparse_Matrix Multiply(a, b)
// if (a의 열의 수 == b의 행의 수)
// c[i][j] = Sum(a[i][k] x b[k][j])인 c 반환
// else return error
C
복사
행렬의 전치 (Transposing a Matrix)
•
row와 column을 교환
for each row i
take element <i, j, value> and store it
as element <j, i, value> of the transpose
C
복사
ex)
(0, 0, 15) → (0, 0, 15)
(0, 3, 22) → (3, 0, 22)
(0, 5, -15) → (5, 0, -15)
•
전치 연산을 위한 연속적인 삽입으로 인해, 기존에 저장된 항목들의 이동이 불가피
2차원 배열에서 transpose 구현
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
b[i][j] = a[j][i];
// 시간복잡도 = O(rows * cols)
C
복사
Transpose
void transpose(term a[], term b[]) {
// a는 입력행렬, b는 출력행렬, b = a^T
int count, i, j, currentB;
count = a[0].value; // a에서 0이 아닌 원소의 수
b[0].row = a[0].col; // b의 행의 수 = a의 열의 수
b[0].col = a[0].row; // b의 열의 수 = a의 행의 수
b[0].value = count; // 0이 아닌 원소 수는 a와 동일
if (count > 0) { // a가 empty matrix가 아니라면
currentB = 1; // 새로운 원소가 b에 저장될 위치
for (i = 0; i < a[0].col; i++) {
// a의 열 순서로 전치 연산 실행 (i: 현재 열)
for (j = 1; j <= count; j++) {
// a에서 현재 열을 찾자 (a는 행 순서로 저장)
if (a[j].col == i) {
// 현재 열의 원소 발견, b에 추가
b[currentB].row = a[j].col;
b[currentB].col = a[j].row;
b[currentB].value = a[j].value;
currentB++; // b의 저장될 위치를 1 증가
}
}}}
}
// 시간복잡도 = O(columns * elements) -> O(n^3)
C
복사
Fast Transpose
•
각 column이 저장될 곳을 미리 파악
•
Column Index 저장을 위한 추가적인 공간 사용
→ O(columns + elements) ≅ O(columns * rows)
→ 배열을 하나만 사용하여 구현 가능
#define MAX_COL 50
void fast_transpose(term a[], term b[]) {
// a는 입력행렬, b는 출력행렬, b = a^T
int row_terms[MAX_COL]; // a의 열의 원소 수 저장
int starting_pos[MAX_COL]; // 각 열의 시작 위치 저장
int num_col = a[0].col;
int num_terms = a[0].value;
b[0].row = num_col;
b[0].col = a[0].row;
b[0].value = num_terms;
if (num_terms > 0) { // a에 0이 아닌 원소들이 존재
for (int i = 0; i < num_col; i++)
row_terms[i] = 0; // 초기화
for (int i = 1; i <= num_col; i++)
row_terms[a[i].col]++; // a의 각 열의 원소 수 계산
// row_terms를 이용하여 시작위치 계산
starting_pos[0] = 1;
for (int i = 1; i < num_col; i++)
starting_pos[i] = starting_pos[i-1] + row_terms[i-1];
// 시작위치를 이용하여, 특정 원소의 저장위치 파악
for (int i = 1; i <= num_terms; i++) {
j = starting_pos[a[i].col]++;
b[j].row = a[i].col;
b[j].col = a[i].row;
b[j].value = a[i].value;
// b에서 동일한 행에 대해 열 순서로 저장되는가?
}
}
}
C
복사
Fast Transpose의 개선
for (i = 1; i <= num_terms; i++)
row_terms[a[i].col]++;
tmp1 = 1;
for (i = 0; i < num_cols; i++) {
tmp2 = row_terms[i];
row_terms[i] = tmp1;
tmp1 += tmp2;
}
for (i = 1; i <= num_terms; i++) {
j = row_terms[a[i].col]++;
b[j].row = a[i].col;
b[j].col = a[i].row;
b[j].value = a[i].value;
}
C
복사
희소 행렬의 곱셈
행렬의 곱셈 방법
A = m x n 행렬, B = n x p 행렬, A x B = D
D = m x p 행렬
for 0 ≤ i < m, 0 ≤ j < p
→ 희소 행렬의 곱셈 결과는 희소 행렬이 아닐 수 있음
희소 행렬의 곱셈의 예
void mmult(term a[], term b[], term d[]) {
// a, b:입력, d:출력, d=a*b
int i, j, column, totalD = 0;
int totalA = a[0].value;
int totalB = b[0].value;
int row_begin = 1, row = a[1].row, sum = 0; // row : a의 현재 행
term new_b[MAX_TERMS];
if (a[0].col != b[0].row) { // a의 열의 수와 b의 행의 수는 동일
fprintf(stderr, "Incompatible matricses\n");
exit(1);
}
else fast_transpose(b, new_b);
// 경계 조건을 설정
a[totalA+1].row = a[0].row;
new_b[totalB+1].row = b[0].col;
for (i = 1; i <= totalA;) {
column = new_b[1].row; // b의 현재열
for (j = 1; j <= totalB + 1;) {
// a의 현재 행과 b의 현재 열에 대해 곱셈 수행
if (a[i].row != row) { // a의 현재 행을 벗어남
storesum(d, &totalD, row, column, &sum);
i = row_begin; // b는 다음 열로, a는 원위치로
for (; new_b[j].row == column; j++);
column = new_b[j].row;
}
else if (new_b[j].row != column( { // b의 현재 열을 벗어남
storesum(d, &totalD, row, column, &sum);
i = row_begin; // a는 원위치
column = new_b[j].row // b는 당므 열로
}
else
switch(COMPARE(a[i].col, new_b[j].col)) {
case -1: // a[i].col < new_b[j].col, a증가
i++; break;
case 0: // 계산 후, a와 b를 모두 진행
sum += (a[i++].value * new_b[j++].value); break;
case 1: // a[i].col > new_b[j].col, b증가
j++;
}
}
for (; a[i].row == row; i++); // b의 모든 원소를 처리한 후
row_begin = i;
row = a[i].row; // a의 현재 행을 다음 행으로
} // end of for i <= totalA
d[0].row = a[0].row;
d[0].col = b[0].col;
d[0].value = totalD;
}
C
복사
void storesum(term d[], int *totalD, int column, int *sum) {
// sum이 0이 아니면, d배열의 *totalD+1 위치에 row, column 값과 함께 저장
if (*sum)
if (*totalD < MAX_TERMS) {
d[++*totalD].row = row;
d[*totalD].col = column;
d[*totalD].value = *sum;
*sum = 0;
}
else {
fprintf(stderr, "Numbers of terms in product exceeds %d\n", MAX_TERMS);
exit(1);
}
}
C
복사
Complexity
→ O(cols_b * totalA + rows_a * totalB)
전통적인 행렬 곱셈 알고리즘
for (int i = 0; i < row_a; i++) {
for (int j = 0; j < cols_b; j++) {
sum = 0;
for (int k = 0; k < cols_a; k++) {
sum += a[i][k] * b[k][j];
}
d[i][j] = sum;
}
}
C
복사
Complexity : O(rows_a * cols_b * cols_a)
다차원 배열
다차원 배열을 저장하는 두가지 방법
행 우선 순서의 주소 계산
2차원 배열 : A[upper0][upper1]
•
a가 A[0][0]의 주소라고 가정
•
A[i][0]의 주소 = a + i*upper1
•
A[i][j]의 주소 = a + i*upper1 + j
3차원 배열 : A[upper0][upper1][upper2]
•
a가 A[0][0][0]의 주소라고 가정
•
A[i][j][k]의 주소 = a + i*upper1*upper2 + j*upper2 + k
ex)
A[10][20][30] 배열에서 A[0][0][0]의 주소가 a
A[4][2][5]의 주소 = a + 4*20*30 + 2*30 + 5
= a + 2465
ex2)
A[5][7][6] 배열에서 A[0][0][0]의 주소가 a
A[4][2][5]의 주소 = a + 4*7*6 + 2*6 + 5
= a + 185