您的位置:首页 > 编程语言 > C语言/C++

和我一起学C++之list<三>

2017-05-10 10:38 218 查看
接着上一章开始说。

我们知道对于一个数据结构来说,增删改查是最基本的操作。在上面的学习中,我们已经知道了怎样增改查了,所以下面我们来介绍下怎么删除一个节点

1、删除list中的节点

画图总是有助于我们分析的,所以直接上图看



与增加一个节点相反,我们只需要把待删除的节点的下一个节点的prev指针指向待删除节点的上一个节点,待删除节点的上一个节点的next指针指向待删除节点的下一个节点即可,然后再删除掉待删除节点本身。

list_iterator list::erase(list_iterator position)
{
if(position == end())
return end();
list_node* node_next = position.node_->next;//待删除节点的下一个节点
list_node* node_prev = position.node_->prev;//待删除节点的上一个节点
node_next->prev = node_prev;
node_prev->next = node_next;
delete position.node_;//删除待删除节点本身
position.node_ = nullptr;
return list_iterator(node_next);
}


2、删除list中的特定元素

利用我们之前的find函数和erase函数很容易就可以达到我们的目的

void list::remove(const int &value)
{
list_iterator it = find(begin(), end(),value);
while (it != end())//找到该元素位置
{
it = erase(it);//删除该元素所在节点,并返回其下一个节点位置
it = find(it, end(), value);//从删除节点下一个节点开始找
}
}


3、清空list中的元素

同样的,利用不断的erase第一个元素所在节点就可以达到我们的目的,如

void list::clear()
{
auto it = begin();
while(it != end())
{
it = erase(it);
}
}


但是这里还是有必要讲下对普通list(c-style)都适应的方法,包括单向和双向的链表。



通过上图我们知道,我们只需要先保存住第一个节点,然后将head哨兵的next指针指向第一个节点的下一个节点,再删除掉第一个节点即可。

void list::clear()
{
if (tail_ && head_)
{
while ( head_->next != tail_)
{
list_node *tmp = head_->next;//保存原第一个节点
head_->next = tmp->next;//新第一个节点
delete tmp;//删除原第一个节点
tmp = nullptr;
}
tail_->prev = head_;
head_->next = tail_;
}
}


4、pop_back 和 pop_front 操作

void list::pop_back()
{
list_iterator tmp = end();
erase(--tmp);
}

void list::pop_front()
{
erase(begin());
}


5、list中的排序问题

排序是笔试面试必考的内容之一,我们应该把每一种排序算法都烂熟于心。我们在这里这讲冒泡方法,当然我也会把STL中的算法给大家贴出来,供大家参考。

5.1、判断list是否有序

判读一个是不是无序的,只要判断是否存在一对相邻对是无序的即可。所以有

bool list::unsorted()
{
//如果空list或者只有1个元素,返回true
if (empty() || head_->next->next == tail_)
return true;
list_node* curr = head_->next;
while(curr != tail_->prev)
{
if(curr->value > curr->next->value)
return true;
curr = curr->next;
}
return false;
}


5.2 bubble 冒泡排序法

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。



从上面的分析可以得到如下代码

void list::sort()
{
// 如果为空list或者只有1个元素,直接退出
if (empty() || head_->next->next == tail_)
return;
list_node *node1 = head_->next;// 外层循环
while (node1 != tail_)
{
list_node* node2 = head_->next;//内层循环
while (node2 != tail_->prev)
{
if (node2->value > node2->next->value)
{
int tmp = node2->value;
node2->value = node2->next->value;
node2->next->value = tmp;
}
node2 = node2->next;
}
node1 = node1->next;
}
}


上面的算法还可以进行改进,比如对如部分有序或者整体有序的序列,是不是还要经过这么多步的操作呢?



这个感兴趣的读者可以自行去查阅相关资料或者自己推导,可能后面我也会写一些排序的文章(没办法,太重要了)。

6、小测试

#include "list.h"
#include <iostream>
#include <cstdlib>
int main()
{
std::cout << "-------------- list test -----------------" << std::endl << std::endl;
yang::list mylist;

for (int i = 0; i < 10; ++i)
{
if (i % 2 == 0)
mylist.push_back(i);
else
mylist.push_front(i);
}
auto it = mylist.begin();

std::cout << "elements are :   ";
for (; it != mylist.end(); ++it)
std::cout << *it << std::ends;
std::cout << std::endl << std::endl;

if (mylist.unsorted())
std::cout << "this is unsorted list !" << std::endl << std::endl;

std::cout << "--------- bubble sort ----------" << std::endl << std::endl;
mylist.sort();

std::cout << "elements are :   ";
for (it = mylist.begin(); it != mylist.end(); ++it)
std::cout << *it << std::ends;
std::cout << std::endl << std::endl;

it = ++mylist.begin();//迭代器指向第二个元素
it = mylist.erase(it); //删除第二个元素,value = 1
mylist.pop_front();//删除首个元素,value = 0
mylist.pop_back(); //删除最后一个元素,value = 9

std::cout << "------ size is : " << mylist.size() << std::endl << std::endl;

std::cout << "elements are :   ";
for (auto it1 = mylist.begin(); it1 != mylist.end(); ++it1)
std::cout << *it1 << std::ends;
std::cout << std::endl << std::endl;

*(++it) = 4; // 此时有两个元素值为4

std::cout << "elements are :   ";
for (it = mylist.begin(); it != mylist.end(); ++it)
std::cout << *it << std::ends;
std::cout << std::endl << std::endl;

mylist.remove(4);

std::cout << "after calling remove(4) the elements are :   ";
for (it = mylist.begin(); it != mylist.end(); ++it)
std::cout << *it << std::ends;
std::cout << std::endl << std::endl;

mylist.clear();

std::cout << "--------- after calling clear() the list size is : " << mylist.size() << std::endl<<std::endl;

system("pause");
return 0;
}


测试结果如下:



小结:本章主要讲了增删改查中删的相关操作和冒泡排序算法。在这章里,我们主要是通过图来分析思路,在实际过程中,借助画图和纸上计算是非常重要的辅助手段。在下一章里,将会讲到swap,merge,reverse等接口。

参考资料:

STL 源码剖析 侯捷著

数据结构(C++语言版)第三版 邓俊辉著

gcc 2.95 source code

https://zhuanlan.zhihu.com/p/24050357
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 list c++