IT 프로그래밍/자료구조

[자료구조] 그룹 액티비티 4

기술1 2024. 10. 23. 11:17

그룹액티비티 1번문제

Node *func(Node *head, int *j)
{
    Node* t1, * t2;
    *j = 0;
    t1 = head;
    if (t1 != nullptr) t2 = t1->next;
    else return head;

    *j = 1;
    if (t2 == nullptr)
        return head;

    while(t2 != nullptr)
    {
	    if(t1->data != t2->data)
	    {
            (*j)++; t1->next = t2; t1 = t2;
	    }
        t2 = t2->next;
    }
    t1->next = nullptr;
    return head;
}

1. 함수가 하는 일:

이 함수는 연결 리스트에서 중복된 값을 제거하는 역할을 합니다. 연결 리스트의 노드들이 오른차순으로 정렬되어 있다고 가정하고, 리스트를 순차적으로 탐색하면서 중복된 값을 찾아 제거합니다. 함수는 리스트에서 각 노드의 값을 비교하여 중복된 노드를 삭제하고, 중복이 없는 리스트를 반환합니다.

2. 매개변수 j의 역할:

매개변수 j는 중복된 노드를 제거하는 동안 몇 번의 중복이 발생했는지를 세는 역할을 합니다. 이 변수는 함수 내에서 중복된 노드가 발견될 때마다 ++ 연산을 통해 값이 증가합니다. 즉, 함수가 제거한 중복 노드의 수를 추적합니다.

3. S1과 S2가 실행되는 횟수:

  • S1 (if (t1->data != t2->data))는 t1과 t2의 데이터가 다른지 비교하는 조건문입니다. 리스트를 순차적으로 탐색하며 비교를 계속하기 때문에, 리스트의 모든 노드를 검사합니다. 리스트의 길이가 n일 때, 이 조건문은 최대 n-1번 실행될 수 있습니다.
  • S2 ((*j)++; t1->next = t2; t1 = t2;)는 중복된 노드가 발견되지 않을 때 실행됩니다. 이 문장은 t1과 t2의 데이터가 다를 때 실행되므로, 중복이 아닌 노드가 발견될 때마다 실행됩니다. S2는 중복된 값을 제거하는 것이므로 최대 n-1번까지 실행될 수 있습니다.

요약:

  1. 이 함수는 연결 리스트에서 중복된 값을 제거하는 역할을 합니다.
  2. 매개변수 j는 중복된 값이 발생한 횟수를 추적하는 데 사용됩니다.
  3. 리스트의 길이가 n >= 2일 때, S1과 S2가 실행되는 횟수는 리스트의 길이에 따라 각각 최대 n-1번입니다.

자료구조 2번

struct Node {
    int data;
    Node* next;
};

Node* merge(Node* first, Node* second) {
    // 두 리스트 중 하나가 비어 있으면 다른 리스트 반환
    if (first == NULL) return second;
    if (second == NULL) return first;

    // 병합된 리스트의 첫 번째 노드를 결정
    Node* result;
    if (first->data < second->data) {
        result = first;
        result->next = merge(first->next, second);  // 재귀적으로 병합
    } else {
        result = second;
        result->next = merge(first, second->next);  // 재귀적으로 병합
    }

    return result;
}

설명:

  • 구조체 Node: Node는 정수형 데이터를 저장하는 단순한 연결 리스트 노드입니다. 이 구조체는 data 필드와 다음 노드를 가리키는 next 포인터로 구성됩니다.
  • 기본 조건: 리스트가 비어 있는 경우 다른 리스트를 반환합니다. 이는 재귀적으로 호출될 때 어느 한쪽 리스트가 더 이상 노드를 가지고 있지 않을 때 종료 조건으로 작용합니다.
  • 첫 번째 노드 선택: 두 리스트의 첫 번째 노드 데이터를 비교하여 작은 값을 가진 노드를 result로 설정합니다. 그런 다음, 해당 노드의 next를 병합된 나머지 리스트의 결과로 설정합니다.
  • 재귀 호출: 작은 값을 가진 노드를 선택하고, 나머지 노드들은 재귀적으로 병합됩니다.

이 방식은 재귀적으로 연결 리스트를 병합하는 방식이며, 새로 노드를 생성하지 않고 기존의 노드들만을 이용하여 병합합니다.

 

자료구조 3번

void func(Node *head) {
    Node *p = head, *q = nullptr;
    while (p != nullptr) {
        if (p->data < 0) 
            q->next = p->next;  // q가 nullptr일 경우 문제가 발생할 수 있음
        else 
            q = p;  // q는 음수가 아닌 노드를 가리키게 함
        p = p->next;  // p는 다음 노드로 이동
    }
}

함수가 하려는 일:

이 함수는 연결 리스트에서 데이터를 가진 노드가 음수인 경우 해당 노드를 제거하고, 음수가 아닌 노드끼리 연결하는 일을 하려는 것 같습니다.

문제점:

  1. 초기값 문제: q가 처음에 nullptr로 초기화되어 있습니다. 리스트의 첫 번째 노드가 음수일 경우, q->next = p->next;에서 q가 아직 nullptr이기 때문에, nullptr에서 next를 참조하려고 하면 런타임 오류가 발생합니다.
  2. 첫 번째 노드가 음수일 때: 첫 번째 노드가 음수라면 q가 제대로 리스트의 연결을 담당하지 못하므로, 리스트의 시작 부분이 손상될 수 있습니다.

수정 방안:

  1. q가 nullptr일 때를 처리: q가 nullptr일 때, 즉 첫 번째 노드를 처리할 때 특별한 처리를 추가해줘야 합니다. q가 nullptr이면 head를 직접 수정하는 방식으로 문제를 해결할 수 있습니다.
  2. 첫 번째 노드가 음수일 때 헤드를 처리: 음수를 포함한 첫 번째 노드를 처리하는 특별한 논리가 필요합니다.
void func(Node *&head) {
    Node *p = head, *q = nullptr;

    while (p != nullptr) {
        if (p->data < 0) {
            if (q == nullptr) {
                // 첫 번째 노드가 음수인 경우 head를 갱신
                head = p->next;
            } else {
                // q가 가리키는 노드의 next를 갱신하여 음수 노드를 건너뜀
                q->next = p->next;
            }
        } else {
            q = p;  // q는 음수가 아닌 노드를 가리킴
        }
        p = p->next;  // p는 다음 노드로 이동
    }
}

수정 사항 설명:

  1. q가 nullptr일 때는 리스트의 첫 번째 노드를 처리하는 경우로, 헤드를 직접 갱신하여 음수 노드를 제거합니다.
  2. 나머지 경우에는 q가 p의 이전 노드를 가리키며, p가 음수이면 q->next를 조정하여 음수 노드를 건너뜁니다.

이렇게 수정하면 첫 번째 노드가 음수일 때도 안전하게 리스트에서 제거할 수 있으며, 나머지 음수 노드도 정상적으로 처리됩니다.

 

 

자료구조 4번

struct Node {
    int data;
    Node* next;
};

Node* deleteNodesInRange(Node* head, int lower, int upper) {
    // 먼저 head가 삭제 대상일 수 있으므로 head부터 처리
    while (head != nullptr && head->data >= lower && head->data <= upper) {
        Node* temp = head;
        head = head->next;  // head를 다음 노드로 이동
        delete temp;        // 현재 head 노드 삭제
    }

    // 현재 리스트가 비었거나 범위 밖이라면 종료
    if (head == nullptr) return nullptr;

    // 나머지 리스트 탐색
    Node* current = head;
    while (current->next != nullptr) {
        // 다음 노드가 삭제 범위에 들어가면 삭제
        if (current->next->data >= lower && current->next->data <= upper) {
            Node* temp = current->next;
            current->next = current->next->next;  // 노드 건너뜀
            delete temp;                          // 해당 노드 삭제
        } else {
            current = current->next;  // 다음 노드로 이동
        }
    }

    return head;
}

 

자료구조 5번

#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> first;  // 빈 리스트 생성
    list<int> second(4,100);  // 100이 4개 있는 리스트 생성: [100, 100, 100, 100]
    list<int> third(second.begin(), second.end());  // second의 모든 원소로 third 리스트 생성: [100, 100, 100, 100]
    list<int> fourth(third);  // third 리스트로 fourth 리스트 복사: [100, 100, 100, 100]

    int myints[] = {16, 2, 77, 29};  // 배열 선언
    list<int> fifth(myints, myints + sizeof(myints)/sizeof(int));  // 배열로 리스트 fifth 생성: [16, 2, 77, 29]

    for (auto it = fifth.begin(); it != fifth.end(); it++)  // fifth 리스트 출력
        cout << *it << ' ';
    cout << '\n';

    return 0;
}
  1. first 리스트: 빈 리스트입니다. 아무런 원소도 없습니다.
  2. second 리스트: 4개의 100으로 초기화된 리스트입니다.
  3. second = [100, 100, 100, 100]
  4. third 리스트: second 리스트의 모든 원소를 복사하여 만든 리스트입니다.
  5. third = [100, 100, 100, 100]
  6. fourth 리스트: third 리스트의 모든 원소를 복사하여 만든 리스트입니다.
     
    fourth = [100, 100, 100, 100]
  7. fifth 리스트: 배열 myints[]를 사용하여 리스트를 초기화합니다.
 

이 부분은 fifth 리스트의 모든 원소를 출력하는 코드입니다. fifth 리스트는 배열 myints[]로 초기화되었으므로, 출력 결과는 배열의 원소들인 16, 2, 77, 29가 순서대로 출력됩니다.

 

#include <iostream>
#include <list>
using namespace std;

int main () {
    list<int> first {1, 2, 3};  // 리스트 first 초기화: [1, 2, 3]
    list<int> second {10, 20, 30, 40, 50};  // 리스트 second 초기화: [10, 20, 30, 40, 50]

    second = first;  // second 리스트에 first 리스트 할당: second = [1, 2, 3]
    auto it = first.begin();  // first 리스트의 첫 번째 원소를 가리키는 iterator
    *it = 100;  // 첫 번째 원소 값을 100으로 변경: first = [100, 2, 3]

    // 첫 번째 for 루프: first 리스트 출력
    for (auto a : first)
        cout << a << ' ';  // 출력: 100 2 3
    cout << endl;

    // 두 번째 for 루프: second 리스트 출력 (현재 second는 first와 같음)
    for (auto b : second)
        cout << b << ' ';  // 출력: 1 2 3 (할당된 이후에 변경된 first의 영향을 받지 않음)
    cout << endl;

    // 세 번째 for 루프: second 리스트 원소들을 1씩 증가
    for (auto &c : second)
        c += 1;  // second = [2, 3, 4]

    // 네 번째 for 루프: 수정된 second 리스트 출력
    for (auto c : second)
        cout << c << ' ';  // 출력: 2 3 4
    cout << endl;

    return 0;
}

상세한 동작 과정:

  1. 리스트 first와 second 초기화:
    • first 리스트는 [1, 2, 3]으로 초기화됩니다.
    • second 리스트는 [10, 20, 30, 40, 50]으로 초기화됩니다.
  2. second = first:
    • second 리스트가 first 리스트와 동일하게 설정됩니다.
    • 이때, second는 [1, 2, 3]으로 바뀌고, 이전의 [10, 20, 30, 40, 50] 값은 없어집니다.
  3. *it = 100:
    • it는 first.begin()이므로, first 리스트의 첫 번째 원소를 가리킵니다.
    • 따라서 first의 첫 번째 값이 100으로 바뀝니다.
    • 이제 first는 [100, 2, 3]이 됩니다.
  4. 첫 번째 for 루프:
    • first 리스트의 모든 원소를 출력합니다.
    • 출력: 100 2 3
  5. 두 번째 for 루프:
    • second 리스트의 모든 원소를 출력합니다.
    • second는 first 리스트의 복사본이었지만, 그 후 first 리스트가 변경되었어도 second 리스트에는 영향을 주지 않습니다. second는 [1, 2, 3] 그대로입니다.
    • 출력: 1 2 3
  6. 세 번째 for 루프:
    • second 리스트의 각 원소에 1을 더합니다.
    • 이제 second는 [2, 3, 4]로 바뀝니다.
  7. 네 번째 for 루프:
    • 수정된 second 리스트의 모든 원소를 출력합니다.
    • 출력: 2 3 4
#include <iostream>
#include <list>
using namespace std;

int main () {
    list<int> first;  // 정수형 리스트 first 선언
    list<int> second;  // 정수형 리스트 second 선언

    first.assign(5, 100);  // first에 100을 5개 추가: [100, 100, 100, 100, 100]
    second.assign(first.begin(), first.end());  // second에 first 리스트 복사: [100, 100, 100, 100, 100]

    int myints[] = {17, 7, 4};  // 배열 myints 선언
    first.assign(myints, myints + 3);  // first 리스트에 myints의 값으로 할당: [17, 7, 4]

    // 첫 번째 for 루프: first 리스트 출력
    for (auto a : first)
        cout << a << ' ';  // 출력: 17 7 4
    cout << endl;

    // 두 번째 for 루프: second 리스트 출력
    for (auto b : second)
        cout << b << ' ';  // 출력: 100 100 100 100 100
    cout << endl;

    return 0;
}

리스트 할당 과정:

  1. first.assign(5, 100):
    • first 리스트에 100이 5개 추가됩니다.
    • 결과적으로 first = [100, 100, 100, 100, 100]
  2. second.assign(first.begin(), first.end()):
    • second 리스트에 first 리스트의 모든 요소를 복사합니다.
    • 결과적으로 second = [100, 100, 100, 100, 100]
  3. first.assign(myints, myints + 3):
    • 배열 myints[]의 값을 first 리스트에 복사합니다.
    • 배열의 크기는 3이므로 first = [17, 7, 4]

출력 과정:

  1. 첫 번째 for 루프:
    • first 리스트의 모든 원소를 출력합니다.
    • first는 [17, 7, 4]이므로, 출력 결과는 17 7 4입니다.
  2. 두 번째 for 루프:
    • second 리스트의 모든 원소를 출력합니다.
    • second는 [100, 100, 100, 100, 100]이므로, 출력 결과는 100 100 100 100 100입니다.
17 7 4 
100 100 100 100 100

 

 

#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> mylist(2, 100);  // 100을 두 개 저장한 리스트: [100, 100]
    mylist.push_front(200);    // 리스트 맨 앞에 200 추가: [200, 100, 100]
    mylist.push_front(300);    // 리스트 맨 앞에 300 추가: [300, 200, 100, 100]
    
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        cout << *it << ' ';  // 리스트의 모든 원소를 출력
    cout << '\n';

    return 0;
}

실행 결과:

  • 초기 리스트는 [100, 100]입니다.
  • push_front(200)을 호출하면 리스트 맨 앞에 200이 추가되어 [200, 100, 100]이 됩니다.
  • 다시 push_front(300)을 호출하면 리스트 맨 앞에 300이 추가되어 [300, 200, 100, 100]이 됩니다.
  • 리스트의 모든 원소를 출력하므로 결과는 다음과 같습니다:


#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> mylist;
    mylist.push_back(100);  // 리스트 맨 뒤에 100 추가: [100]
    mylist.push_back(200);  // 리스트 맨 뒤에 200 추가: [100, 200]
    mylist.push_back(300);  // 리스트 맨 뒤에 300 추가: [100, 200, 300]
    
    while (!mylist.empty()) {
        cout << ' ' << mylist.front();  // 리스트의 첫 번째 원소 출력
        mylist.pop_front();  // 첫 번째 원소 삭제
    }

    return 0;
}

실행 결과:

  • 초기 리스트는 빈 리스트입니다.
  • push_back(100), push_back(200), push_back(300)을 통해 리스트는 [100, 200, 300]이 됩니다.
  • while 루프에서는 mylist.front()로 첫 번째 원소를 출력한 뒤, pop_front()로 첫 번째 원소를 제거합니다.

 

#include <iostream>
#include <list>
#include <vector>
using namespace std;

int main() {
    list<int> mylist;
    for (int i = 1; i <= 5; ++i)
        mylist.push_back(i);  // 1부터 5까지 저장한 리스트: [1, 2, 3, 4, 5]
    
    list<int>::iterator it = mylist.begin();
    ++it;  // it은 리스트의 두 번째 원소를 가리킴 (2)
    
    mylist.insert(it, 10);  // 두 번째 원소 앞에 10 삽입: [1, 10, 2, 3, 4, 5]
    mylist.insert(it, 2, 20);  // 두 번째 원소 앞에 20을 두 번 삽입: [1, 20, 20, 10, 2, 3, 4, 5]
    
    vector<int> myvector(2, 30);  // 벡터 생성: [30, 30]
    mylist.insert(it, myvector.begin(), myvector.end());  // 두 번째 원소 앞에 벡터 삽입: [1, 30, 30, 20, 20, 10, 2, 3, 4, 5]
    
    for (it = mylist.begin(); it != mylist.end(); ++it)
        cout << *it << ' ';  // 리스트의 모든 원소를 출력
    cout << '\n';

    return 0;
}

실행 결과:

  • 초기 리스트는 [1, 2, 3, 4, 5]입니다.
  • ++it로 it는 두 번째 원소인 2를 가리킵니다.
  • insert(it, 10)로 2 앞에 10을 삽입하므로 리스트는 [1, 10, 2, 3, 4, 5]가 됩니다.
  • insert(it, 2, 20)으로 2 앞에 20을 두 번 삽입하므로 리스트는 [1, 20, 20, 10, 2, 3, 4, 5]가 됩니다.
  • 벡터 [30, 30]을 두 번째 원소 앞에 삽입하므로 최종 리스트는 [1, 30, 30, 20, 20, 10, 2, 3, 4, 5]가 됩니다.

 

#include <iostream>
#include <list>

int main() {
    std::list<int> mylist1, mylist2;
    std::list<int>::iterator it;

    // mylist1에 1, 2, 3, 4 추가
    for (int i = 1; i <= 4; ++i)
        mylist1.push_back(i);  // mylist1 = [1, 2, 3, 4]

    // mylist2에 10, 20, 30 추가
    for (int i = 1; i <= 3; ++i)
        mylist2.push_back(i * 10);  // mylist2 = [10, 20, 30]

    it = mylist1.begin();
    ++it;  // it은 mylist1의 두 번째 원소(2)를 가리킴

    // mylist2의 모든 원소를 mylist1의 두 번째 위치에 삽입
    mylist1.splice(it, mylist2);  // mylist1 = [1, 10, 20, 30, 2, 3, 4], mylist2는 빈 리스트가 됨

    // mylist2의 비어있는 리스트에 mylist1의 처음부터 두 번째 원소 앞까지를 다시 삽입
    mylist2.splice(mylist2.begin(), mylist1, mylist1.begin(), it);  // mylist2 = [1], mylist1 = [10, 20, 30, 2, 3, 4]

    it = mylist1.begin();
    std::advance(it, 3);  // it을 세 번째 위치로 이동 (mylist1의 30을 가리킴)

    // mylist1의 처음부터 세 번째 원소 앞까지를 mylist1의 끝으로 이동
    mylist1.splice(mylist1.begin(), mylist1, it, mylist1.end());  // mylist1 = [30, 2, 3, 4, 10, 20]

    // mylist1의 모든 원소 출력
    for (it = mylist1.begin(); it != mylist1.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    // mylist2의 모든 원소 출력
    for (it = mylist2.begin(); it != mylist2.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}

실행 과정 및 결과:

  1. mylist1 초기값: [1, 2, 3, 4]
  2. mylist2 초기값: [10, 20, 30]
  3. mylist2의 원소들이 mylist1의 두 번째 위치에 삽입됩니다. mylist1 = [1, 10, 20, 30, 2, 3, 4]가 되고, mylist2는 비어 있습니다.
  4. mylist1의 첫 번째 원소(1)를 mylist2로 옮기고, mylist2 = [1]가 됩니다. mylist1 = [10, 20, 30, 2, 3, 4]가 됩니다.
  5. mylist1의 세 번째 원소(30)부터 끝까지 처음으로 이동시키면, mylist1 = [30, 2, 3, 4, 10, 20]가 됩니다.
  6. mylist1의 모든 원소를 출력하면: 30 2 3 4 10 20
  7. mylist2의 모든 원소를 출력하면: 1
#include <iostream>
#include <list>
using namespace std;

int main() {
    int myints[] = {17, 89, 7, 14};  // 배열 초기화
    list<int> mylist (myints, myints + 4);  // 배열의 값으로 리스트 생성: [17, 89, 7, 14]

    mylist.remove(89);  // 리스트에서 89 삭제: [17, 7, 14]

    // 리스트의 모든 원소 출력
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        cout << ' ' << *it;
    cout << '\n';

    return 0;
}

실행 과정 및 결과:

  1. mylist는 [17, 89, 7, 14]로 초기화됩니다.
  2. mylist.remove(89)는 리스트에서 89를 삭제합니다. 결과적으로 mylist = [17, 7, 14]가 됩니다.
  3. 리스트의 모든 원소를 출력하면: 17 7 14

 

#include <iostream>
#include <list>

bool single_digit(const int& value) {
    return (value < 10);
}

bool is_odd(const int& value) {
    return (value % 2) == 1;
}

int main() {
    int myints[] = {15, 36, 7, 17, 20, 39, 4, 1};
    std::list<int> mylist(myints, myints + 8);  // 배열의 값으로 리스트 생성: [15, 36, 7, 17, 20, 39, 4, 1]

    mylist.remove_if(single_digit);  // 한 자리 숫자 제거: [15, 36, 17, 20, 39]
    mylist.remove_if(is_odd);        // 홀수 제거: [36, 20]

    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}

실행 과정 및 결과:

  1. myints[] 배열을 기반으로 리스트 생성: mylist = [15, 36, 7, 17, 20, 39, 4, 1]
  2. mylist.remove_if(single_digit): 한 자리 숫자를 삭제하므로, mylist = [15, 36, 17, 20, 39]가 됩니다.
  3. mylist.remove_if(is_odd): 홀수를 삭제하므로, 최종적으로 mylist = [36, 20]가 됩니다.
  4. 리스트의 모든 원소를 출력하면 36 20이 출력됩니다.
#include <iostream>
#include <cmath>
#include <list>

bool same_integral_part(double first, double second) {
    return int(first) == int(second);
}

struct is_near {
    bool operator() (double first, double second) {
        return fabs(first - second) < 5.0;
    }
};

int main() {
    double mydoubles[] = {12.15, 2.72, 73.0, 12.77, 3.14, 12.77, 73.35, 72.25, 15.3, 72.25};
    std::list<double> mylist(mydoubles, mydoubles + 10);

    mylist.sort();                              // 리스트를 정렬
    mylist.unique();                            // 중복 제거: 동일한 값을 연달아 나타나는 중복만 제거
    mylist.unique(same_integral_part);          // 정수 부분이 같은 값 제거
    mylist.unique(is_near());                   // 두 값의 차가 5.0 미만인 경우 제거

    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}

실행 과정 및 결과:

  1. mydoubles[] 배열을 기반으로 리스트 생성: mylist = [12.15, 2.72, 73.0, 12.77, 3.14, 12.77, 73.35, 72.25, 15.3, 72.25]
  2. mylist.sort(): 리스트를 오름차순으로 정렬하면, mylist = [2.72, 3.14, 12.15, 12.77, 12.77, 15.3, 72.25, 72.25, 73.0, 73.35]
  3. mylist.unique(): 연속된 중복 값 제거. 12.77, 72.25가 중복되므로 삭제되고, mylist = [2.72, 3.14, 12.15, 12.77, 15.3, 72.25, 73.0, 73.35]
  4. mylist.unique(same_integral_part): 정수 부분이 같은 값 제거. 즉, 12.xx 값 중 하나만 남기고 삭제, 72.xx, 73.xx 값들도 하나만 남김. 결과는 mylist = [2.72, 3.14, 12.15, 72.25]
  5. mylist.unique(is_near()): 값들의 차이가 5 미만인 경우 제거. 여기서는 제거 대상 없음.
  6. 리스트의 모든 원소를 출력하면 2.72 3.14 12.15 72.25가 출력됩니다.

 

#include <iostream>
#include <list>
#include <string>
#include <cctype>  // tolower 함수 사용을 위해 추가
using namespace std;

// 대소문자 구분 없이 문자열을 비교하는 함수
bool compare_nocase(const string &first, const string &second) {
    unsigned int i = 0;
    // 두 문자열의 각 문자를 소문자로 변환하여 비교
    while (i < first.length() && i < second.length()) {
        if (tolower(first[i]) < tolower(second[i]))
            return true;
        else if (tolower(first[i]) > tolower(second[i]))
            return false;
        ++i;
    }
    // 길이가 짧은 문자열을 우선으로 비교
    return first.length() < second.length();
}

int main() {
    list<string> mylist;
    list<string>::iterator it;

    // 리스트에 문자열 추가
    mylist.push_back("one");
    mylist.push_back("two");
    mylist.push_back("Three");

    // 기본 정렬 (대소문자를 구분하여 정렬)
    mylist.sort();
    for (it = mylist.begin(); it != mylist.end(); ++it)
        cout << ' ' << *it;
    cout << '\n';

    // 대소문자 구분 없이 정렬
    mylist.sort(compare_nocase);
    for (it = mylist.begin(); it != mylist.end(); ++it)
        cout << ' ' << *it;
    cout << '\n';

    return 0;
}

실행 과정:

  1. 리스트에 문자열 추가:
    • mylist 리스트에 "one", "two", "Three"가 차례대로 추가됩니다.
    • 리스트의 초기 상태는: ["one", "two", "Three"]
  2. 기본 정렬 (mylist.sort()):
    • 기본 정렬은 대소문자를 구분하여 이루어집니다. 대문자가 소문자보다 ASCII 값이 작으므로, "Three"가 가장 앞에 오고, 그 다음 "one", "two"가 옵니다.
    • 정렬 결과: ["Three", "one", "two"]
  3. 대소문자 구분 없이 정렬 (mylist.sort(compare_nocase)):
    • compare_nocase 함수는 대소문자 구분 없이 문자열을 비교합니다. 따라서 "one", "Three", "two"가 대소문자 상관없이 사전 순으로 정렬됩니다.
    • 정렬 결과: ["one", "Three", "two"]

 

 

함수설명

1. std::list::sort

void sort();
template <class Compare> void sort(Compare comp);

설명:

  • 기본 정렬: sort()는 대소문자 구분을 포함하여 리스트의 원소들을 기본적으로 오름차순으로 정렬합니다. 즉, 원소들의 데이터 타입이 기본적으로 제공하는 < 연산자를 사용합니다.
  • 사용자 정의 비교 함수 정렬: sort(Compare comp)는 사용자가 정의한 비교 함수 comp에 따라 리스트를 정렬합니다. 비교 함수는 두 개의 인자를 받아야 하며, 첫 번째 인자가 두 번째 인자보다 작을 때 true를 반환해야 합니다.

예시:

mylist.sort();  // 기본 정렬
mylist.sort(compare_nocase);  // 사용자 정의 함수인 compare_nocase를 사용하여 정렬

2. std::list::remove_if

template <class Predicate> void remove_if(Predicate pred);

설명:

리스트 내의 원소 중에서 주어진 조건(pred)을 만족하는 원소들을 제거합니다. pred는 논리적 참(true) 또는 거짓(false)을 반환하는 함수 포인터 또는 함수 객체여야 합니다.

예시:

bool single_digit(const int& value) {
    return (value < 10);  // 10 미만의 숫자를 찾음
}

mylist.remove_if(single_digit);  // 10 미만의 숫자를 모두 제거

3. std::list::splice

void splice(iterator position, list& x);
void splice(iterator position, list& x, iterator i);
void splice(iterator position, list& x, iterator first, iterator last);

설명:

  • 리스트 x의 모든 원소 또는 특정 범위의 원소를 현재 리스트(this)의 position 위치에 삽입합니다.
  • 첫 번째 형태: x의 모든 원소를 position 위치에 삽입합니다.
  • 두 번째 형태: x의 i 위치에 있는 단일 원소를 position에 삽입합니다.
  • 세 번째 형태: x의 first부터 last까지의 원소를 position에 삽입합니다.

예시:

mylist1.splice(it, mylist2);  // mylist2의 모든 원소를 mylist1의 it 위치에 삽입
mylist1.splice(it, mylist2, mylist2.begin(), mylist2.end());  // mylist2의 범위 지정

4. std::list::remove

void remove(const T& value);

설명:

리스트에서 주어진 값 value를 가진 모든 원소를 제거합니다.

예시:

mylist.remove(89);  // 리스트에서 89를 가진 모든 원소 제거

5. std::list::unique

void unique();
template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);

설명:

  • 기본 중복 제거: 리스트 내에서 연속된 중복 원소를 제거합니다. 연속된 값들 중 하나만 남깁니다.
  • 사용자 정의 조건 중복 제거: unique(BinaryPredicate binary_pred)는 사용자가 정의한 조건에 따라 연속된 중복 원소를 제거합니다.

예시:

mylist.unique();  // 연속된 중복 원소 제거
mylist.unique(same_integral_part);  // 정수 부분이 동일한 원소 제거

6. std::advance

void advance(InputIterator& it, Distance n);

설명:

입력된 반복자 it을 n만큼 전진시킵니다. 즉, 반복자를 n번 만큼 이동시킵니다.

예시:

std::list<int>::iterator it = mylist.begin();
std::advance(it, 3);  // 반복자를 3칸 이동

7. std::fabs

double fabs(double x);

설명:

절대값을 반환합니다. 주어진 실수 값 x의 절대값을 반환하는 함수입니다.

예시:

fabs(5.0 - 3.0);  // 결과는 2.0

8. std::tolower

int tolower(int ch);

설명:

주어진 문자 ch를 소문자로 변환합니다. 문자가 대문자인 경우 소문자로 변환하고, 그렇지 않은 경우 그대로 반환합니다.

예시:

tolower('A');  // 'a' 반환
tolower('b');  // 'b' 그대로 반환

 


9. 사용자 정의 함수들

1. compare_nocase

bool compare_nocase(const string& first, const string& second) {
    unsigned int i = 0;
    while (i < first.length() && i < second.length()) {
        if (tolower(first[i]) < tolower(second[i]))
            return true;
        else if (tolower(first[i]) > tolower(second[i]))
            return false;
        ++i;
    }
    return first.length() < second.length();
}
  • 두 문자열을 대소문자 구분 없이 비교합니다. tolower()를 사용하여 두 문자열의 각 문자를 소문자로 변환하여 비교하고, 길이가 짧은 문자열이 우선시됩니다.

2. single_digit

bool single_digit(const int& value) {
    return (value < 10);
}
  • 주어진 값이 한 자리 숫자인지를 확인합니다. 값이 10 미만인 경우 true를 반환하고, 그렇지 않은 경우 false를 반환합니다.

3. is_odd

bool is_odd(const int& value) {
    return (value % 2) == 1;
}
  • 주어진 값이 홀수인지를 확인합니다. 값이 홀수인 경우 true, 그렇지 않은 경우 false를 반환합니다.

4. same_integral_part

bool same_integral_part(double first, double second) {
    return int(first) == int(second);
}
  • 두 실수 값의 정수 부분이 같은지 확인합니다. 두 값의 정수 부분이 같으면 true를 반환하고, 그렇지 않으면 false를 반환합니다.

5. is_near

struct is_near {
    bool operator() (double first, double second) {
        return fabs(first - second) < 5.0;
    }
};
  • 두 실수 값이 5.0 미만의 차이를 가지는지를 확인합니다. 두 값의 차이가 5 미만이면 true, 그렇지 않으면 false를 반환하는 함수 객체입니다.