[자료구조 - C언어] 자료구조 [11강-14강] 복습 및 정리

2022. 11. 9. 16:51CS/자료구조

728x90

//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎

 

자료구조 11강

  • 배열
  • 연결 리스트
    • 노드(데이터 필드 + 링크 필드)들이 링크로 연결되어 있는 자료구조
    • 첫 번째 노드를 저장하는 head 노드, 마지막 노드의 링크 필드는 NULL ~ 연결 리스트의 끝 표시

  • 노드
  • typedef struct node{
    	char *data;           //연결 리스트에 저장할 데이터 타입 지정
    	struct node * next;   //다음 노드의 주소를 저장할 필드, 자기 참조형 구조체
    }Node;
    
    Node *head = NULL;      //연결 리스트의 첫 번째 노드의 주소를 저장할 포인터
    
    • 자기 참조형 구조체: 자신과 동일한 구조체에 대한 포인터를 멤버로 가진다.
      • struct node *next: struct node를 가리키고 있는 struct node 타입의 포인터 변수
  • 연결 리스트 사용 방법
  • //첫 노드 생성
    Node *head = (Node *)malloc(sizeof(Node));
    head->data = "2nd";
    head->next = NULL;
    
    //두 번째 노드로 삽입
    Node *q = (Node *)malloc(sizeof(Node));
    q->data = "3rd";
    q->next = NULL;
    head->next = q;
    
    //첫 번째 노드로 삽입
    q = (Node *)malloc(sizeof(Node));
    q->data = "1st";
    q->next = head;
    
    head = q;    //첫 번째 노드의 주소를 보관
    
    //출력과 순회(traverse)
    Node *p = head;
    whlie(p!=NULL){
    	printf("%s\\n", p->data);
    	p=p->next;
    }
    
  • 함수에서 head가 전역 변수가 아니면 → 호출된 함수가 호출한 곳의 head 값에 영향을 끼쳐야 한다.
    • 변수의 값 대신 변수의 주소를 매개변수로 넘겨줄 것
    • = head 노드의 주소를 값으로 넘겨주는 것이 아니라,
         head 노드의 주소가 저장된 변수의 주소를 매개변수로 → 호출한 함수에서 head의 값을 변경 O
    void add_first1(Node **ptr_head, char *item){
    	Node *temp = (Node *)malloc(sizeof(Node));
    	temp->data = item;
    	temp->next = *ptr_head;
    	*ptr_head = temp;
    }
    
    add_first1(&head, item_to_store);
    
    //or 새로운 head 노드의 주소를 return
    Node *add_first2(Node *head, char *item){
    	Node *temp = (Node *)malloc(sizeof(Node));
    	temp->data = item;
    	temp->next = head;
    	return temp;
    }
    
    head = add_first2(head, item_to_store);
    
  • 삽입과 삭제
    • 삽입과 삭제 모두 삽입/삭제할 위치의 바로 앞 노드의 주소가 필요하다.
      • 그렇기 때문에 주로 어떤 노드의 뒤에 삽입한다.
    • 삽입
      void add_after(Node *prev, char *item){
          if(prev==NULL)
          return;
          Node *temp = (Node*)malloc(sizeof(Node));
          temp->data = item;
          temp->next = prev->next;
          prev->next = temp;
      }
    • 삭제
      //첫 번째 노드의 삭제
      Node *remove_first(){
          if(head==NULL)
              return NULL;
          Node *temp = head;
          head = head->next;
          return temp;
      }
      
      //어떤 노드의 다음 노드 삭제
      Node *remove_after(Node *prev){
          Node *temp = prev->next;
          if(temp==NULL)
              return NULL:
          else{
              prev->next=temp->next;
              return temp;
          }
      }
  • 순회(traverse): 연결 리스트의 노드들을 순차적으로 방문하는 것
    else{
        q->next = p->next;
        free(p);	
    }
  • 투 포인터를 사용하기
    • 삽입/삭제하려는 위치의 앞(이전) 노드의 주소를 알아야 뒤에 삽입/삭제할 수 있다.
    Node *p = head;
    Node *q = NULL:
    
    while(p!=NULL && 조건){
        q = p;        //q는 p를 한 칸 뒤에서 따라가는 역할
        p = p->next;  //p는 한 칸 전진
    }

 

자료구조 12강

  • 객체를 생성하고 기본값으로 초기화해주는 함수를 따로 만들어 사용하면 초기화 오류를 예방할 수 있다.
  • 단방향 연결 리스트에서 삽입 연산
    • 맨 앞에 삽입하는 경우(empty list 포함)
      //p노드 이후에 삽입
      //q노드는 p노드의 한 칸 뒤에 위치
      if(q==NULL){
          new_node->next = head->next;
          head = new_node;
      }
    • 중간에 삽입하는 경우
      else{
          new_node->next = p->next;
          p->next = new_node;
      }
  • 단방향 연결 리스트에서 삭제 연산(empty list가 아닐 때)
    • 첫 번째 노드를 삭제
      //삭제하고자 하는 노드 p
      //삭제하고자 하는 노드의 이전 노드 q
      if(q==NULL){
          head = p->next;
          free(p);
      }
    • 이외의 경우
      • p 노드가 맨 마지막 노드이더라도 p→next는 NULL 이기 때문에 q→next = NULL;
      else{
          q->next = p->next;
          free(p);	
      }

 

자료구조 13강

  • 양방향 연결 리스트의 Node
typedef struct node{
    char *data;
    struct node *next;
    struct node *prev;
}Node;

Node *head;    //연결 리스트의 첫 번쨰 노드 주소 저장
Node *tail;    //연결 리스트의 마지막 노드 주소 저장
  • 양방향 연결 리스트에서의 삽입
    • 앞 노드의 주소가 있어야 그 뒤에 삽입이 가능한 단방향 리스트와 다르게
    • 양방향 연결 리스트에서는 prev next를 통해 어떤 노드의 앞에 노드를 삽입할 수 있다.
      [어떤 노드 에 새로운 노드 삽입하기] ‼️새로운 노드 → 앞 연결 → 뒤 연결‼️ (순서 유의)
      (0) 새로운 노드를 만들고 추가할 데이터를 저장한다.
      (1) 새로운 노드의 링크를 먼저 연결한다.
      (2) 새로운 노드의 앞 노드가 새로운 노드를 가리키도록 한다.
      (3) 새로운 노드의 뒷 노드가 새로운 노드를 가르키도록 한다.
      //(0) 새로운 노드를 만들고 추가할 데이터 저장하기
      Node *new_node = (Node *)malloc(sizeof(Node));
      new_node->data = "by";
      
      //(1) 새로운 노드의 링크를 먼저 연결
      new_node->next = p;
      new_node->prev = p->prev;
      
      //(2) 새로운 노드의 앞 노드가 새로운 노드를 가리키도록
      p->prev->next = new_node;
      
      //(3) 새로운 노드의 뒷 노드가 새로운 노드를 가리키도록
      p->prev = new_node;
  • [case 별로 어떤 노드 에 새로운 노드 삽입하기] ‼️새로운 노드 → 뒤 연결 → 앞 연결‼️ (순서 유의)
    Node head = (Node *)malloc(sizeof(Node));
    Node tail = (Node *)malloc(sizeof(Node));
    Node *p = head;
    
    void add_after(Node *pre, char *item);
    • 연결 리스트에 노드가 하나도 없는 empty list
      //head가 NULL이면 empty list, empty list이기 때문에 pre도 NULL
      if(pre==NULL && head==NULL){
          head = new_node;
          tail = new_node
      }
    • 맨 앞에 삽입하는 경우
      //head != NULL일 떄 pre==NULL이면 제일 앞에 삽입해야 하는 경우
      else if(pre==NULL){
          new_node->next = head;
          head->prev = new_node;
          head = new_node;         //첫 노드의 주소를 가지고 있는 head노드를 가장 마지막에
      }
    • 맨 마지막에 삽입하는 경우
      //pre 뒤에 삽입해야하는데, pre가 마지막이면 마지막에 삽입해야 하는 경우
      else if(pre==tail){
          new_node->prev = tail;
          tail->next = new_node;
          tail = new_node;         //마지막 노드의 주소를 가지고 있는 head노드를 가장 마지막에
      }
    • 중간에 삽입하는 경우
      else{
          new_node->prev = pre;
          new_node->next = pre->next;
          pre->next->prev = new_node;
          pre->next = new_node;
      }
  • 양방향 연결 리스트에서의 삭제
    • 앞 노드의 주소가 있어야 그 뒷 노드를 삭제할 수 있는 단방향 리스트와 다르게
    • 양방향 연결 리스트에서는 삭제하고자 하는 노드만 있다면 그 노드를 삭제할 수 있다.[노드 삭제하기] ‼️앞 → 뒤‼️ (순서 유의)
      (1) 삭제할 노드의 앞 노드가 삭제할 노드의 뒷 노드를 가리키도록 한다.
      (2) 삭제할 노드의 뒷 노드가 삭제할 노드의 앞 노드를 가리키도록 한다.
      p->prev->next = p->next;
      p->next->prev = p->prev;
  • [case 별로 노드 삭제하기]
    • 삭제할 노드 p가 유일한 노드
      if(head==p && tail==p){
          head = NULL;
          tail = NULL;
          free(p);
      }
    • 제일 앞 노드인 경우 = p가 head인 경우
      //p가 tail이 아닐 때 p가 head 라면 ~ 제일 앞 노드를 삭제
      else if(p==head){
          p->next->prev = NULL;
          head = p->next;
          free(p);
      }
    • 제일 뒷 노드인 경우 = p가 tail인 경우
      //p가 head가 아닐 때 p가 tail 이라면 ~ 제일 마지막 노드를 삭제
      else if(p==tail){
          p->prev->next = NULL;
          tail = p->prev;
          free(p);
      }
    • 중간 삭제
      else{
          p->prev->next = p->next;
          p->next->prev = p->prev;
          free(p);
      }
  • strcmp를 활용해서 정렬된 위치에 노드를 삽입할 수 있다.
  • 원형 연결 리스트: 마지막 노드 ➡️ 이전 노드
    • 원형 이중 연결 리스트: 마지막 노드 ↔  이전 노드

 

자료구조 14강

  • 프로그램을 여러 개의 소스파일로 분리할 경우
    • 서로 연관된 함수들과 변수들이 하나의 파일에 있어(모듈화) → 구조가 좀 더 알기 쉽고 명료
    • 수정된 파일만 개별적으로 컴파일 → 컴파일 시간 절약
    • 소프트웨어 재사용 용이
  • 여러 개의 소스 파일로 분할할 경우 해결해야 할 문제점
    • 어떻게 다른 파일에 정의되어 있는 함수를 호출? && 어떻게 다른 파일들이 메크로나 타입 정의를 공유?
      • header 파일과 source 파일을 분리한 후 다른 파일에서 header 파일을 include
      • header 파일에는 프로토타입, source 파일에는 실제 구현
      • header 파일에 다른 소스파일과 공유할 매크로, 타입 정의를 넣기
    • 어떻게 다른 파일에 정의되어 있는 변수를 사용?
      • header 파일에 공유 변수의 선언 → 공유 변수를 사용할 모든 source 파일은 header 파일을 include
      • 오직 한 source 파일에서 공유 변수를 정의
        • 변수의 선언(declaration): 컴파일러에게 변수의 존재를 알려줌
          //정의하지 않고 선언만 하기
          extern int i;
          extern int a[];
        • 변수의 정의(definition): 실제로 메모리를 할당
      • 서로 다른 파일 간의 변수는 최대한 공유하지 않는 것이 좋다.
    • 중첩된 include 문제
      • 매크로 정의, 함수 프로토타입, 외부 변수의 선언은 여러 번 중복되어도 상관없다.
      • 하지만 타입 정의가 중복되는 것은 컴파일러의 오류를 야기
      • #indef - #endif
      #indef (header파일이름)
      #define (header파일이름으로 매크로 정의)
      
       ...
      
      #endif
      
  •  

 

 

 


부경대학교 권오흠 교수님의 [c로 배우는 자료구조 및 여러 가지 예제 실습] 강의 정리입니다. 감사합니다.

728x90