C List 대입 - C List daeib

위의 1과 2를 통해 list 원소들을 복사하는 방법에 대해 알아봤다. 물론 또다른 방법으로는 직접 루프를 작성하거나 list의 insert 멤버 함수를 사용하는 방법이 있을 것이다. 하지만 효용성의 측면에서 assign 멤버 함수를 사용하여, 원소를 복사하는 것이 좋다고 하는데, 이는 이중 링크트 리스트인 list의 next, prev 포인터가 계속되는 원소 추가에 대해 불필요한 포인터 세팅을 하지 않도록 하고, 한번의 대입 만으로 각 포인터가 삽입 후의 노드 주소 값을 가지도록 세팅해주기 때문이다.

안녕하세요 오늘은 C++ 자료구조 컨테이너 중 하나인 vector 라이브러리에 대해 살펴봅시다. [1탄 목차] 1. 벡터란 무엇인가? 2. 벡터의 구조와 특징 (장단점) 3. 언제 벡터를 사용하는가 4. 벡터를 사

jhnyang.tistory.com

리스트 LIST란 무엇인가? 간단요약

이전 포스팅에서 살펴봤던 vector 처럼 데이터를 저장하기 위한 일종의 자료구조예요.

vector, list외에도 stack, queue, heap, graph 등등.. 다양한 자료구조가 존재하는대요.

 

다수의 데이터를 저장할 수 있는 vector가 있음 그걸 쓰면 되지, 왜 리스트와 같은 또 다른 자료구조가 필요하냐고요?

자료를 저장하는 방식에 따라 장단점이 달라지기 때문입니다. A방식은 데이터를 추가하는데 성능이 좋은데, B방식은 검색하는데 성능이 좋고~ 모두 다 좋으면 좋겠지만 그럴 수 없기에 내가 구현하고자 하는 프로그램의 목적에 맞춰서 효율적으로 데이터를 사용하기 위함이예요.

리스트란 순서를 가진 항목들의 모임입니다. ADT를 정의하면 저장 형태는 데이터를 나란히 저장을 하며 저장 특성으로는 중복된 데이터의 저장을 허용한다는 것입니다. 리스트 자료구조의 ADT의 예시는 다음과 같습니다. (지정 하기 나름입니다)

    • 리스트의 초기화
    • 데이터 저장 (데이터 추가)
    • 저장된 데이터의 탐색 및 탐색 초기화
    • 다음 데이터 참조(반환)
    • 바로 이전에 참조(반환)이 이루어진 데이터의 삭제
    • 현재 저장되어 있는 데이터의 수 반환

그럼 이걸 C언어 함수 같이 만들어 볼까요?

    • ListInit(&list) :: 리스트 초기화
    • Linsert(&list, item) :: 데이터 저장
    • LFirst(&list, item) :: 저장된 데이터의 탐색 및 탐색 초기화
    • LNext(&list, item) :: 다음 데이터의 참조(반환)
    • LRemove(&list) ::바로 이전에 참조(반환)이 이루어진 데이터의 삭제
    • LCount(&list) :: 현재 저장되어 있는 데이터 수 반환

배열을 이용하여 만드는 방법과 동적 할당을 통해서 이용하는 방법입니다. 우선 이번에는 배열을 통한 리스트 구현에 대해서 알아보겠습니다.

C List 대입 - C List daeib

1차원 배열을 이용하기 때문에 다음과 같은 특징이 있습니다. 우선 1차원 배열에 항목들을 순서대로 저장합니다. 2번째는 삽입위치부터의 항목들을 뒤로 밀어 주어야 한다는 것이고 3번쨰는 삭제 연산시에 삭제를 한 자리를 비우고 다음항목들을 앞으로 당겨 줘야 합니다. 이는 배열의 특징중 중간 데이터가 빌 수 없다는 특징에 따라서 위의 경우들을 지켜주어야합니다.

배열로 만들면 어떤게 좋고 어떤게 안좋을까요? 다음과 같이 나눠봤습니다.

  • 장점
    • 구현이 간단하다
    • 인덱스 값 기준으로 어디든 한 번에 참조 가능(3번 자료! -> 2번 배열!) -> 데이터 참조가 쉬움
  • 단점
    • 삽입, 삭제 시 오버헤드 : 삽입 및 삭제 과정에서 데이터 이동(복사)가 매우 빈번하게 일어남
    • 항목의 개수 제한 : 배열의 길이는 초기에 결정하고 변경할 수 없기 때문에 유한적인 데이터만 넣을 수 있다.

이제 특징과 장단점도 알아봤으나 구현을 해보겠습니다. 배열로 구현을 하기 위하여 순차 리스트 구조체를 다음과 같이 썼습니다..

    int Array[MAX_SIZE];//배열의 크기

    int length = 0;//배열에 저장된 항목 개수

cs

앞으로의 자료구조가 그렇듯 꼭 같을 필요는 없습니다. 구현해보는 것이기 때문에 데이터 참조위치 변수를 더 추가한다거나 할 수도 있습니다.

저는 배열로 리스트를 구현할 때 다음의 ADT들을 구현하였습니다.

void Add_First(ListArray *L, int Value)//배열 리스트의 가장 처음 데이터를 추가

void Add(ListArray *L, int Value, int POSITION)//배열 리스트의 특정 위치에 데이터를 추가

void Add_Last(ListArray *L, int Value)// 배열 리스트의 제일 마지막에 데이터를 추가

void Erase(ListArray *L, int POSITION)//배열 리스트의 특정 위치의 데이터를 삭제 

void Array_Clear(ListArray *L)// 배열 리스트의 모든 데이터를 없앰

void Find_Value(ListArray *L, int VALUE)// 특정값을 배열 리스트에서 찾음

int Replace(ListArray *L, int POSITION, int VALUE)//배열 리스트의 특정 위치의 값을 다음 값으로 바꿈

void Return_length(ListArray *L)// 배열 리스트의 총 길이를 보여줌

void is_Full(ListArray *L)//배열 리스트가 가득 찾는지를 확인

void is_Empty(ListArray *L)//배열 리스트가 비엇는지를 확인

void Output(ListArray *L)//현재 배열 리스트에 있는 데이터를 순차적으로 모두 출력

cs

다른 기능을 구현해도 좋지만 제가 한 것들 하나씩 살펴보겠습니다.

    1. 배열 리스트의 가장 처음 데이터 추가

      void Add_First(ListArray *L, int Value)

              cout << "Can not add Value in List any more." << endl;

              L->Array[L->length] = Value;

      cs

      배열 리스트를 만들고 데이터를 처음 넣을 때 사용하는 ADT입니다. 사실 배열에서는 별로 의미는 없긴 한데 동적 할당을 통한 리스트 구현시에는 의미가 있기 떄문에 그래도 배열에서도 ADT를 만들 어 봤습니다. 이 ADT는 가득 찾는지 길이를 통해서 알아본 뒤 (사실 제일 처음 데이터를 넓는 것이기 때문에 필요없긴 합니다.) 그 다음 배열의 첫번지에 데이터를 넣어주고 길이를 1증가 시키는 ADT입니다.

    2. 배열 리스트의 특정 위치에 데이터를 추가

      void Add(ListArray *L, int Value, int POSITION)

          if (L->length >= 100)// 가득 찼는지 검사

              cout << "Can not add Value in List any more." << endl;

          for (int i = L->length; i > POSITION; i--)

              L->Array[i] = L->Array[i - 1];// 특정 위치부터 한칸씩 뒤로 밀어줌

          L->Array[POSITION] = Value;

      cs

      리스트 배열을 생성후 첫 데이터를 넣은 이후의 모든 데이터는 이것으로 넣을 수 있습니다. Add_First와는 다르게 위치도 지정해서 넣을 수 있습니다. 예외 처리 검사를 합니다. 3~7번째 줄에서는 ListArray 구조체의 length를 검사하여 배열이 가득 찼는지를 확인합니다. 가득 차지 않았다면 다음은 지정 위치를 검사합니다. 만약 배열 6까지 차있는데 18번쨰 위치를 지정하였다면 위치가 어찌됐던 7번째에 넣을 수 밖에 없기 떄문에(배열의 특성상) 마지막에 넣는 Add_Last ADT를 호출해줍니다. 모든 예외 검사를 통과 해 줬다면 for문을 이용하여 들어갈 위치뒤에 모든 데이터 들을 한칸씩 밀어주며 다 밀어준뒤 빈자리에 원하는 데이터를 넣어주는 ADT 입니다. 데이터를 추가했다면 마지막에 길이를 하나 더해줍니다.

    3. 배열 리스트의 가장 마지막에 데이터를 추가

      void Add_Last(ListArray *L, int Value)

              cout << "Can not add Value in List any more." << endl;

              L->Array[L->length] = Value;

      cs

      리스트 배열의 가장 마지막 (리스트의 가장 마지막에 있는 데이터 바로뒤)에 데이터를 넣어주는 ADT입니다. 이 ADT는 배열 전체를 움직이거나 할 필요가 없습니다. (배열 중간에가 비면 땡겨야 하지만 제일 마지막에 들어오는 것은 그냥 입력만 하면 되기 떄문) 그래도 살펴 보겠습니다. 간단하게 예외 처리를 해줍니다. 꽉 찼으면 더이상 넣을 수 없으므로 이를 확인해 주는 것입니다. 만약 꽉 차있지 않다면 마지막에 값을 대입하여 줍니다.

    4. 배열 리스트의 특정 위치의 데이터를 없앰

      void Erase(ListArray *L, int POSITION)

              printf("List is empty.\n");

              printf("there is no data\n");

          for (int i = POSITION - 1; i < L->length - 1; i++)

              L->Array[i] = L->Array[i + 1];

      cs

      리스트 배열에 있는 데이터들 중 특정한 위치의 데이터를 삭제하는 ADT입니다. 삭제시에는 추가와는 반대로 뒤로 밀어 주는 것이 아니라 삭제되는 데 까지 차례 차례 앞으로 당겨서 삭제 된 부분을 매꿔주어야 합니다. Erase ADT는 우선 예외 사항부터 검색하여 줍니다. length를 조사하여 비었는지, 값은 5번 배열 까지만 들어가있는데 혹시나 7번 값을 삭제하라고는 하지 않는지를 조사합니다. 조사가 끝난뒤에 삭제될 데이터는 앞으로 한칸씩 삭제위치 까지 당겨서 메꿔주고 전체길이를 하나 줄이는 ADT입니다.

    5. 배열 리스트의 데이터를 모두 없앰

      void Array_Clear(ListArray *L)

      cs

      사실 별거 없는 ADT입니다. 위에 것들을 보시면서 아셨는지는 모르겠지만 이 배열 리스트는 Length 라는 변수를 통해 제한되고 이동하고 하고 있습니다. 그렇기 떄문에 이것만 날려주면 배열에 데이터가 있던 없던 없는걸로 취급 됩니다. 뭐.. 마치 free를 안한 동적할당 메모리 같은 거라고 할까요?