기술나눔

[C초급] 리스트 클래스에 대한 사전 이해

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. 표준 라이브러리목록친절한

목록의 맨 아래 계층은 센티널 비트가 있는 양방향 순환 연결 목록 구조입니다.
Forward_list의 단일 연결 리스트 구조와 비교하여 list의 반복자는 양방향 반복자입니다.
벡터와 같이 순차적으로 구조화된 컨테이너에 비해 리스트는 임의 위치에서 삽입 및 삭제가 더 효율적이지만 임의 위치에서 임의 액세스를 지원하지 않습니다.
List는 템플릿 클래스입니다. 이를 사용할 때 요소의 유형을 제공해야 합니다.

목록 클래스를 사용할 때 헤더 파일을 포함해야 합니다.<list>

2. 리스트 클래스의 공통 인터페이스

1. 일반적으로 사용되는 생성자

1. 기본 생성자, 빈 연결 리스트를 생성합니다.

list();

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. }

2. 목록 객체를 구성하고 n개의 값으로 초기화합니다.

list(size_type n, const value_type& val =  value_type());

예를 들어:

  1. void test()
  2. {
  3. list<int> lt(3, 1);
  4. for (auto i : lt)
  5. {
  6. cout << i << " ";
  7. }
  8. }

작업 결과:

3.리스트 클래스의 생성자 복사

list(const list& x); 

예를 들어:

  1. void test()
  2. {
  3. list<int> lt(3, 1);
  4. list<int> lt1(lt);
  5. for (auto i : lt1)
  6. {
  7. cout << i << " ";
  8. }
  9. }

작업 결과:

4. 반복자의 초기화 생성자를 사용하십시오.

  1. Template<class InputIterator>
  2. list(InputIterator first, InputIterator last);

예를 들어:

  1. void test()
  2. {
  3. list<int> lt(3, 1);
  4. list<int> lt1(lt.begin(), lt.end());
  5. for (auto i : lt1)
  6. {
  7. cout << i << " ";
  8. }
  9. }

작업 결과:

2. 일반적으로 사용되는 용량 인터페이스

여전히 똑같습니다. 여기서 함께 이야기하겠습니다.

1.크기, 비어 있음, 최대 크기, 크기 조정

크기:반품연결리스트컨테이너의 요소 수입니다.

size_type size() const;

비어 있는:반품목록컨테이너가 비어 있는지 여부(예:크기0)입니다.
(참고: 이 함수는 어떤 방식으로든 컨테이너를 수정하지 않습니다.연결리스트용기의 내용물은 다음을 참조하세요.목록::지우기。)

bool empty() const;

최대 크기(일반적으로 사용되지 않음): 반환연결리스트컨테이너가 보유할 수 있는 최대 요소 수입니다.
(알려진 시스템 또는 라이브러리 구현 제한으로 인해 이는 컨테이너가 달성할 수 있는 최대 잠재력입니다.크기 하지만 컨테이너가 해당 크기에 도달한다는 보장은 없습니다. 해당 크기에 도달하기 전에는 언제든지 스토리지를 할당하지 못할 수도 있습니다. )

size_type max_size() const;

크기 조절:포함할 컨테이너 크기 조정N강요.
만약에N현재 컨테이너보다 작음크기, 콘텐츠가 이전 내용으로 축소됩니다.N요소를 초과하는 요소를 제거하고 파괴합니다.
만약에N현재 컨테이너보다 큼크기, 그런 다음 끝에 필요한 수의 요소를 삽입하여 콘텐츠가 확장됩니다.N 의 크기.지정된 경우이면 새 요소가 다음으로 초기화됩니다.복사본이 아니면 값이 초기화됩니다.
(이 함수는 컨테이너에 요소를 삽입하거나 삭제하여 컨테이너의 실제 내용을 변경한다는 점에 유의하세요.)

void resize (size_type n, value_type val = value_type());

예를 들어:

  1. void test()
  2. {
  3. list<int> lt(3, 1);
  4. /*list<int> lt1(lt);*/
  5. list<int> lt1(lt.begin(), lt.end());
  6. list<int> lt2;
  7. cout << lt1.size() <<endl;
  8. cout << lt1.empty() <<endl;
  9. cout << lt2.empty() <<endl;
  10. lt1.resize(10);
  11. cout << lt1.size() << endl;
  12. for (auto i : lt1)
  13. {
  14. cout << i << " ";
  15. }
  16. }

작업 결과:

3. 공통 접근 및 순회

1. 반복자

  1. iterator begin();
  2. const_iterator begin() const;
  3. iterator end();
  4. const_iterator end() const;

Iterator: 연결리스트의 첫 번째 노드와 마지막 노드의 위치를 ​​얻는 데 사용됩니다.다음 위치(예: 파수꾼 위치)

예를 들어:

  1. void test()
  2. {
  3. list<int> lt(5, 2);
  4. list<int> ::iterator it = lt.begin();
  5. while (it != lt.end())
  6. {
  7. cout << *it << " ";
  8. ++it;
  9. }
  10. }

작업 결과:

2. 역방향 반복자

역방향 반복자 rbegin();

const_reverse_iterator rbegin() const;

역방향 반복자 rend();

const_reverse_iterator rend() const;

역방향 반복자, rbegin은 컨테이너의 마지막 노드 위치를 가져오고, rend는 컨테이너의 센티널 비트 위치를 가져옵니다.

  1. void test()
  2. {
  3. list<int> lt = { 1,23,4,4,5,2 };
  4. list<int> ::reverse_iterator rit = lt.rbegin();
  5. while (rit != lt.rend())
  6. {
  7. cout << *rit << " ";
  8. ++rit;
  9. }
  10. }

작업 결과:

참고: 역방향 반복기 rit도 -- 대신 ++를 사용해야 합니다.

3.앞과 뒤

앞쪽:

참조 front();

const_reference 앞() const;

연결리스트의 첫 번째 노드에 저장된 데이터에 대한 참조를 반환합니다.

뒤쪽에:

참조 백();

const_reference 뒤로() const;

연결된 목록의 마지막 노드에 저장된 데이터에 대한 참조를 반환합니다.

예를 들어:

  1. void test()
  2. {
  3. list<int> lt = {213123,123,34524,213};
  4. cout << lt.front() << endl;
  5. cout << lt.back() << endl;
  6. }

작업 결과:

4.목록 추가, 삭제, 확인, 수정

1) push_front 및 pop_front

push_front를 헤드 삽입이라고 하며, 연결된 목록의 헤드에서 요소를 삽입합니다.

void push_front(const 값 유형& val);

pop_front는 헤드 삭제(head 삭제)라고 하며 연결 ​​목록의 헤드에서 요소를 삭제합니다.

void 팝_프론트();

void pop_front();

예를 들어:

  1. void test()
  2. {
  3. list<int> lt = {213123,123,34524,213};
  4. cout << lt.front() << endl;
  5. lt.push_front(21345);
  6. cout << lt.front() << endl;
  7. lt.pop_front();
  8. cout << lt.front() << endl;
  9. }

작업 결과:

2) push_back 및 pop_back

push_back은 꼬리 삽입(tail insert)이라고 하며, 연결된 목록의 끝에서 요소를 삽입합니다.

void push_back(const 값 유형& val);

pop_back은 연결 리스트의 끝에서 요소를 삭제하는 tail 삭제라고 합니다.

void 팝 백();

예를 들어:

  1. void test()
  2. {
  3. list<int> lt = {213123,123,34524,213};
  4. cout << lt.back() << endl;
  5. lt.push_back(21345);
  6. cout << lt.back() << endl;
  7. lt.pop_back();
  8. cout << lt.back() << endl;
  9. }

작업 결과:

3) 찾다

  1. template <class InputIterator, class T>
  2. InputIterator find(InputIterator first, InputIterator last, const T& val);

두 반복자의 범위에서 val을 찾아 그것이 위치한 반복자를 반환합니다.

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 3; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
  9. *pos = 114514;
  10. for (auto i : lt)
  11. {
  12. cout << i << " ";
  13. }
  14. }

작업 결과:

참고: 이 함수는 list의 멤버 함수가 아니라 표준 라이브러리의 함수입니다. 여러 컨테이너가 find 함수를 공유합니다.

보시다시피 목록의 반복자를 직접 역참조하고 해당 내용을 수정할 수 있지만 반복자가 노드를 가리켜서는 안 되나요? 역참조하면 데이터가 수정될 수 있는 이유는 무엇입니까?

실제로 목록의 반복자는 원래 생태학적 포인터를 사용하여 시뮬레이션 및 구현되지 않으며 하단에 캡슐화되어야 하며 이는 목록의 시뮬레이션 구현 소스 코드에 반영됩니다.

4) 삽입

반복자 삽입(반복자 위치, const 값_유형& val);

void insert(반복자 위치, 크기 유형 n, const 값 유형& val);

————————————————————————————————————————

주형<class InputIterator>

void insert(반복자 위치, InputIterator 처음, InputIterator 마지막);

위치 앞에 하나 이상의 요소 삽입

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 3; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
  9. lt.insert(pos, 8848);
  10. for (auto i : lt)
  11. {
  12. cout << i << " ";
  13. }
  14. }

작업 결과:

주의 깊은 학생들은 목록의 삽입 연산이 다음과 같다는 것을 발견했을 것입니다.반복자가 유효하지 않게 되지 않습니다., pos가 가리키는 노드는 그대로 유지되고 상대 위치도 변하지 않기 때문입니다.

그러나 목록의 삭제 작업으로 인해 반복자는 확실히 유효하지 않게 되며 삭제된 노드를 가리키는 반복자만 유효하지 않게 되고 다른 반복자는 영향을 받지 않습니다.

5) 지우다

  1. iterator erase(iterator position);
  2. iterator erase(iterator first, iterator last);

해당 위치의 요소를 삭제하거나 [첫 번째, 마지막) 범위의 모든 요소를 ​​삭제합니다.

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 3; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
  9. lt.erase(lt.begin());
  10. for (auto i : lt)
  11. {
  12. cout << i << " ";
  13. }
  14. }

작업 결과:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 3; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. list<int>::iterator pos = find(lt.begin(), lt.end(), 2);
  9. lt.erase(lt.begin(),pos);
  10. for (auto i : lt)
  11. {
  12. cout << i << " ";
  13. }
  14. }

작업 결과:

6) 교환

void swap(벡터& x);

두 개의 목록 개체를 교환합니다.

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. list<int> lt2(6, 12);
  5. for (int i = 0; i < 10; i++)
  6. {
  7. lt.push_back(i);
  8. }
  9. lt.swap(lt2);
  10. for (auto i : lt2)
  11. {
  12. cout << i << " ";
  13. }
  14. cout << endl;
  15. }

작업 결과:

7)할당하다

주형<class InputIterator>

void assign(입력 반복자를 먼저, 입력 반복자를 마지막에 할당함);

void assign(크기 유형 n, const 값 유형& val);

목록의 새 콘텐츠를 지정하고, 현재 콘텐츠를 바꾸고, 노드 수를 수정합니다.

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 10; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. lt.assign(4, 0);
  9. for (auto i : lt)
  10. {
  11. cout << i << " ";
  12. }
  13. }

작업 결과:

8)클리어

무효 지우기();

연결리스트의 모든 노드 삭제

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 10; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. lt.clear();
  9. cout << lt.size() << endl;
  10. }

작업 결과:

5.목록 순서 수정 인터페이스

(1)종류

void 정렬();

주형<class Compare>

void sort(comp 비교);

목록 구조의 특수성으로 인해 반복자가 뺄셈 연산을 수행할 수 없기 때문에 표준 라이브러리의 정렬 함수를 사용할 수 없습니다.

그리고 C++ 문서에서 표준 라이브러리의 정렬이 반복기 유형을 무작위 액세스(RandomAccess)로 제한하는 것을 볼 수 있습니다.

  1. default (1)
  2. template <class RandomAccessIterator>
  3. void sort (RandomAccessIterator first, RandomAccessIterator last);
  4. custom (2)
  5. template <class RandomAccessIterator, class Compare>
  6. void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

반복자에는 여러 가지 기능 범주가 있습니다.

  • 단방향 반복자는 ++만 수행할 수 있습니다.
  • 양방향 반복자는 ++ 및 --일 수 있습니다.
  • 무작위 반복자는 ++, --, + 및 -일 수 있습니다.

그러나 연결 목록을 직접 정렬하는 효율성은 벡터에 복사한 다음 정렬한 다음 연결 목록에 다시 복사하는 것보다 훨씬 느리기 때문에 목록의 정렬 기능이 필요하지 않습니다.

(2)뒤집다

template <class BidirectionalIterator>
  void reverse (BidirectionalIterator first, BidirectionalIterator last);

연결리스트를 뒤집는다

예를 들어:

  1. void test()
  2. {
  3. list<int> lt;
  4. for (int i = 0; i < 10; i++)
  5. {
  6. lt.push_back(i);
  7. }
  8. for (auto i : lt)
  9. {
  10. cout << i << " ";
  11. }
  12. cout << endl;
  13. lt.reverse();
  14. for (auto i : lt)
  15. {
  16. cout << i << " ";
  17. }
  18. cout << endl;
  19. }

작업 결과:


오류가 있으면 정정해 주세요.