C++数据结构: 线性表之单链表
来源:互联网 发布:无线虚能矩阵 编辑:程序博客网 时间:2024/06/10 09:12
上一篇我们已经实现过了线性表的顺序存储结构:顺序表,现在我们继续来实现基本的线性表的线性存储结构之单链表;
首先,单链表的存储结构是以节点数据为基本存储单元的(一个节点存储一个数据,头结点不存放数据),其次,其内存是向堆中申请的,而不是向栈中申请的,所以单链表的长度是可拓展的。所以其实现方式与上一次顺序表的实现方式大有不同。下面请看:
首先,我们的先实现存储数据的节点(为适应数据的不同类型,使用了类模板机制):
#pragma once //避免被包含多次template<typename T>class Node /*与书中不同,我这里并没有采用结构体类型,而是采用了类,但实际上功能是一样的*/{public: T data; //数据存储域 Node *next; //指向下一个节点的指针};
实现了单链表的数据存放节点之后,我们再来实现单链表的类模板实现
#pragma once /*避免文件被include多次效果与 #ifndef __SOMEFILE_H__ #define __SOMEFILE_H__ #endif 相同*/ #include<iostream> //头文件的引入 #include"Node.h";using namespace std;template<typename T> /*类模板的声明,成员函数中实现了基本的增删查改,数据成员中有数据的头结点,和计算链表长度的int类型的Length成员(这个在书中并没有)*/class LinkList{public: LinkList(T a[], int n); //有参构造函数 使用了头插法 ~LinkList(); //析构函数 int Length(); //返回单链表的长度 T Get(int i); //按位查找,查找第i个节点的元素 int Locate(T x); //按值查找,查找链表中第一个值为x的元素,并返回序号 bool Insert(int i, T x); //插入元素,在第i个位置插入值x bool Delete(int i); //删除节点,删除第i个节点 bool InsertHead(T x); //头插法插入节点 bool InsertTail(T x); //尾插法插入节点 void ListTraverse(); //遍历节点private: Node<T> *first; //头结点的指针 int m_Length; //实际使用过程当中,添加多一个数据成员Length会更好};**以下是成员函数的实现**template<typename T>LinkList<T>::LinkList(T a[], int n) //有参构造函数, 使用头插法(注意点:头插法是将元素放在头结点的后面){ first = new Node<T>; //空链表的初始化 first->next = NULL; for (int i=0;i<n;i++) //有疑问:这样做,头结点是不是没数据? 经过调试,发现头结点是不存储数据的 { Node<T> *s = new Node<T>; s->data = a[i]; s->next = first->next; first->next = s; } m_Length = n; /*for (int i = 0; i < n; i++) //直接调用下面的头插法插入数据更方便! { InsertHead(a[i]); }*/}//template<typename T>//LinkList::LinkList(T a[], int n) //同样是有参构造函数,但是使用的是尾插法//{// Node<T> *first = new NOde<T>;// Node<T> *r = first; //将头指针赋值给变量r// for (int i = 0; i < n; i++)// {// s = new Node; // s->data = a[i]; //创建新节点,赋值// r->next = s; //将r的指针指向s// r = s; //变量r后移到最后一个节点// }// r->next = NULL; //将尾节点的指针置空//}template<typename T>bool LinkList<T>::InsertHead(T x) //头插发插入数据{ Node<T> *Temp = first->next; //建立指向头指针的临时变量 Node<T> *s = new Node<T>; //建立新节点 if (s==NULL) //判断新节点是否申请成功 { return false; } s->data = x; //赋值 s->next = Temp; //上链 first->next = s; m_Length++; //链表的长度加一 return true; //插入成功,返回true}template<typename T>bool LinkList<T>::InsertTail(T x) //使用尾插法插入数据,使数据插入到最后一个{ Node<T> *p = first; //建立临时遍历指针 Node<T> *s = new Node<T>; //建立新节点 if (s == NULL) //判断新节点是否申请成功,若申请失败,则退出函数,不插入数据 { return false; } s->data = x; while (p->next != NULL) //遍历指针,使临时指针指向尾节点 { p = p->next; } p->next = s; //尾节点重新导向,将新节点上链 s->next = NULL; //将上链后的尾节点的指针域指向空 m_Length++; return true; //返回true,插入成功}template<typename T>LinkList<T>::~LinkList() //析构函数{ while (first!=NULL) { Node<T> *q = first; //遍历删除头指针指向的节点,将头指针暂存 first = first->next; //将头指针后移 delete q; //从链表中脱离出来的指针删除,释放内存 } m_Length = 0;}template<typename T>int LinkList<T>::Length() /*返回链表的长度的算法,实现思想:设定循环函数,将节点的指针从头指针开始依次后移,后移一次便将计数器自加1,直至到尾节点的指针为空,此时结束循环,返回计数器*/{ /*int num=0; Node<T> *p = first->next; while (p!= NULL) { p = p->next; num++; }*/ return m_Length; //添加数据成员length后,使得返回链表的长度函数更简单,代码更少 /*return num;*/}template<typename T>T LinkList<T>::Get(int i) //按位查找,返回第i个节点的元素{ Node<T> *p = first->next; int count = 1; while (p!= NULL&&count < i) { p = p->next; count++; } if (p == NULL) { throw"位置"; } else { return p->data; }}template<typename T>int LinkList<T>::Locate(T x) //按值查找,返回d第一个匹配值的序号{ Node<T> *p = first->next; int count = 1; while (p != NULL) { if (p->data == x) { return count; } p = p->next; count++; } return 0;}template<typename T>bool LinkList<T>::Insert(int i,T x) //往链表中插入元素,i为要插入的位置,x为要插入的值{ Node<T> *p = first; int count = 0; int num = i - 1; while (p!= NULL&&count <num) { p = p->next; count++; } if (p == NULL) { return false; } else { Node<T> *s = new Node<T>; s->data = x; s->next = p->next; p->next = s; m_Length++; return true; }}template<typename T>void LinkList<T>::ListTraverse(){ Node<T> *p = first->next; while (p != NULL) { cout << p->data<<","; p = p->next; //遍历的指针的后移,注意不能写成p++,因为这是节点 }}template<typename T>bool LinkList<T>::Delete(int i){ Node<T> *p = first; int count = 0; while (p != NULL&&count < i - 1) { p = p->next; count++; } if (p == NULL) { return false; } else { Node<T> *q; q = p->next; p->next = q->next; delete q; m_Length--; return true; }}
以上就是单链表类模板的实现。
需要注意的几个点:
- 头结点并不实际存放实际的数据,所以在实现个别成员函数(如插入指定位置的函数,查找指定值所在位置的函数,删除函数)的时候需要特别注意,头节点并不是第一个数据节点,而是,头结点的下一个节点,所以,实现的时候需要注意一下,比如,计算位置的num数该从何时开始计数?该从何时开始循环?循环何时结束?
- 指针的移动,不是p++,而是p->next
- 节点数据中存放的指针指向的是下一个一整块的节点,而不仅仅是下一个节点的存储数据的域、
- 时间复杂度方面:插入和删除时无需移动元素时间复杂度为o(1) (优点) ,而按位查找方面则不如顺序表方便。时间函数为o(n),所以解决问题时根据需求选择存储结构
- 删除和析构函数方面:由于这是向堆中申请的内存,所以不能单纯地摘链,还要将申请到地空间释放出来
实例调用:
#include<iostream>#include"LinkList.h"using namespace std;int main(){ int a[5] = {1,2,3,4,5}; LinkList<int> MyList(a,5); cout << MyList.Length() << endl; //链表长度函数调用成功 cout << "第5个节点的元素为:" << MyList.Get(5) << endl; //输入有效长度的数据可成功调用,但是,若输入不合法的数据将出错,throw的语句未完成 if (MyList.Locate(5) != 0) { cout << "元素5所在的位置为:" << MyList.Locate(5) << endl; } //输入合法的数据,测试成功 else if(MyList.Locate(5)==0){ cout << "输入数据不合法,输入的节点位置超过链表超度" << endl; } if (MyList.Insert(3, 2)) { cout << "插入元素成功!" << endl; }//插入元素测试成功 else { cout << "插入元素失败!" << endl; } MyList.ListTraverse(); if(MyList.Delete(3)) { cout << "删除节点成功" << endl; } else { cout << "删除节点失败" << endl; } MyList.ListTraverse(); //单链表的遍历成功 cout << endl; MyList.InsertHead(7); //调用头插法 cout << "调用头插法成功!"; MyList.ListTraverse(); MyList.InsertTail(4); //调用尾插法 cout << endl; cout << "调用尾插法成功!"; MyList.ListTraverse(); return 0;}
调用结果:
最后一点:
这只是单链表地基本实现方式,要想用来解决实际问题,还要经过添加各种算法(函数)。
以上!
阅读全文
1 0
- C数据结构-线性表之单链表
- 数据结构之线性表(C#)
- C数据结构-线性表之顺序表
- C语言数据结构之线性表
- C语言数据结构之线性表
- C语言数据结构之线性表(续)
- 数据结构C语言版之线性表
- 数据结构之线性表(C++)---数组描述
- 数据结构之线性表(C语言版)
- 数据结构:线性表之单链表
- 数据结构线性表之单链表
- 数据结构:线性表之单链表
- 数据结构-线性表之单链表
- 数据结构-线性表 (C++)
- 数据结构线性表c
- C - 数据结构 - 线性表
- 数据结构之线性表
- 数据结构之线性表
- 【deeplearning.ai】第二门课:提升深层神经网络——偏差和方差
- 【51Nod1952】栈
- dubbo入门官方案例学习
- springmvc上传图片方法
- Android Webview H5交互之LocalStorage
- C++数据结构: 线性表之单链表
- 缓存淘汰算法 LFU and LRU
- 进程同步与异步
- MFC多线程编程
- Xbrowser远程连接显示灰屏
- HDU 1426(PE多次)
- Vector去掉重复数字(改变原来的排列顺序)
- 【python学习笔记】Python函数式编程
- 9.23模拟总结