这个双链表,是我模仿stl的list制作的,只实现了一些基本功能,像merge,transfer这些就没有实现,用户可以用基本操作来自己做外部实现。

我没有选用stl的[begin,end)迭代器模式,而是使用传统的[head,tail]。不过,为了配合stl算法,我还是加了两个begin(),end()方法,模拟了一下stl容器。模拟的还算及格,至少我可以做类似for (; begin != end; ++begin)这样的事,也可以让我的容器搭配一些stl算法,这在之后的demo里可以看到。不过,我的iterator很朴素,所以不能用于std::sort。

至于模拟的实现,我的方法是让所有越界的迭代器都指向一个预先设定好的内存地址。然后用户调用end()方法的时候,就返回指向这个内存地址的迭代器。这样,当我的迭代器越界的时候,就和end()指向的内存地址一样了。

代码采用C++11实现,在Win7 Mingw32 Gcc4.7的环境下,demo运行正常。如果发现写的不对的地方,还请回复一下。

以下是类的实现:

 #pragma once

 #include <cstddef>
#include <stdexcept> namespace jt { template <class value_t>
class list {
friend class list<value_t>; private:
struct node {
value_t val;
node *next, *prev;
} *_head = nullptr,
*_tail = nullptr; size_t _siz; enum { endpointer = }; public:
class iterator {
friend class iterator;
friend class list<value_t>; public:
iterator(node *nn = nullptr) { _init(nn); }
iterator(const iterator &i) { _init(i.n); } value_t& operator*() const { return n->val; }
value_t* operator->() const { return &(operator*()); }
iterator operator++() { _inc(); return *this; }
iterator operator++(int) { iterator tmp = *this; _inc(); return tmp; }
iterator operator--() { _dec(); return *this; }
iterator operator--(int) { iterator tmp = *this; _dec(); return tmp; }
bool operator==(iterator& i) const { return i.n == n; }
bool operator!=(iterator& i) const { return i.n != n; } private:
node *n; void _init(node *nn) {
if (nn) {
n = nn;
} else {
n = (node*)endpointer;
}
} void _inc() { n = (n->next) ? n->next : (node*)endpointer; } // increment
void _dec() { n = (n->prev) ? n->prev : (node*)endpointer; } // decrement
}; list() { clear(); }
list(const list<value_t> &l) { if (&l != this) _init(l.begin(), l.end()); }
~list() { clear(); } bool empty() const { return !_head; } void clear() {
if (!empty()) {
node *tmp;
for (node *n = _head; n; n = tmp) {
tmp = n->next;
delete n;
} _head = nullptr;
_tail = nullptr;
_siz = ;
}
} void insert(const iterator &pos, const value_t &val) {
if (pos.n == _head) {
push_front(val);
} else {
node *n = new node;
n->val = val;
n->next = pos.n;
n->prev = pos.n->prev; pos.n->prev->next = pos.n->prev = n;
++_siz;
}
} void erase(const iterator &pos) {
if (pos.n == _head) {
pop_front();
} else if (pos.n == _tail) {
pop_back();
} else {
pos.n->prev->next = pos.n->next;
pos.n->next->prev = pos.n->prev;
delete pos.n;
--_siz;
}
} iterator head() const { return iterator(_head); }
iterator tail() const { return iterator(_tail); } iterator begin() const { return iterator(_head); }
iterator end() const { return iterator((node*)endpointer); } void push_front(const value_t &val) {
node *n = new node;
n->val = val;
n->next = _head;
n->prev = nullptr; if (empty()) _tail = n;
else _head->prev = n;
_head = n; ++_siz;
} void push_back(const value_t &val) {
node *n = new node;
n->val = val;
n->next = nullptr;
n->prev = _tail; if (empty()) _head = n;
else _tail->next = n;
_tail = n; ++_siz;
} void pop_front() {
if (_siz == ) {
throw std::logic_error("Empty List");
} else if (_siz == ) {
clear();
} else {
node *tmp = _head->next;
delete _head;
_head = tmp;
_head->prev = nullptr;
} --_siz;
} void pop_back() {
if (_siz == ) {
throw std::logic_error("Empty List");
} else if (_siz == ) {
clear();
} else {
node *tmp = _tail->prev;
delete _tail;
_tail = tmp;
_tail->next = nullptr;
} --_siz;
} value_t front() const { return *head(); }
value_t back() const { return *tail(); } size_t size() const { return _siz; } private:
template <class iter>
void _init(iter head, iter tail) {
clear(); // copy
if (head && tail) {
for (; head != tail; ++head) {
push_back(*head);
}
push_back(*tail);
}
}
}; }

我没有做测试,而是直接做了一个demo。

这个demo是一个正整数的统计计算器。可以插入,删除,求和,求平均数,等等。

以下是demo的代码,同样是C++11:

 #include "list.h"
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <numeric>
#include <algorithm>
#include <map>
#include <functional> int main() {
jt::list<unsigned> cont;
std::string buf;
std::string helpmsg =
"h for HELP\n"
"s for SUM\n"
"a for AVERAGE\n"
"z for SIZE\n"
"i,n,p for INSERT n AT p\n"
"e,n for ERASE AT p\n"
"p,n for PUSH_BACK n\n"
"q for QUIT"; int n = -;
int p = -; // create func
std::map<char, std::function<void()>> func;
func['h'] = [&]() { std::cout << helpmsg << std::endl; };
func['s'] = [&]() { std::cout << "sum: " << std::accumulate(cont.begin(), cont.end(), ) << std::endl; };
func['a'] = [&]() { std::cout << "average: " << double(std::accumulate(cont.begin(), cont.end(), )) / cont.size() << std::endl; };
func['z'] = [&]() { std::cout << "size: " << cont.size() << std::endl; };
func['i'] = [&]() {
auto i = cont.begin();
while (--p) ++i;
cont.insert(i, n);
};
func['e'] = [&]() {
auto i = cont.begin();
while (--n) { ++i; }
cont.erase(i);
};
func['p'] = [&]() { cont.push_back(n); };
func['q'] = [](){}; char *optargs = "hsaziepq"; while (buf != "q") {
// display
std::cout << "\n[ ";
std::for_each(cont.head(), cont.end(), [&](const unsigned &n) { std::cout << n << " "; });
std::cout << "]" << std::endl; // read in args
std::cout << optargs << "> ";
std::cin >> buf; if (!strchr(optargs, buf[])) {
std::cout << "Wrong Argument" << std::endl;
} else {
sscanf(buf.c_str(), "%*c,%d,%d", &n, &p);
func[buf[]]();
}
} return ;
}

下面我使用demo的记录,看了可能会比较好理解代码一点:

[ ]
hsaziepq> h
h for HELP
s for SUM
a for AVERAGE
z for SIZE
i,n,p for INSERT n AT p
e,n for ERASE AT p
p,n for PUSH_BACK n
q for QUIT [ ]
hsaziepq> s
sum: [ ]
hsaziepq> z
size: [ ]
hsaziepq> p, [ ]
hsaziepq> p, [ ]
hsaziepq> p, [ ]
hsaziepq> a
average: [ ]
hsaziepq> s
sum: [ ]
hsaziepq> z
size: [ ]
hsaziepq> i,, [ ]
hsaziepq> e, [ ]
hsaziepq> i,, [ ]
hsaziepq> i,, [ ]
hsaziepq> i,, [ ]
hsaziepq> e, [ ]
hsaziepq> s
sum: [ ]
hsaziepq> a
average: [ ]
hsaziepq> z
size: [ ]
hsaziepq> q

[C++11][数据结构]自己的双链表实现的更多相关文章

  1. 数据结构图文解析之:数组、单链表、双链表介绍及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  2. C# 数据结构 - 单链表 双链表 环形链表

    链表特点(单链表 双链表) 优点:插入和删除非常快.因为单链表只需要修改Next指向的节点,双链表只需要指向Next和Prev的节点就可以完成插入和删除操作. 缺点:当需要查找某一个节点的时候就需要一 ...

  3. 深入理解Redis 数据结构—双链表

    在 Redis 数据类型中的列表list,对数据的添加和删除常用的命令有 lpush,rpush,lpop,rpop,其中 l 表示在左侧,r 表示在右侧,可以在左右两侧做添加和删除操作,说明这是一个 ...

  4. 数据结构(C达到)------- 双链表

    双链表中的每个节点包含两个指针域,指针域包含其后继节点的内存地址,还有一个指针所存储的存储器地址其领域前驱节点. 双向链表结点的类型描写叙述: //双向链表的类型描写叙述 typedef int El ...

  5. 数据结构与算法之PHP实现链表类(单链表/双链表/循环链表)

    链表是由一组节点组成的集合.每个节点都使用一个对象的引用指向它的后继.指向另一个节点的引用叫做链表. 链表分为单链表.双链表.循环链表.   一.单链表 插入:链表中插入一个节点的效率很高.向链表中插 ...

  6. Python与数据结构[0] -> 链表/LinkedList[1] -> 双链表与循环双链表的 Python 实现

    双链表 / Doubly Linked List 目录 双链表 循环双链表 1 双链表 双链表和单链表的不同之处在于,双链表需要多增加一个域(C语言),即在Python中需要多增加一个属性,用于存储指 ...

  7. Java数据结构和算法之链表

    三.链表 链结点 在链表中,每个数据项都被包含在‘点“中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LINK对象中 ...

  8. 数组、单链表和双链表介绍 以及 双向链表的C/C++/Java实现

    概要 线性表是一种线性结构,它是具有相同类型的n(n≥0)个数据元素组成的有限序列.本章先介绍线性表的几个基本组成部分:数组.单向链表.双向链表:随后给出双向链表的C.C++和Java三种语言的实现. ...

  9. 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t

    nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...

随机推荐

  1. NCBI database download

    ascp -T -l 200M -i ~/.aspera/connect/etc/asperaweb_id_dsa.openssh --host=ftp-private.ncbi.nlm.nih.go ...

  2. [Think In Java]基础拾遗1 - 对象初始化、垃圾回收器、继承、组合、代理、接口、抽象类

    目录 第一章 对象导论第二章 一切都是对象第三章 操作符第四章 控制执行流程第五章 初始化与清理第六章 访问权限控制第七章 复用类第九章 接口 第一章 对象导论 1. 对象的数据位于何处? 有两种方式 ...

  3. Mysql连表之多对多

    说明 这里的文章是接着前面 Mysql连表一对多 写的. 连表多对多 可以理解成一夫多妻和一妻多夫. 男人表: nid name 1 xxx 2 yyy 3 zzz 女人表: nid name 1 a ...

  4. elk系列6之tcp模块的使用

    preface tcp模块的使用场景如下: 有一台服务器A只需要收集一个日志,那么我们就可以不需要在这服务器上安装logstash,我们通过在其他logstash上启用tcp模块,监听某个端口,然后我 ...

  5. elasticsearch curl operation

    Elastic Search API Index.简单的介绍了使用Elastic Search 如何建立索引. ElasticSearch-API-Index 索引创建API允许初始化一个索引.Ela ...

  6. Mono-D在MacOS上的设置

    1. 下载DMD 建议下载tar.xz压缩包,不建议下载dmg安装包,因为dmg中没有src,而后面需要用src中的内容设置代Code Completion. 地址:http://dlang.org/ ...

  7. getCanonicalName和getSimpleName getName的区别与应用

    接口: package com.test; public interface Fruit { } 一个实现类: package com.test; public class Apple impleme ...

  8. PHP代码编写规范

    一. 变量命名 a) 所有字母都使用小写 b) 首字母根据变量值类型指定 i. 整数i ii. 浮点数f iii. 字符串s iv. 布尔值b v. 数组a vi. 对象o vii. 资源r viii ...

  9. Unix/Linux进程间通信(一):概述

    序 Linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进 ...

  10. Android中的动画机制

          1 逐帧动画   逐帧动画 就是一系列的图片按照一定的顺序展示的过程.   逐帧动画很简单, 只需要在drawable中或者anim中定义一个Animation-list 其中包含多个it ...