STL 是C++的标准模板库(Standard Template Library),包含了常用数据结构的类实现(array,list,stack,queue,等等),一些常用算法的实现(查找,排序,关系,等等),以及迭代器,仿函数,等等。在项目开发过程中,使用标准模板库可以提高开发效率。
本篇来讲一下 STL 中的 list 的常用方法以及简单的模拟实现
STL list
链表 list,是一个模板类,在C++ std 命名空间内,使用时包含list头:
#include <list>
using namespace std;
指定一下放入链表中的数据类型,如 int,声明一个存放整数的链表 li:
list<int> li;
就表示链表 li 中存储的数据都是 int 的,其它数据类型也是如此:
// 存储 string 类型
list<string> ls;
list 的一些常用方法:
size() | 返回链表的当前大小 |
front() | 返回链表的第一个元素 |
back() | 返回链表的最后一个元素 |
empty() | 判断链表是否为空(空返回true,非空返回false) |
clear() | 清空链表 |
push_back(要插入的数据) | 往链表尾部插入元素(尾插法) |
push_front(要插入的数据) | 往链表头部插入数据(头插法) |
remove(要删除的数据) | 删除链表中的某元素,所有这些值的元素都会被删除 |
begin() | 返回链表中第一个元素的迭代器 |
end() | 返回链表中最后一个元素的迭代器 |
list 的遍历
使用迭代器来遍历:
list<int>::iterator iter;
li.begin() 是 li 的第一个元素的迭代器,li.end()是 li 的最后一个元素的迭代器,让 iter 从第一个迭代器开始一直到最后一个迭代器移动即可。怎么移动呢?迭代器重载了 ++ 运算符,使 iter 后移,并且重载了 * 运算符来获取当前迭代器的元素值。
使用下面方法来遍历链表:
list<int>::iterator iter;
for (iter = li.begin(); iter != li.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
可以看到这个很像指针,其实迭代器就是模仿了指针的行为。
也可以使用 C++11 中扩展的 for 循环来遍历:
for (auto v : li)
{
cout << v << " ";
}
cout << endl;
这种方法的本质也就是迭代器遍历,使用这种方法遍历的时候类中必须要有一个迭代器类!
简单测试一下 STL list
#include <list>
#include <iostream>
#include <string>
using namespace std;
void testIntList()
{
list<int> li;
for (int i = 0; i < 10; i++)
{
// 尾部添加数据
li.push_back(i);
}
// 打印一下链表当前大小
cout << li.size() << endl;
// 头部添加数据
li.push_front(1);
// 打印一下链表的首个元素和最后一个元素
cout << li.front() << " " << li.back() << endl;
// 迭代器遍历
list<int>::iterator iter;
for (iter = li.begin(); iter != li.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
// C++11 新的 for 循环遍历(本质是迭代器遍历)
for (auto v : li)
{
cout << v << " ";
}
cout << endl;
// 删除链表中的所有元素值为 1 的节点
li.remove(1);
// C++11 新的 for 循环遍历(本质是迭代器遍历)
for (auto v : li)
{
cout << v << " ";
}
cout << endl;
}
void testStringList()
{
list<string> ls;
ls.push_front(string("C"));
ls.push_front(string("B"));
ls.push_front(string("A"));
for (auto v : ls)
{
cout << v << " ";
}
cout << endl;
}
int main()
{
testIntList();
cout << endl;
testStringList();
return 0;
}
测试结果:
简单实现 MyList
MyList.hpp
#pragma once
#include <iostream>
// 节点类
template <class _T>
struct Node
{
_T data;
Node<_T>* next;
Node(_T data) :data(data), next(nullptr) {}
};
// 链表类
template <class _T>
class MyList
{
public:
// 构造
explicit MyList()
{
frontNode = nullptr;
backNode = nullptr;
curSize = 0;
}
// 析构
virtual ~MyList()
{
Node<_T>* moveNode = frontNode;
while (moveNode != nullptr)
{
Node<_T>* tmp = moveNode->next;
delete moveNode;
moveNode = tmp;
}
}
// 返回头节点数据
_T front() const { return frontNode->data; }
// 返回尾节点数据
_T back() const { return backNode->data; }
// 返回链表当前大小
int size() const { return curSize; }
// 判空
bool empty() const { return curSize == 0; }
// 重载 [] 运算符
_T operator[](int idx)
{
int i = 0;
iterator it;
for (i = 0, it = begin(); it != end(); i++, it++)
{
if (i == idx)
{
return *it;
}
}
}
// 头插法
void push_front(_T data)
{
Node<_T>* newNode = new Node<_T>(data);
newNode->next = frontNode;
if (curSize == 0)
{
backNode = newNode;
}
frontNode = newNode;
curSize++;
}
// 尾插法
void push_back(_T data)
{
Node<_T>* newNode = new Node<_T>(data);
if (curSize == 0)
{
frontNode = newNode;
}
else
{
backNode->next = newNode;
}
backNode = newNode;
curSize++;
}
// 重载 << 运算符
friend std::ostream& operator<<(std::ostream& out, const MyList& list)
{
Node<_T>* pMove = list.frontNode;
while (pMove != nullptr)
{
out << pMove->data << " ";
pMove = pMove->next;
}
return out;
}
public:
// 迭代器类
class iterator
{
public:
explicit iterator(Node<_T>* pNode) :pMove(pNode) {}
iterator() = default;
_T operator*() { return pMove->data; }
iterator operator++() { pMove = pMove->next; return (*this); }
iterator operator++(int) { pMove = pMove->next; return (*this); }
iterator operator+=(int add)
{
for (int i = 0; i < add; i++)
{
pMove = pMove->next;
}
return (*this);
}
bool operator!=(iterator obj) { return obj.pMove->next != this->pMove; }
protected:
Node<_T>* pMove;
};
iterator begin() const { return iterator(frontNode); }
iterator end() const { return iterator(backNode); }
protected:
Node<_T>* frontNode;
Node<_T>* backNode;
int curSize;
};
testMyList.cpp
#include "MyList.hpp"
#include <iostream>
using namespace std;
int main()
{
MyList<int> myList;
for (int i = 0; i < 10; i++)
myList.push_back(i);
// 这种循环就是迭代器遍历
for (auto i : myList)
cout << i;
cout << endl << "--------------------------------" << endl;
cout << myList << endl;
MyList<int>::iterator it;
for (it = myList.begin(); it != myList.end(); it += 2)
cout << *it << " ";
cout << endl << myList[2] << endl;
for (auto i : myList)
cout << i << " ";
cout << endl;
return 0;
}