본문 바로가기

Datastructure/[5] 집합

[5] 집합 ② 연결리스트를 이용한 집합 구현

728x90
반응형

연결리스트를 이용한 집합 구현

아래 알고리즘에서 단일 연결리스트의 헤더노드는 값을 저장하는 유효 노드로서 작동한다.

집합 알고리즘의 구조체 선언

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h>

typedef int element;

typedef struct DListNode {
	element data;
	struct DListNode* next;
	struct DListNode* prev;
}DListNode;

typedef struct SetType {
	DListNode* H;
    DListNode* T;
    element N;
}SetType;
DListNode SetType
멤버 data 집합의 데이터 저장 변수 멤버 H 헤드 노드 저장
next 다음 노드의 위치 저장 T 테일 노드 저장
prev 이전 노드의 위치 저장 N 노드의 개수
기능 연결리스트의 노드로 작동하며 3개로 구분됨 기능 연결리스트 노드를 포괄적으로 관리함.

getNode 함수:  초기화된 임의의 노드 생성

① node라는 임의의 노드를 동적할당한다.

②  node의 데이터 값을 0으로 초기화한다.

③ 위치 저장 구조체 포인터를 초기화한다.

DListNode* getNode(){
    DListNode* node = (DListNode*)malloc(sizeof(DListNode));
    node -> data = 0;
    node ->next = NULL;
    node ->prev = NULL;

    return node;
}

addNode함수: p 위치에 노드를 추가

① new라는 노드를 생성해 저장한다. getNode 함수를 이용한다.

② new노드에 데이터 값 e를 저장한다.

③ 생성한 new 노드와 인자로 받은 p 노드를 연결한다.

노드 추가 (이중 연결)

void addNode(SetType* S,DListNode* p, element e){
    DListNode *new = getNode();
    new ->data = e;
    
    new->prev = p;
    new->next = p->next;

    p->next->prev = new;
    p->next = new;

    S->N ++;
}

init 함수: 연결리스트 초기화 (SetType)

① 헤드 노드에 노드를 생성 후 저장

② 테일 노드에 노드를 생성 후 저장

③  헤드 노드의 다음 노드를 테일 노드로 연결

  헤드 노드의 이전 노드를 헤드 노드로 연결

⑤  노드의 개수를 0으로 초기화

void init(SetType* S) {
    S->H = getNode();
    S->T = getNode();

    S->H ->next = S->T;
    S->T->prev = S->H;
    S->N = 0;
}

add 함수: 연결리스트 추가

값을 정렬하여 추가하는 함수

 

① S의 헤드 노드에 접근하고 이를 p에 저장한다.

② 변수 status의 값을 1로 초기화한다.

③ p의 다음 노드가 테일 노드인 경우(노드가 하나도 없는 경우), S에 p노드를 추가한다.

p의 다음 노드가 테일 노드가 아닌 경우(노드가 하나 이상 존재), 

⑤ e >= p->next->data && p->next->next!= NULL 조건을 만족하는 경우 반복

⑥ 인자로 받은 데이터 값이 p의 다음 노드 데이터와 같은 경우 status를 0으로 하고 반복을 종료한다.

⑦ p 다음 노드로 이동한다.

⑧ status 값이 1인 경우 노드를 추가한다.

void add(SetType* S, element e) { 
    DListNode* p = S->H;
    int status = 1;

    if(p->next == S->T) addNode(S,p,e);
    else{
        while(e >= p->next->data && p->next->next != NULL){
            //입력된 원소의 값이 노드의 값보다 큰 경우
            // = 노드를 순회하며 값을 저장해야함.
            if(e == p->next->data) {status = 0;break;}
            p = p->next;
        }//값이 작거나, 마지막 노드보다 작거나, 값이 같거나
        if(status == 1)addNode(S,p,e);
    }
}

add 함수에서 addNode 함수의 실행 조건은 아래와 같다.

1 노드가 하나도 없는 경우(H,T만 존재)
2 순회하고 있는 노드의 다음노드의 데이터 보다 크고 순회 중인 노드가 T의 이전 이전 노드보다 왼쪽인 경우 순회하고 있는 다음 노드의 데이터와 중복이 아닌 경우

 

· H와 T 노드만 있는 경우 노드는 무조건 추가된다.

· 노드가 한 개 이상 인 경우 위 표의 2번 조건을 만족해야만 노드가 추가된다. 이는 중복과 선형 오름차순을 위함이다.

· 아래 코드를 이용해 중복을 제거한다. 따라서 반복 횟수만큼 노드가 생성되지 않을 수 있다.

· 처음에 높은 수가 생성될 때, 낮은 숫자가 생성되면 while을 거치지 않고 노드가 추가될 수 있다. (while 조건에 부합하지 않음)

if(e == p->next->data) {status = 0;break;}

addLast 함수: 후미에 연결리스트 추가

new라는 노드를 생성해 저장한다. getNode 함수를 이용한다.

② new노드에 데이터를 저장한다.

③ S의 테일 노드에 접근하고 이를 p에 저장한다. 리스트의 후미에 노드를 추가하기 위함이다.

new->prev에 S->T ->prev를 연결한다.

⑤ S->T->prev->next 에 new를 연결한다.

⑦ S->T->prev에 new를 연결한다.

⑧ S의 노드 개수를 증가한다.

addLast 구조

void addLast(SetType* S, element e){
    DListNode* new = getNode();
    new->data = e; // 새로운 노드에 값 저장
    DListNode* p = S->T;
    
    new->prev = S->T ->prev;
    S->T->prev->next = new;
    S->T->prev = new;
    // printf("[%d]\n",new->data);
    S->N++;
}

 

UnionSe함수: 합집합 생성

① 반환할 리스트 S3를 생성하고 초기화한다.

② 합집합을 만들기 위해 인자로 받은 두 개의 리스트를 각각 p, q에 저장한다. 

   이때 NULL값으로 시작하는 것이 아니라, 노드만 사용한다. (S->H->next)

③ p와 q가 모두 NULL이 아닐 때까지 반복한다.

p의 요소가 q의 요소보다 큰 경우 p의 요소를 합집합에 추가하고 p를 다음 노드로 보낸다.

⑤ q의 요소가 p의 요소보다 큰 경우 q의 요소를 합집합에 추가하고 q를 다음 노드로 보낸다.

p의 요소와 q의 요가 같은 경우 p 또는 q의 요소를 합집합에 추가하고 p와 q를 다음 노드로 보낸다.

⑦ 남은 원소를 추가한다.

⑧ 합집합 S3를 반환한다.

SetType UnionSet(SetType* S1, SetType* S2) {
    SetType S3;
    init(&S3);

    DListNode* p = S1->H->next;
    DListNode* q = S2->H->next;

    while (p != S1->T && q != S2->T) {
        if (p->data < q->data) {
            addLast(&S3, p->data);
            p = p->next;
        } else if (p->data > q->data) {
            addLast(&S3, q->data);
            q = q->next;
        } else { // 두 집합에 모두 존재하는 원소인 경우
            addLast(&S3, p->data);
            p = p->next;
            q = q->next;
        }
    }

    // 남은 원소들 추가
    while (p != S1->T) {
        addLast(&S3, p->data);
        p = p->next;
    }
    while (q != S2->T) {
        addLast(&S3, q->data);
        q = q->next;
    }

    return S3;
}

IntersectSet 함수: 교집합 생성

① 반환할 리스트 S3를 생성하고 초기화한다.

② 교집합을 만들기 위해 인자로 받은 두 개의 리스트를 각각 p, q에 저장한다. 

   이때 NULL값으로 시작하는 것이 아니라, 노드만 사용한다. (S->H->next)

③ p와 q가 모두 NULL이 아닐 때까지 반복한다.

 p의 요소가 q의 요소보다 큰 경우 q를 다음 노드로 보낸다.

⑤ q의 요소가 p의 요소보다 큰 경우  p를 다음 노드로 보낸다.

 p의 요소와 q의 요가 같은 경우 p 또는 q의 요소를 합집합에 추가하고 p와 q를 다음 노드로 보낸다.

⑦ 교집합은 하나의 집합이 종료되면 더 이상 순회할 필요가 없다.

⑧ 교집합 S3를 반환한다.

두 리스트 중 하나의 값이 더 큰 경우(두 리스트의 값이 다른 경우),
작은 쪽을 크게하여 같은 값을 찾는다.
SetType IntersectSet(SetType* S1, SetType* S2) {
    SetType S3;
    init(&S3);

    DListNode* p = S1->H->next;
    DListNode* q = S2->H->next;

    while (p != S1->T && q != S2->T) {
        if (p->data < q->data) {
            p = p->next;
        } else if (p->data > q->data) {
            q = q->next;
        } else {
            addLast(&S3, p->data);
            p = p->next;
            q = q->next;
        }
    }
    return S3;
}

DifferenceSet 함수: 차집합 생성

① 반환할 리스트 S3를 생성하고 초기화한다.

② 인자로 받은 S1에 S2의 요소를 제거한 차집합을 구하기 위해 p에 S1->H->next를 저장한다.

③ p가 NULL이 아닐 때까지 반복한다.

 p의 요소가 q의 요소보다 큰 경우 q를 다음 노드로 보낸다.

⑤ p의 요소가 S2에 존재하면, 차집합에 추가한다.

p의 요소가 S2에 존재하지 않으면, 다음 노드로 넘어간다.

⑦ 차집합 S3를 반환한다.

//차집합
SetType DifferenceSet(SetType* S1, SetType* S2) {
    SetType S3;
    init(&S3);

    DListNode* p = S1->H->next;

    while (p != S1->T) {
        if (!isMember(S2, p->data)) {
            addLast(&S3, p->data);
        }
        p = p->next;
    }

    return S3;
}

isMember 함수: 집합 요소 확인

① 임의의 노드 p를 생성한다.

② p = S->H->next부터 p->next!= NULL일 때까지 p = p->next를 저장한다.

③ 인자로 받은 요소 e가 집합에 존재하면 1을 반환한다.

조건문을 만족하지 못한 경우 0을 반환한다.

int isMember(SetType* S, element e){
    DListNode* p;
    for (p = S->H->next; p->next != NULL; p = p->next) {
        if(p->data == e) return 1;
    }
    return 0;
}

traverse 함수: 순회 및 출력 함수

void traverse(SetType* S){
    DListNode* p;
    for (p = S->H->next; p->next != NULL; p = p->next) {
      printf("[%d] <=> ", p->data);
   }printf("\b\b\b\b   \n");
}

 

main 함수

void lineprint(){printf("------------------------------------------------------------------------------------------------------------------------\n");}

int main(){
    SetType A,B;
    init(&A);init(&B);

    srand(time(NULL));
    for(int i=0;i<7;i++){
        int e1  = rand() % 10 +1;
        int e2  = rand() % 10 +1;

        add(&A,e1);
        add(&B,e2);
    } lineprint();    
    printf("A : "); traverse(&A);
    printf("B : "); traverse(&B); lineprint();    
    SetType union_ = UnionSet(&A,&B);
    printf("union : "); 
    traverse(&union_);lineprint();    

    SetType intersection;  init(&intersection);
    intersection = IntersectSet(&A,&B);
    printf("intersection : "); traverse(&intersection); 
    lineprint();    
    SetType differenceAB = DifferenceSet(&A,&B);
    printf("difference(AB) : "); traverse(&differenceAB);
    SetType differenceBA = DifferenceSet(&B,&A);
    printf("difference(BA) : "); traverse(&differenceBA); lineprint();    

    
}

전체 코드

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h>

typedef int element;

typedef struct DListNode {
	element data;
	struct DListNode* next;
    struct DListNode* prev;
}DListNode;

typedef struct SetType {
	DListNode* H;
    DListNode* T;
    element N;
}SetType;

DListNode* getNode(){
    DListNode* node = (DListNode*)malloc(sizeof(DListNode));
    node -> data = 0;
    node ->next = NULL;
    node ->prev = NULL;

    return node;
}

void addNode(SetType* S,DListNode* p, element e){
    DListNode *new = getNode();
    new ->data = e;

    new->prev = p;
    new->next = p->next;

    p->next->prev = new;
    p->next = new;

    S->N ++;
}

int isMember(SetType* S, element e){
    DListNode* p;
    for (p = S->H->next; p->next != NULL; p = p->next) {
        if(p->data == e) return 1;
    }
    return 0;
}

void init(SetType* S) {

    S->H = getNode();
    S->T = getNode();

    S->H ->next = S->T;
    S->T->prev = S->H;
    S->N = 0;
}
void add(SetType* S, element e) { 
    DListNode* p = S->H;
    int status = 1;

    if(p->next == S->T) addNode(S,p,e);
    else{
        while(e >= p->next->data && p->next->next != NULL){
            /*
            인자로 받은 값이 p->next의 노드보다 큰 경우 : 작은 순서대로 정렬 (중복 배제 전)
            p->next->next != NULL인경우
            */
            //입력된 원소의 값이 노드의 값보다 큰 경우
            // = 노드를 순회하며 값을 저장해야함.
            if(e == p->next->data) {status = 0;break;}
            p = p->next;
        }//값이 작거나, 마지막 노드보다 작거나, 값이 같거나
        if(status == 1)addNode(S,p,e);
    }
}
void addLast(SetType* S, element e){
    DListNode* new = getNode();
    new->data = e; // 새로운 노드에 값 저장
    DListNode* p = S->T;
    new->prev = S->T ->prev;
    new->next = S->T;

    S->T->prev->next = new;
    S->T->prev = new;
    // printf("[%d]\n",new->data);
    S->N++;
}



SetType UnionSet(SetType* S1, SetType* S2) {
    SetType S3;
    init(&S3);

    DListNode* p = S1->H->next;
    DListNode* q = S2->H->next;

    while (p != S1->T && q != S2->T) {
        if (p->data < q->data) {
            addLast(&S3, p->data);
            p = p->next;
        } else if (p->data > q->data) {
            addLast(&S3, q->data);
            q = q->next;
        } else { // 두 집합에 모두 존재하는 원소인 경우
            addLast(&S3, p->data);
            p = p->next;
            q = q->next;
        }
    }

    // 남은 원소들 추가
    while (p != S1->T) {
        addLast(&S3, p->data);
        p = p->next;
    }
    while (q != S2->T) {
        addLast(&S3, q->data);
        q = q->next;
    }

    return S3;
}
/*교집합*/
SetType IntersectSet(SetType* S1, SetType* S2) {
    SetType S3;
    init(&S3);

    DListNode* p = S1->H->next;
    DListNode* q = S2->H->next;

    while (p != S1->T && q != S2->T) {
        if (p->data < q->data) {
            p = p->next;
        } else if (p->data > q->data) {
            q = q->next;
        } else {
            addLast(&S3, p->data);
            p = p->next;
            q = q->next;
        }
    }

    return S3;
}

//차집합
SetType DifferenceSet(SetType* S1, SetType* S2) {
    SetType S3;
    init(&S3);

    DListNode* p = S1->H->next;

    while (p != S1->T) {
        if (!isMember(S2, p->data)) {
            addLast(&S3, p->data);
        }
        p = p->next;
    }

    return S3;
}



void traverse(SetType* S){
    DListNode* p;
    for (p = S->H->next; p->next != NULL; p = p->next) {
      printf("[%d] <=> ", p->data);
   }printf("\b\b\b\b   \n");
}

void lineprint(){
    printf("------------------------------------------------------------------------------------------------------------------------\n");
}
int main(){
    SetType A,B;
    init(&A);init(&B);

    srand(time(NULL));
    for(int i=0;i<7;i++){
        int e1  = rand() % 10 +1;
        int e2  = rand() % 10 +1;

        add(&A,e1);
        add(&B,e2);
    } lineprint();    
    printf("A : "); traverse(&A);
    printf("B : "); traverse(&B); lineprint();    
    SetType union_ = UnionSet(&A,&B);
    printf("union : "); 
    traverse(&union_);lineprint();    

    SetType intersection;  init(&intersection);
    intersection = IntersectSet(&A,&B);
    printf("intersection : "); traverse(&intersection); 
    lineprint();    
    SetType differenceAB = DifferenceSet(&A,&B);
    printf("difference(AB) : "); traverse(&differenceAB);
    SetType differenceBA = DifferenceSet(&B,&A);
    printf("difference(BA) : "); traverse(&differenceBA); lineprint();    

    
}
728x90
반응형

'Datastructure > [5] 집합' 카테고리의 다른 글

[5] 집합 ① 배열을 이용한 집합 구현  (0) 2024.04.06
댓글