看标题,写给无C++基础的读者
很多地方,我也知道该用lambda表达式,该用for_each,但是毕竟是写给无C++基础的,这时候谈什么匿名函数为时过早
作者本身是小白,很多表述也只是为了无C++基础的好理解,请大佬请勿喷
链表写起来麻烦(当然如果还不熟练建议多练习)
其实在C++的STL中有个容器叫list,本质上是一个双向链表
因此可以使用使用list完成链表的题目
不造轮子,省时省力啊!
因为是使用C++,要使用c++的编译器
选择 g++ 或者 clang++ 都可以
C++并不是什么高大上的东西
编译器选择C++,除了使用list以外,其他代码可以完全写成C语言风格
因此这里并不存在门槛问题
因为list包含在list头文件中
后面用到的find函数包含在algorithm头文件中
因此使用的时候应该在开头写上
#include
#include
// 至于这一个,每次直接这样写就好了
using namespace std;
迭代器这个名字听起来很高大上
不要被这个高大上的名字吓坏了
对于迭代器 目前理解成是一个指针就好了
就记住这句话 “迭代器先理解成指针”指针是可以进行++操作的
++操作的结果是,指针会移向目前保存的这个地址的下一个地址
比如一个int* 的指针
int ia[10] = {0};
// p指向ia的第一个元素 下标为0
int *p = ia;
// p被后移一位 指向ia的第二个元素 下标为1
++p;
迭代器也可以执行同样的操作
假设it是一个迭代器 指向某个元素
// it由目前指向的这个元素后移一位 指向了下一个元素
it++;
–操作同样类比
// list<链表元素的类型> 链表名称; // 超简单 一行搞定 list<int> l; list<char> cl; list<double> dl;
list有两个成员函数 对应链表操作中的"头插" 和 “尾插”
list.push_back() 往链表尾部插入元素
list.push_front() 往链表头部插入元素// int型的链表 list<int> l; // 往链表尾部添加元素123 l.push_back(123); // 往链表头部添加元素233 l.push_front(233);
再来讲迭代器
我们刚刚说到迭代器可以类比指针
假设现在一个链表有 0-9 共10个元素// 建立一个int链表 里面有0-9 共10个元素 list<int> l; for (int cnt = 0; cnt != 10; ++cnt) { // 在尾部插入 l.push_back(cnt); }
我们可以获取list的头迭代器和尾后迭代器
头迭代器 : 指向链表第一个元素,在上面那个链表中,即指向1
尾后迭代器 : 指向链表的最后一个元素的后一个元素,在上那个链表中,即指向10的后一个元素
特别要注意的:尾后迭代器没有实际意义 只用于判断下面是获得头迭代器和尾后迭代器的操作
// 这里的auto跟int char一样,是数据类型 // auto类型让编译器自动帮忙猜测这个变量的类型 // 由于迭代器的类型写起来非常麻烦 因此大多使用auto声明迭代器 auto ibegin = l.begin(); auto iend = l.end();
上面说了迭代器可以类比指针,也可以进行解引用操作获得值
// 链表 0 1 2 3 4 5 6 7 8 9 // ibegin指向链表的第一个元素0 printf("%d", *ibegin); // ibegin指向0 因此这里输出0
迭代器同样也可以像指针一样使用
*ibegin = 2; // 链表的第一个元素由1变成2
迭代器可以执行++操作
// 链表 0 1 2 3 4 5 6 7 8 9 // ibegin原来指向链表的第一个元素0 // 这时候ibegin后移一位 // 现在指向0的后一位 1 ++ibegin; printf("%d", *ibegin); // 输出1
用迭代器遍历链表
// it从指向链表的第一个元素开始 // 到指向链表的最后一个元素的后一个元素停止 // **还是要说明** .l.end()是尾后迭代器 // 链表 0 1 2 3 4 5 6 7 8 9 // l.end()指向了9的后一个元素 // 这里使用 != 就是让它遍历到9的后一个元素就停止 // 每次循环后,it就向后移动一个,指向后一个元素 for (auto it = l.begin(); it != l.end(); ++it) { printf("%d ", *it); } // 输出结果 0 1 2 3 4 5 6 7 8 9
链表中经常有访问元素next指针的操作
而list也可以轻松思想这个操作
假设还是刚刚那个链表 0 1 2 3 4 5 6 7 8 9
it是个迭代器,指向元素5// it2指向5的前一个元素4 auto it2 = prev(it); // it3指向5的后一个元素6 auto it3 = next(it); // 输出 // ====这里漏了*==== printf("%d %d", *it2, *it3); // 输出结果4 6
list除了用刚刚上述方法遍历查找以外
提供了好用的查找函数
还是刚刚那个链表 0 1 2 3 4 5 6 7 8 9
要查找 元素 5
当然可以使用下面的这个操作for (auto it = l.begin(); it != l.end(); ++it) { if(*it == 5) { printf("我找到5了"); } }
但是还有更简洁的查找方式
// find 查找函数 // l.begin() l.end()表示从l开头查找到结尾 // 后面的5就表示我要查找=5的元素 auto it = find(l.begin(), l.end(), 5); // **还是要说明** .l.end()是尾后迭代器 // 它指向9后一个元素 // 而9后一个元素其实不存在 // 所以如果查找到的迭代器等于end 那么就表示这个数组中并没有5 if(it != c.end()) { printf("我找到5了"); }
it是迭代器,可以通过it轻松修改元素的值
// 找到list中的5 并且修改成6 auto it = find(l.begin(), l.end(), 5); if(it != c.end()) { *it = 6; }
list提供了insert() 函数
通过这个函数可以很方便的进行插入
还是刚刚那个链表 0 1 2 3 4 5 6 7 8 9
我想要在6的前面插入10
我可以这么写:// 找到6的迭代器 auto it = find(l.begin(), l.end(), 6); // 在6这个元素的前面插入10 l.insert(it, 10);
说删操作前
先回顾一下qsort排序函数
qsort中有个参数传入一个函数
这个函数的返回值决定了当前这个元素排在前面还是后面
同样的,list提供的remove_if操作也需要传入一个函数
当这个函数返回true时,这个元素将被删除
当这个函数返回false时,这个元素不删除// 函数的参数n将被传入当前元素的值 bool to_remove(int n) { if (n == 6) { // 返回true 表示将被删除 return true; } else // 返回false 表示不会被删除 return false; }
应该不难理解,这个函数只有当元素值=6时,才会返回true
也就表面 要删除值为6的元素可以这么写:// to_remove 是 上面写的函数 l.remove_if(to_remove);
#include
#include
using namespace std;
bool to_remove(int n)
{
if (n == 1)
{
return true;
}
else
return false;
}
int main()
{
// 创建
list<int> l;
/* 增 */
// 链表最基本的操作 头插和尾插
for (int cnt = 0; cnt != 10; ++cnt)
{
l.push_back(cnt);
}
// 遍历输出链表
for (auto it = l.begin(); it != l.end(); ++it)
{
printf("%d ", *it);
}
// 查
auto it = find(l.begin(), l.end(), 5);
// 改
if (it != l.end())
{
*it = 6;
}
// 删
l.remove_if(to_remove);
// 插
l.insert(it, 15);
return 0;
}
很多东西其实讲的不是很准确
毕竟作者也是初学者
希望有所帮助吧 能用上确实快
如上操作掌握了,其他STL的容器如std::vector std::array也可以很轻松上手