데이타구조

2016-03-15

 

배열, 포인터, 동적할당, 구조체
귀납적 정의 (Inductive Definition)
리스트, 트리

퀴즈

  1. 추상 데이타 타입 (Abstract Data Type) 이란?
    답: 인터페이스(interface) 와 구현(implementation)을 분리해서 생각
  2. 추상 데이타 타입을 사용하는 이유? (효과랄까?)
    답: 인터페이스를 유지하면서 내부 구현을 바꿀 수 있다 (더 효율적이거나 새로운 기술 도입)
  3. 크기가 n인 배열 a[n]에 대한 다음 프로그램의 시간복잡도로 맞는 것을 모두 고르시오 ... 답: 3,4
  4.      1. O(1)   2. O(n)   3. O(n^2)   4. O(n^3)
    for (int i = 0; i < n; ++i) {
        for (int j = i+1; j <n; ++ i) {
                swap(a+i, a+j) // O(1)
        }
    }

kyagrd@korea.ac.kr
제목: [퀴즈0315] 이름 학번

과제 제출 안내

  • Google Drive 공유 폴더를 통해 (종이 X, 메일 X)
  • [새로 만들기] 버튼을 눌러
    DS16<이름><학번> 폴더를 만들고 (예: DS16김연아123456)
    만들어진 폴더를 우클릭하여
    폴더 공유설정으로 kyagrd@gmail.com 에게 공유 (편집권한)
  • Google 메일 계정만 있으면 Google Drive 서비스 이용 가능 
  • Google 메일 계정은 무료로 만들 수 있으며
    참고로 안드로이드 스마트폰 사용자는 누구나 이미 보유

배열과 포인터 요약 정리

  • T a[N]; // T 타입의 길이 N짜리 배열

    • a는 첫 칸 주소를 가리키는 포인터와 같은 역할
      • *a == a[0] 또는 a == &(a[0])
    • sizeof(a) == sizeof(T) * N
  • T* p = a; // p도 배열 a의 첫 칸 주소를 가리킴
    • 그러나 sizeof(p) 는 보통은 sizeof(int) 와 같다
    • p[i] == *(p + i) == *(i + p) == i[p]   // 네번째는 비추
  • 할당된 메모리의 끝의 바로 다음까지는 계산해도 됨
    • a + N 또는 p + N 까지 하는 것은 괜찮다
    • a[N] 또는 *(a + N) 는 하면 안된다 

동적 (메모리) 할당

  • C++의 new 문법이 훨씬 간결하므로 C++ 기준으로 설명
    (C의 동적할당이 궁금하면 교과서에서 찾아볼 것)

  • 정적 할당 (일반 변수 및 배열)
    • 실행 전에 필요한 메모리 크기가 결정됨
    • 변수 스코프를 벗어나면 알아서 메모리가 해제됨
  • 동적 할당
    • 프로그램 실행 중에 메모리가 필요한 만큼
    • C/C++에서는 기본적으로 수동으로 메모리를 해제해야
    • 동적 할당된 메모리를 자동으로 해제하는 방식으로는 Reference Counting과 Garbage Collection이 있다

동적 (메모리) 할당

  • T* p = new T;

    • T 타입의 값을 하나 저장할 수 있는 메모리 공간 할당
    • delete p; 와 같이 해제
  • T* p = new T[N]; // 동적 배열 할당
    • T 타입의 값을 N개 저장할 수 있는 연속적 공간 할당
    • delete [] p; 와 같이 해제
    • N값을 프로그램 실행 중에 계산할 수 있음
    • 포인터일 뿐 sizeof(p) 는 그냥 주소값 정수 크기
      배열처럼 할당된 공간을 sizeof로 알 수 없다
  • 이 수업에서는 메모리 수동 해제는 생략할 방침

이차원 이상 배열 동적할당 팁

  • ??????? p = new T[M][N1][N2]...[Nn];
    • 이거 무슨 포인터 타입인지 생각하면 머리아픔
    • 결론적으로는 T[N1][N2]...[Nn] 에 대한 포인터인데
      이걸 C/C++ 문법으로 어떻게 쓸건지도 머리아픔
  • 그러므로 typedef 활용해서 컴파일러가 알아서 하도록
    • typedef int MyInt;
    • typedef T MyArray[N1][N2]...[Nn];
      int main(void) {
          MyArray* = new MyArray[M];
      }

동적할당하면서 초기화 팁

  • T* p = new T( 초기값 );
  • int* p = new int(3); // *p == 3

구조체

  • struct  MyType  { T1 name1; T2 name2; ...; Tn namen };

    • struct  list  {  int val;   list* next;  };

    • struct  tree  {  int val;   tree* left;   tree* right;  };

    • struct  point  {  float x;   float y;  };

  • point p = { 1.3, 4.5 }; // 배열과 초기화와 같은 문법

    • p.x == 1.3

    • (&p)->y == 4.5

  • point* ptr = new point( { 1.3, 4.5 } ); // 동적 할당 + 초기화

    • ptr->x == 1.3

    • (*ptr).y == 4.5

귀납적 정의 (Inductive Definition)

  • 자연수의 귀납적 정의

    • 0 은 자연수 (base case)

    • n 이 자연수면 1+n 도 자연수 (inductive case)

  • 참고: 수학적귀납법
    P(0)가 참임을 보이고 (base case)
    P(n)이 참이라면 P(1+n)도 참임을 보이면 (inductive case)
    모든 자연수에 대해 P라는 성질이 만족함을 증명한 것이다

재귀적(recursive) 데이타 구조의 귀납적 정의

  • 리스트의 귀납적 정의

    • [] 는 리스트 (base case)

    • l 이 리스트면 v :: l 도 리스트 (inductive case)

  • Binary Tree (두갈래나무 또는 이진트리)의 귀납적 정의
    • nil 은 두갈래나무

    • t1 과 t2 가 두갈래나무라면              도 두갈래나무

t1

t2

v

(참고: 교과서에서는 recursion을
"재귀" 대신 "순환"이라 번역함)

귀납적 정의를 코드로 옮기면

  • struct list { int val; list* next };

    • NULL // 또는 0

    • // v가 int 값이고 l은 list 포인터일 때
      new list( { v, l } )

  • struct tree { int val; tree* left; tree* right; }; // binary tree
    • NULL // 또는 0

    • // v가 int 값이고 t1과 t2가 tree 포인터일 때
      new tree { v, t1, t2 }

과제 (Due: 3/2월요일 밤 1번만)

  1. 정수 배열로부터 다음을 구하는 프로그램 작성

    • 평균값(mean) -- 쉽다

    • 중간값(median) -- 힌트는 정렬

    • 최빈값(mode) -- 힌트는 역시 정렬

  2. 이번 수업에서 다룬 NULL로 끝나는 연결 리스트에 대해
    • 리스트의 맨 끝에 원소를 하나 추가하는 프로그램 작성
    • 순서를 거꾸로 뒤집은 리스트를 만드는 프로그램 작성
  • 이번 과제는 main함수 이외에 다른 함수 작성 안해도 됩니다.
  • Google Drive 자신의 공유폴더에 hw1.cpp 파일명으로 제출
  • 반드시 컴파일되는지 확인. 컴파일 에러나면 0점.

데이타구조

By 안기영 (Ahn, Ki Yung)