数据结构练手02 双向链表实现
双向链表实现
今天晚上用模板方法把双向链表实现了下,由于有点小粗心,在 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 双向链表实现相关推荐
- 数据结构练手小项目(AVL树、哈希表、循环链表、MySQL数据库)
文章目录 前言 正文(无删减) 我的想法(删减修改版) 数据导入与数据存储 功能实现 数据结构 用户结构 SIM卡结构 AVL树数据结构 哈希表结构 数据表 用户表 SIM卡表 时间安排 前言 本月主 ...
- vue练手02之表格处理
订单处理 code <!DOCTYPE html> <html lang="en"> <head><meta charset=" ...
- c语言判断一个点在长方体内部_21个入门练手项目,让你轻松玩转C语言
C 语言作为大学理工科专业的必修,是很多同学走进编程世界的第一课.那么怎样才能更好的入门 C 语言呢? 下面整理了 21 个 C 语言练手项目,从基础语法开始,逐步深入,通过一个个练手项目,让你轻松驰 ...
- c语言21个入门练手项目,21个入门练手项目让你轻松玩转C语言
整理了 21 个 C 语言练手项目,从基础语法开始,逐步深入,通过一个个练手项目,让你轻松驰骋在 C 语言的快车道.不走弯路就是捷径! 1.<C语言入门教程>:引入大量的 C 语言程序案例 ...
- 人脸识别:insightface自定义数据集制作 | 附练手数据集
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 转自|小白玩转python 01 人脸识别简介 简单来讲,人脸识别 ...
- 棱形旋转c语言程序_C 语言时隔 5 年重回巅峰,这 20 个热门项目拿去练手!
在上个月的 TIOBE 编程语言排名中,C 语言和 Java 的差距只有 0.01%.在近日 TIOBE 公布的 2020 年 5 月编程语言排行榜中,C 语言成功超越了 Java,重返第一的王者宝座 ...
- 推荐 Python 十大经典练手项目,让你的 Python 技能点全亮!
前言:如果有人问:"Python还火吗?""当然,很火.""哪能火多久呢?""不知道." 技术发展到现在衍生出许多种编程 ...
- 给Python初学者的最好练手项目,进来看就对了!
有很多朋友问我学习了Python后,有没有什么好的项目可以练手. 其实,做项目主要还是根据需求来的.但是对于一个初学者来说,很多复杂的项目没办法独立完成,因此博主挑选了一个非常适合初学者的项目,内容不 ...
- 推荐一个C++练手项目,面试也可用
经常有人问我,C++初学阶段有哪些项目可以拿来练手? 我一般都会建议他们尝试去写一个单元测试框架.因为这个项目十分考验我们对C++基础语法的掌握度. 但是自己摸索容易出错和遗漏,所以给大家推荐下面这个 ...
最新文章
- 解决Http响应内容中文乱码问题
- P1420 最长连号(python3实现)
- Telnet命令在Linux / Unix中的用法
- 什么是真正的程序员:A Little Printf Story
- foreach()与list()的综合应用,用list给嵌套的数组解包
- 教你写一个弹幕库,确定不了解一下?,请查收
- 重写弹幕射击游戏的记录
- 双层pdf怎么制作(可以复制里面文字)纸质书如何制作扫描图片书签目录?
- span标签之间的空隙如何解决
- XML采用Boost::regex解析实例
- 谈谈HTTP协议中的短轮询、长轮询、长连接和短连接
- Ceph剖析:Leader选举
- 基于zookeeper的瞬时节点实现分布式锁
- QT QDateTime获取当前时间且分出年月日时分秒
- MacW资讯:开启Mac的壁纸自动更换功能
- Hangzhou Invitation Day1
- 交互设计实用指南系列11-减少记忆负担
- flask之淘票票后端API项目总体规划
- 服装色彩搭配的一些忌讳
- CMOS电路ESD保护结构设计