您的位置:首页 > 理论基础 > 数据结构算法

数据结构:链表List的实现与代码分析

2017-01-08 19:16 405 查看
跟Vector一样,我自己也写了一个简单的List来进行研究。

这里实现的是双向链表,因为双向链表包含了单向链表的所以功能,所以就没有单独写一个了。

这个双向链表的实现,在创建的时候,就已经有了两个节点,分别是head和tail。这是抽离出来,不存放数据的节点,是为了让程序简化,被称为哨兵节点。

假如不使用哨兵节点,在表头表尾操作的插入删除的时候,有很多要特殊处理的地方。

加了着两个节点,就可以大大简化这些特殊操作。数据上的表头不再试实际代码的表头。

//
// List.h 双向链表的实现
// HelloWorld
// csdn blog:http://blog.csdn.net/u012175089
// Created by feiyin001 on 17/1/7.
// Copyright (c) 2017年 FableGame. All rights reserved.
//

#ifndef __HelloWorld__List__
#define __HelloWorld__List__

namespace Fable
{
//双向链表,使用模板
template<typename Object>
class List
{
private:
//节点,只在List内部使用,所有在私有区域内声明
struct Node
{
Object data;//链表内的有效数据
Node* prev;//指向前一个数据
Node* next;//指向下一个数据
Node(const Object& d = Object(), Node* p = nullptr, Node* n = nullptr): data(d),prev(p),next(n){}
};
public:
//const的迭代器
class const_iterator
{
public:
//默认构造函数
const_iterator():current(nullptr){}
//获得Object对象
const Object& operator*()const
{
return retrieve();
}
//自增,前缀自增
const_iterator& operator++()
{
current = current->next;
return *this;
}
//自增,后缀自增,这里的int只是作为一种标志来使用,并没有什么卵用
const_iterator operator++(int)
{
const_iterator old = *this;//原来的数据
++(*this);//调用前缀版本的自增
return old;//返回原来的数据
}
//自减,前缀自增
const_iterator& operator--()
{
current = current->prev;
return *this;
}
//自减,后缀自减,这里的int只是作为一种标志来使用,并没有什么卵用
const_iterator operator--(int)
{
const_iterator old = *this;//原来的数据
--(*this);//调用前缀版本的自增
return old;//返回原来的数据
}

//==重载
bool operator==(const const_iterator& rhs) const
{
return current == rhs.current;
}
//!=重载
bool operator!=(const const_iterator& rhs)const
{
return !(*this == rhs);
}
protected:
const List<Object> * theList;//把列表传了进来,作为检测的时候使用的
Node* current;//迭代器所指向的结点
//获得结点的数据,方便给子类使用
Object& retrieve()const
{
return current->data;
}
void assertIsValid()const//检测指针是否合法
{
if (!theList || !current || current == theList->head)//检测列表为空,当前指针为空,当前在头结点
{
throw ("IteratorOutOfBoundsException");//抛出异常,这里应该使用Exception相关类
}
}
//构造函数
const_iterator(const List<Object>& lst, Node* p):theList(&lst), current(p){}
//友元类,方便在List中访问
friend class List<Object>;

};
//迭代器
class iterator: public const_iterator
{
public:
iterator(){}//默认构造函数
//*重载
Object& operator*()
{
//return retrieve();无法通过编译
return this->retrieve();//也可以用域作用符
}
//*重载
const Object& operator*()const
{
return const_iterator::operator*();//调用父类函数
}
//自增,前缀
iterator operator++()
{
//current = current->next;无法通过编译
this->current = this->current->next;//也可以用域作用符
return *this;
}
//自增,后缀
iterator operator++(int)
{
iterator old = *this;
++(*this);
return old;
}
//自减,前缀
iterator operator--()
{
this->current = this->current->prev;//也可以用域作用符
return *this;
}
//自减,后缀
iterator operator--(int)
{
iterator old = *this;
--(*this);
return old;
}

//==重载
bool operator==(const iterator& rhs) const
{
return this->current == rhs.current;
}
//!=重载
bool operator!=(const iterator& rhs)const
{
return !(*this == rhs);
}

protected:
//构造函数,list内部调用的
iterator(const List<Object>& lst, Node* p) :const_iterator (lst, p){}
//友元类
friend class List<Object>;
};

public:
//默认构造函数
List()
{
init();//初始化
}
//复制构造函数
List(const List& rhs)
{
init();
*this = rhs;
}
//析构函数
~List()
{
clear();//清空链表
//释放掉两个指针的内存
delete head;
delete tail;
}
//复制赋值运算符
const List& operator=(const List& rhs)
{
//如果是相同的地址,就不用复制了。
if (this == &rhs)
{
return *this;
}
clear();//清空当前的链表
//按顺序一个一个复制进链表中
for (const_iterator iter = rhs.begin(); iter != rhs.end(); ++iter)
{
push_back(*iter);
}
return *this;
}
//获取第一个迭代器
iterator begin()
{
return iterator(*this, head->next);//head是不存放数据的
}
//获取第一个数据的迭代器
const_iterator begin() const
{
return const_iterator(*this, head->next);//head是不存放数据的
}
//获取最后一个迭代器
iterator end()
{
return iterator(*this, tail);//在STL里面,end返回的是超界地址,而不是最后一个数据的地址,这里是tail
}
const_iterator end()const
{
return const_iterator(tail);//在STL里面,end返回的是超界地址,而不是最后一个数据的地址,这里是tail
}
//获得链表的大小
int size()const
{
return theSize;
}
//是否为空链表
bool empty()const
{
return theSize == 0;
}
//清空整个列表
void clear()
{
while (!empty())
{
pop_front();//一个一个从表头开始删除。
}
}
//第一个数据的迭代器
Object& front()
{
return *begin();
}
//第一个数据的迭代器
const Object& front()const
{
return *begin();
}
//最后一个数据的迭代器。
Object& back()
{
return *(--end());//end()返回的是超界迭代器,所以要自减才能获得最后一个数据
}
//在表头插入数据
void push_front(const Object& x)
{
insert(begin(), x);//因为head和tail都是不放数据的,所以无论在哪里插入,都是指定表中的位置插入数据
}
//在表尾插入收据
void push_back(const Object& x)
{
insert(end(), x);
}
//弹出最开始的数据
void pop_front()
{
erase(begin());
}
//弹出最后的数据
void pop_back()
{
erase( --end() );
}
//在迭代器的位置插入数据
iterator insert(iterator iter, const Object& x)
{
iter.assertIsValid();//检测迭代器是否合法
if (iter.theList != this)//检测是否同一个链表的
{
throw ("IteratorMismatchException");
}
Node* p = iter.current;//当前的指针,会被压后一个
theSize++;//链表大小+1
//p的前一个指针是新的节点,p的原来的前一个指针的后一个指针指向新的节点。
return iterator(*this, p->prev = p->prev->next = new Node(x, p->prev, p));//构建新的指针
}
//erase函数,删除迭代器指向的节点,这个很重要,具体的实现,决定了for循环中删除数据的写法。
iterator erase(iterator iter)
{
Node* p = iter.current;
iterator retVal(*this, p->next);
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;//这个写法里面,P指向的节点已经被删除了,
theSize--;//减少一
return retVal;//返回的是下一个指针。
}
//删除区间内的节点
iterator erase(iterator start, iterator end)
{
for (iterator iter = start ; iter!= end; )
{
iter = erase(iter);//删除之后,返回的是下一个数据,所以这个for循环里面,是不需要iter++的。
}
}
private:
int theSize;
Node* head;
Node* tail;
//初始化,把构造函数里面的内容抽出来
void init()
{
//创建了两个节点,把节点指针互相指向,这是一个空链表
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;//指向结尾
tail->prev = head;
}
};
}
#endif /* defined(__HelloWorld__List__) */
写的过程中,发现还是有好多好多细节要实现的,其实非常繁琐。
估计还有很多很多地方是没有实现的,例如异常检测,还是少了点。

不过可以大致看到原理。

再写个简单的测试程序:

//
// List.cpp
// HelloWorld
// csdn blog:http://blog.csdn.net/u012175089
// Created by feiyin001 on 17/1/7.
// Copyright (c) 2017年 FableGame. All rights reserved.
//

#include "List.h"
#include <iostream>
using namespace Fable;
int main(int argc, char* argv[])
{
List<int> li;
for (int i = 0; i < 100; i++)
{
li.push_back(i);
}

for ( List<int>::iterator iter = li.begin(); iter != li.end(); )
{
if (*iter % 3 == 0)
{
iter = li.erase(iter);
}
else
{
std::cout << *iter << std::endl;
++iter;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ STL list 数据结构