双向链表实现

今天晚上用模板方法把双向链表实现了下,由于有点小粗心,在 insert中手抖了下,把tail->prev 打成了 tail->next,由于错误是发生在drop函数执行时,让我一直去调drop函数,调了半天还是一样错误,最后我系统在vs中监视了下值的变化,终于看到是insert出错了。 看来程序员离不开调试呀。

另外,昨天说的模板输出重载,我说要在友元的实现时加上 <>, 但是今天我在gcc中测试了下,居然说找不到匹配的函数,导致编译不通过,真心蛋疼,vs和g++的区别还真心大,看来改天要好好专研下模板输出重载,知道的朋友希望能够告知下。


对数据结构的实现,其实都很简单,思想就是:

1、定义结点结构类,包含数据域和指针域

2、定义 链表(树,图)结构,其中封装包含几个结点,作为数据接口,因为节点定义指向自身类型的指针,因此而后所有的操作都围绕这个数据接口进行。


对于双向链表来说,结点定义很简单,一个数据域,一个next指针,表示以后他将指向下一个结点; 一个prev指针,表示它将指向前一个结点。

template<typename T>struct Node{T datum;Node* next;Node* prev;};

由于链式结构添加的结点都要new出来,且结点类包含了数据域,因此需要对结点类进行构造函数的编写,一般两个,带数据域的(以后new来做链表中结点),默认无参的(用于new给head和tail的);析构函数就不用了,因为其指针域所指向的东西是由表结构new出来的,操作应该留给表。

结点类定义好了后,我们定义下链表类,主要部分就是要包含下 head 指针, tail 指针。

另外,所有的表(树,图结构)最好包含个 “表示长度的数据”,如size, 这样求length()操作只要O(1)的复杂度,要不然就要遍历整个链表,复杂度就是O(n)了。

对于head指针,规定 head->next 指向第一个结点,head->prev 指向自身或NULL;

对于tail指针,规定 tail->prev 指向最后一个结点,tail->next指向自身或NULL;  这样规定了head,tail后,后面的操作就会很顺畅,我们就可以next/prev到底了,哈哈。

空的条件为: size == 0 或者 head->next == NULL 或者 tail->prev == NULL ,看个人喜好。

剩下一些增删改查操作,别手抖写错了指向关系就行,另外可以加上一个位置判断,看看是从头或从尾开始,哪边移动的少。

函数写法可以给的建议是:

若只是访问不修改,成员函数为const;

若需要操作类类型,则用 & 或 const&;

好的,直接上代码:

先是头文件,注意最后一行;

 1 #ifndef MY_CHAIN_H
 2 #define MY_CHAIN_H
 3 #include <ostream>
 4 using namespace std;
 5 template <class T>
 6 struct Node{
 7     T datum;
 8     Node* prev;
 9     Node* next;
10
11     Node(const T& x);
12     Node();
13 };
14
15
16 template<class T>
17 class dList{
18     public:
19         dList();
20         ~dList();
21         bool find(int pos,T& hold) const;
22         int search(const T& x) const;
23         int length() const;
24         bool isEmpty() const;
25         dList<T>& drop(int pos, T& hold);
26         dList<T>& insert(int pos, const T& x);
27         dList<T>& push_back(const T& x);
28         dList<T>& push_front(const T& x);
29         T& operator[](int pos);
30         void show(std::ostream& os) const;
31         friend ostream& operator<< <>(ostream& os, const dList& d);
32     protected:
33         Node<T>* head;
34         Node<T>* tail;
35         int size;
36 };
37 #endif
38
39 #include "chain.cpp"

View Code

接着是实现文件:

#ifndef MY_CHAIN_CPP
#define MY_CHAIN_CPP
#include "chain.h"
#include <ostream>
#include <cassert>
using std::ostream;
// 节点类的构造
template<class T>
Node<T>::Node(const T& x) : datum(x){next = prev = NULL;
}template<class T>
Node<T>::Node(){next = prev = NULL;datum = 0;
}// 双向链表类的构造
template<class T>
dList<T>::dList()
{head = new Node<T>();
//    tail = new Node<T>();
head->next = NULL;head->prev = head;tail = head;tail->next = tail;tail->prev = NULL;size = 0;
}// 析构
template<typename T>
dList<T>::~dList()
{if(head){   // 一个建议,若类中数据成员是指针类型,且指向是通过new出来的,那么最好这里加上一个判断。 当然我这里是多余的,因为构造时必然new了。Node<T>* p = head->next;Node<T>* t;while(p != NULL){t = p;p = p->next;delete t;}delete head;head = NULL;  // 最好删除后指向空tail = NULL;}
}template<class T>
bool dList<T>::isEmpty() const
{return size == 0;
}template<class T>
bool dList<T>::find(int pos,T& hold) const   // 访问不修改,成员函数const
{if(pos < 1 || pos > size) return false;Node<T>* p;if(pos <= (size>>1)){      // 从头开始比较快p  = head;int count = pos;while(count--){p = p->next;}    // next 给 head}else{p = tail;                      // 从尾开始比较快int count = size - pos;while(count--){p = p->prev;}    // 哈哈,中就是为什么把prev给tail, 代码是不是很顺畅?
        }hold = p->datum;return true;
}template<class T>
int dList<T>::search (const T& x) const
{Node<T>* p = head->next;int count = 1;while((p!= NULL) && (p->datum != x)){p = p->next;count++;}if (p == NULL) {return 0;}return count;
}
template<class T>
int dList<T>::length ()const
{return size;
}
template<class T>
dList<T>& dList<T>::push_front (const T& x)   // 前插
{Node<T>* p = new Node<T>(x);if (size == 0) {head->next = p;p->prev = NULL;tail->prev = p;p->next = NULL;}else{p->next = head->next;head->next->prev = p;p->prev = NULL;head->next = p;}++size;return *this;
}template<class T>
dList<T>& dList<T>::push_back (const T& x)     // 尾插
{Node<T>* p = new Node<T>(x);if (tail->prev == NULL) {head->next = p;p->prev = NULL;tail->prev = p;p->next = NULL;}else{tail->prev->next = p;p->prev = tail->prev;p->next = NULL;tail->prev = p;}++size;return *this;
}template<class T>
dList<T>& dList<T>::insert(int pos, const T& x)    // 指定位置插入,范围[1,size+1]  注意,我的代码中,1为下标开始;除了后边重载的[]操作符。
{if (pos == 1) {return push_front (x);}if (pos == (size+1)) {return push_back (x);}Node<T>* in = new Node<T>(x);Node<T>* p;if (pos <= (size/2)) {p = head;int t = pos;while(t--){p = p->next;}}else{p = tail;int t = size - pos;while(t--){p = p->prev;}}in->next = p;in->prev = p->prev;    // 哎,这里就是我万恶粗心的地方,Mark下,下次要心细点。p->prev->next = in;p->prev = in;++size;return *this;
}template<class T>
dList<T>& dList<T>::drop(int pos, T& hold)
{if (pos < 1 || pos > size) {exit(1);}Node<T>* d = NULL;if(pos == 1){d = head->next;hold = d->datum;if(size == 1){            tail->prev = NULL;head->next = NULL;}else{head->next = d->next;d->next->prev = NULL;}--size;delete d;d = NULL;return *this;}if(pos == size){d = tail->prev;hold = d->datum;tail->prev = d->prev;d->prev->next = NULL;size--;delete d;d = NULL;return *this;}else{if(pos <= (size/2)){int c=pos;d = head;while(c--){d = d->next;}}else{int c = size - pos;d = tail;while(c--){d = d->prev;}}hold = d->datum;d->prev->next = d->next;d->next->prev = d->prev;--size;delete d;d = NULL;return *this;}}template<class T>
T& dList<T>::operator[](int pos)
{pos = pos + 1;if(pos<1 || pos> size) {exit(1);}Node<T>* p = NULL;if (pos <= (size>>1)) {int t = pos;p = head;while(t--){p = p->next;}}else{int t = size - pos;p = tail;while (t--) { p = p->prev;}}return p->datum;
}template<class T>
void dList<T>::show (ostream& os) const
{Node<T>* p = head->next;int t = 0;while(p != NULL){os << p->datum << " ";p = p->next;t++;}assert(t==size);
}template<class T>
ostream& operator<< <>(ostream& out, const dList<T>& x)
{x.show(out);return out;
}
#endif

View Code

最后是测试文件:

 1 #include "chain.h"
 2 #include <iostream>
 3 using namespace std;
 4
 5 int main()
 6 {
 7     dList<int> dl;
 8     int x=0;
 9     dl.insert (1,5);
10     cout << "insert pos1 5: " << dl << endl;
11     cout << "length: " << dl.length() << endl;
12     dl.push_front (3);
13     cout << "push front 3: " << dl << endl;
14     cout << "length: " << dl.length() << endl;
15     dl.push_back (6);
16     cout << "push back 6: " << dl << endl;
17     cout << "length: " << dl.length() << endl;
18     dl.insert(4,8);
19     cout << "insert pos4 8: " << dl << endl;
20     cout << "length=" << dl.length () << endl;
21     dl.find (3,x);
22     dl.drop(2,x);
23     cout << "drop pos 2: " << dl << endl;
24     cout << "length=" << dl.length () << endl;
25     cout << "dl[0]=" << dl[0] << endl;
26     dl[0] = 10;
27     cout << "dl[0]=" << dl[0] << endl;
28     cout << dl << endl;
29 }

View Code

结果如下图所示:

补充: 其实还可以重载更多的操作符,有心情的朋友可以自己添加下,比如++(int), ++操作等。甚至,可以加入个迭代器类,这样更方便使用,有时间可以实现下哦。

另外,心细,心细,手别抖。 自勉下!

转载于:https://www.cnblogs.com/IntellX/archive/2013/05/23/3094042.html

数据结构练手02 双向链表实现相关推荐

  1. 数据结构练手小项目(AVL树、哈希表、循环链表、MySQL数据库)

    文章目录 前言 正文(无删减) 我的想法(删减修改版) 数据导入与数据存储 功能实现 数据结构 用户结构 SIM卡结构 AVL树数据结构 哈希表结构 数据表 用户表 SIM卡表 时间安排 前言 本月主 ...

  2. vue练手02之表格处理

    订单处理 code <!DOCTYPE html> <html lang="en"> <head><meta charset=" ...

  3. c语言判断一个点在长方体内部_21个入门练手项目,让你轻松玩转C语言

    C 语言作为大学理工科专业的必修,是很多同学走进编程世界的第一课.那么怎样才能更好的入门 C 语言呢? 下面整理了 21 个 C 语言练手项目,从基础语法开始,逐步深入,通过一个个练手项目,让你轻松驰 ...

  4. c语言21个入门练手项目,21个入门练手项目让你轻松玩转C语言

    整理了 21 个 C 语言练手项目,从基础语法开始,逐步深入,通过一个个练手项目,让你轻松驰骋在 C 语言的快车道.不走弯路就是捷径! 1.<C语言入门教程>:引入大量的 C 语言程序案例 ...

  5. 人脸识别:insightface自定义数据集制作 | 附练手数据集

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 转自|小白玩转python 01 人脸识别简介 简单来讲,人脸识别 ...

  6. 棱形旋转c语言程序_C 语言时隔 5 年重回巅峰,这 20 个热门项目拿去练手!

    在上个月的 TIOBE 编程语言排名中,C 语言和 Java 的差距只有 0.01%.在近日 TIOBE 公布的 2020 年 5 月编程语言排行榜中,C 语言成功超越了 Java,重返第一的王者宝座 ...

  7. 推荐 Python 十大经典练手项目,让你的 Python 技能点全亮!

    前言:如果有人问:"Python还火吗?""当然,很火.""哪能火多久呢?""不知道." 技术发展到现在衍生出许多种编程 ...

  8. 给Python初学者的最好练手项目,进来看就对了!

    有很多朋友问我学习了Python后,有没有什么好的项目可以练手. 其实,做项目主要还是根据需求来的.但是对于一个初学者来说,很多复杂的项目没办法独立完成,因此博主挑选了一个非常适合初学者的项目,内容不 ...

  9. 推荐一个C++练手项目,面试也可用

    经常有人问我,C++初学阶段有哪些项目可以拿来练手? 我一般都会建议他们尝试去写一个单元测试框架.因为这个项目十分考验我们对C++基础语法的掌握度. 但是自己摸索容易出错和遗漏,所以给大家推荐下面这个 ...

最新文章

  1. 解决Http响应内容中文乱码问题
  2. P1420 最长连号(python3实现)
  3. Telnet命令在Linux / Unix中的用法
  4. 什么是真正的程序员:A Little Printf Story
  5. foreach()与list()的综合应用,用list给嵌套的数组解包
  6. 教你写一个弹幕库,确定不了解一下?,请查收
  7. 重写弹幕射击游戏的记录
  8. 双层pdf怎么制作(可以复制里面文字)纸质书如何制作扫描图片书签目录?
  9. span标签之间的空隙如何解决
  10. XML采用Boost::regex解析实例
  11. 谈谈HTTP协议中的短轮询、长轮询、长连接和短连接
  12. Ceph剖析:Leader选举
  13. 基于zookeeper的瞬时节点实现分布式锁
  14. QT QDateTime获取当前时间且分出年月日时分秒
  15. MacW资讯:开启Mac的壁纸自动更换功能
  16. Hangzhou Invitation Day1
  17. 交互设计实用指南系列11-减少记忆负担
  18. flask之淘票票后端API项目总体规划
  19. 服装色彩搭配的一些忌讳
  20. CMOS电路ESD保护结构设计

热门文章

  1. Java 用POST方式 传对象给 Servlet
  2. Akka系列(二):Akka中的Actor系统
  3. 安装exchange
  4. 自定义Dialog宽度占满屏幕
  5. Android保存之SharedPreferences
  6. 风云再起-后IOE时代的Oracle架构变迁与创新-V3
  7. Python学习笔记-Tuple
  8. [笔记].痛哉!!!Error: Can't access JTAG chain, Error: Operation failed
  9. oracle中的new old 关键字
  10. eclipse 中配置php的 XDebug调试