单链表
线性表的链式存储结构的特点:
- 是用一组任意的存储单元来存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的 。
我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。 指针域中存储的信息称做指针或链。 这两部分组成数据元素ai 的存储映像,称为结点。
头指针 :就是链表中的第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行的。
为了更方便地对链表进行操作,如删除第一个结点的特殊情况(第一个结点没有前驱,而要摘除一个结点需要首先找到它的前驱才能做摘除操作),经常在单链表的第一个结点前附设一个结点,称为头节点,这样头指针就指向了头结点。
头指针和头结点的异同:
- 头指针是指向列表第一个结点的指针,若链表有头结点, 则是指向头结点的指针。
- 头指针具有标识作用,所以常用头指针冠以链表的名字
- 无论链表是否为空,头指针均不为空。 头指针是链表的必要元素。
- 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(也可存放链表的长度)。
- 有了头结点,对第一个元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了。
指针变量和结点变量:
指针变量p和结点变量*p的关系:
指针变量P 结点变量*P 定义 在变量说明部分显示定义 在指针变量P使用时,通过标准的new生成 取值 结点的地址 结点各个域的内容 操作方式 指针变量名访问 通过 *取值运算符访问
结点域的访问
- (*p).data 和 (*p).next
- p->data 和 p->next
指针变量p和结点变量*p的关系 :
- 指针变量 p的值是结点的地址
- 结点变量 *p的值是结点的内容
- *((*p).next)是*p后继结点的内容
优点:相对于数组,删除和插入效率高
缺点:相对于数组,查询元素和获取元素效率低
要执行插入操作,只需要如下的代码:
s->next = p->next
p-next = s ;
执行删除操作,只需要如下的代码:
p->next = p->next->next
或者
q = p->next;
p->next = q->next;
单链表结构和顺序表存储结构的优缺点:
结论:
- 若线性表需要频繁查找,很少进行插入和删除操作,采用顺序表,若需要频繁插入和删除时,采用单链表。
- 当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑存储空间的大小问题。 而如果事先知道线性表的大致长度,顺序表效率更好
下面用 C++ 语言 实现单链表:
单链表的头文件:
#include<iostream>
#include<cassert>
using namespace std;
#ifndef TT_CHAIN_LIST
#define TT_CHAIN_LIST
#define TT_OK 1
#define TT_ERROR 0
namespace tt
{
class ChainList
{
public:
using ElemType = int;
using Status = void;
public:
struct Node
{
ElemType m_data; //数据域
Node *m_next; //指针域
};
public:
ChainList();
~ChainList();
ElemType insertAt(ElemType i, ElemType elem);
ElemType removeAt(ElemType i, ElemType &elemOut);
ElemType getAt(ElemType i, ElemType &elemOut);
ElemType destroy();
ElemType getIndexElemAt(ElemType &i, ElemType elemOut);//查找与e相等的元素,返回第一个与e相等元素在线性表中的下标,否则,返回0
ElemType isEmpty()const;
Status getLength()const;
ElemType clear();
Status show();
Status createTail(ElemType *datas, ElemType length);//创建长度为length的链表,数据通过数组指定,这里采用尾插法
Status createHead(ElemType *datum, ElemType extent);
ElemType priorElemAt(ElemType cur_e, ElemType &pri_e);//若cur_e是链表的数据元素,且不是第一个,则用pre_e返回它的前驱
ElemType nextElemAt(ElemType cur_e, ElemType &Nex_e); //若cur_e是链表的数据元素,且不是最后一个,则用next_e返回它的后继,
Status reverseList(); //反转列表
Node *recurReverseList(Node *heap); // 递归反转列表
private:
Node *m_heap; //头结点
};
inline ChainList::ElemType ChainList::isEmpty()const
{
return (m_heap->m_data == 0);
}
inline ChainList::Status ChainList::getLength()const
{
cout << "当前的单链表中的数据为:" << (m_heap->m_data) << "\n" << endl;
}
}
#endif //TT_CHAIN_LIST
单链表的源文件:
#include"ChainList.h"
namespace tt
{
ChainList::ChainList()
{
m_heap = new Node;
assert(m_heap != nullptr);
m_heap->m_next = nullptr;
m_heap-&g