版权声明:QQ:763905926 未经允许请勿转载,转载请注明出处! https://blog.csdn.net/lms1008611/article/details/81916359
前边我们创建了顺序存储结构的线性表,简称顺序表,顺序表最大的问题是:插入和删除需要移动大量的元素。为了解决 这个问题, 我们引入链式存储结构的线性表,简称链表,链表与顺序表不同,链表的每个结点在内存中是分开存放的,每个结点都包含数据域和指针域:
- 数据域 :存储数据元素本身
- 指针域 :存储相邻结点的地址
链式存储结构的线性表有
- 单链表:每个结点只包含直接后继的地址信息
- 循环链表: 单链表中的最后一个结点的直接后继为第一个结点
- 双向链表: 单链表中的结点包含直接前驱和后继的地址信息
- 双向循环链表:双向链表的最后一个结点的后继为第一个结点,第一个结点的前驱为最后一个结点
链表中的基本概念
- 头结点 : 链表中的辅助结点,包含指向第一个数据元素的指针
- 数据结点:链表中代表数据元素的结点,包含数据元素与地址两部分
- 尾结点:链表中的最后一个数据结点,包含的地址信息为空
这里我们创建的单链表,结点可以 定义为如下
struct Node : public MyObject
{
T value; //数据域
Node* Next; //指针域
};
单链表的内部结构如下,头结点不存储实际的数据元素,只是为了数据元素定位,方便插入和删除操作
下边我们直接来看代码中的实现
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include "List.h"
namespace MyLib
{
template <typename T>
class LinkList : public List<T>
{
private:
struct Node : public MyObject
{
T value; //数据域
Node* Next; //指针域
};
mutable struct : public MyObject
{
char reserved[sizeof(T)]; //作为占位用,不存放数据
Node* Next;
}m_header;
int m_length; //存储链表长度
Node* m_current; //指向当前的结点
int m_step; //
Node* position(int index) const //获取index处的结点
{
Node* ret = reinterpret_cast<Node*>(&m_header);
for (int i=0; i<index; i++)
{
ret = ret->Next;
}
return ret;
}
virtual Node* create()
{
return new Node;
}
virtual void destroy(Node* p)
{
delete p;
}
public:
LinkList()
{
m_length = 0;
m_header.Next = NULL;
m_current = NULL;
m_step = 1;
}
bool insert( const T& e) //尾部插入结点
{
return insert(m_length, e);
}
bool insert(int index, const T& e) //插入结点
{
bool ret = ( (0 <= index)&&(index <= m_length) );
if (ret)
{
Node* currentNode = position(index);
Node* newNode = create();
newNode->value = e;
newNode->Next = currentNode->Next;
currentNode->Next = newNode;
m_length++;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
}
return ret;
}
bool remove(int index) //删除结点
{
bool ret = ( (0 <= index)&&(index < m_length) );
if (ret)
{
Node* currentNode = position(index);
Node* toRemove = currentNode->Next;
if (m_current == toDel)
{
m_current = toDel->Next;
}
currentNode->Next = toRemove->Next;
m_length--;
destroy(toRemove);
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
}
return ret;
}
bool get(int index, T& e) //获取具体位置的结点元素
{
bool ret = ( (0 <= index)&&(index < m_length) );
if (ret)
{
Node* currentNode = position(index);
e = currentNode->Next->value;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
}
return ret;
}
bool set(int index, const T& e)const //设置具体位置的结点元素
{
bool ret = ( (0 <= index)&&(index < m_length) );
if (ret)
{
Node* currentNode = position(index);
currentNode->Next->value = e;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Bounds Exception...");
}
return ret;
}
int find(const T& e) const //链表中寻找元素位置
{
Node* tofind = m_header.Next;
for (int i=0; i<m_length; i++)
{
if (e == tofind->value)
{
return i;
}
else
{
tofind = tofind->Next;
}
}
return -1;
}
void move(int index, int step = 1) //游标移动初始化
{
bool ret = ( (0<=index)&&(index < m_length) );
if (ret)
{
m_current = position(index)->Next;
m_step = step;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Exception...");
}
}
void next() //游标移动到下一个位置
{
int i=0;
while ( (!end())&&(i<m_step) )
{
m_current = m_current->Next;
i++;
}
}
bool end() //判断游标是否到达链表最尾处
{
return m_current == NULL;
}
T current() //返回游标所指处的元素
{
if (!end())
{
return m_current->value;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Index Out Of Exception...");
}
}
int length()const //返回列表长度
{
return m_length;
}
void clear() //清空列表
{
while(m_header.Next)
{
remove(m_length-1);
}
}
};
}
#endif //__LINKLIST_H__
上边我们就基本把单链表的内容实现完了,下边我们来在main函数中使用一下
#include <iostream>
#include <string>
#include "LinkList.h"
using namespace std;
using namespace MyLib;
int main()
{
LinkList<int> list;
for (int i=0; i<10; i++)
{
list.insert(i);
}
list.set(2, 23);
for (list.move(0); !list.end(); list.next()) //采用游标方式遍历链表
{
cout << list.current() << endl;
}
cout << "find(3) = " << list.find(3) << endl;
system("pause");
return 0;
}
编译执行
总结
- 链表中的数据元素在物理内存中无相邻关系
- 链表中的结点都包含数据域与指针域
- 头结点用于辅助数据元素的定位,方便插入和删除操作
- 插入和删除操作需要保证链表的完整性