逻辑结构上相邻的数据元素,存储在指定的一块内存空间中,数据元素只允许在这块内存空间中随机存放,这样的存储结构生成的链表称为静态链表。
静态链表和动态链表的区别:静态链表限制了数据元素存放的位置范围;动态链表是整个内存空间。
静态链表的构建方法
静态链表使用数组这一数据类型预先申请足够大的内存空间。
由于各数据元素在数组申请的内存空间内随机存放,为了体现逻辑上的相邻,为每一个数据元素配备一个具有指针作用的整形变量,用于记录下一元素在数组中的位置。
在数组申请的存储空间中,各数据元素虽随机存储,每一个元素都记录着下一元素在数组中的位置,通过前一个元素,可以找到下一个元素,构成了一条链表,这条被局限在特定内存空间的链表就是静态链表。
静态链表中结点的构成
静态链表中每个结点既有自己的数据部分,还需要存储下一个结点的位置,所以静态链表的存储实现使用的是结构体数组,包含两部分: 数据域 和 游标(存放的是下一个结点在数组中的位置下标)。
静态链表的空间重复利用
由于静态链表提前申请了有限的内存空间,在使用的过程中,极有可能会出现申请的内存空间不足,需要使用之前被遗弃的内存空间。
被遗弃的意思是:之前已经使用,但是后期对该结点做了摘除操作,该内存空间中存放的是已经不用的垃圾数据。
所以,在整个过程中,需要自己动手把两者区分开,也就是需要自己实现 malloc 和 free 两个函数的作用。
解决的办法是:提前将所有未被使用的结点链成一个备用链表。需要对链表做插入操作时,从备用链表上摘下一个结点使用;删除链表中的结点时,删除的同时链接到备用链表上,以备下次使用。
#pragma once
#include<vector>
using namespace std;
template<typename T>
class Node {
public:
Node(){}
Node(T d, int i) {
data = d;
cur = i;
}
T data;
int cur;
};
template<typename T>
class CStaticList
{
public:
CStaticList(int s);
~CStaticList();
bool isEmpty();
//生请待插入节点 返回待插入节点再数组中的下标
int Malloc_SLL();
//插入代码1
void ListInsert(int i, T data);
//获取链表长度
int ListLength();
//初始化
int InitArr();
//查找
int selectElem(T data);
//插入代码2
void insertArr(int i, T data);
//删除
void deleteArr(T data);
//放入备用节点
void freeArr(int del);
private:
int MAX_SIZE;
int m_lenght = 0;
vector<Node<T>> m_list;
};
template<typename T>
CStaticList<T>::CStaticList(int s) :MAX_SIZE(s)
{
m_list.resize(MAX_SIZE);//分配空间
int i;
for (i = 0; i < MAX_SIZE; i++) {
m_list[i].cur = i + 1;
}//初始化为备用链表
//MAXsize - 1的位置 存放第一个有数值元素的下标 相当于链表的头节点作用
m_list[MAX_SIZE - 1].cur = 0;//当此位置的cur为0时相当于空表
}
template<typename T>
CStaticList<T>::~CStaticList()
{
}
template<typename T>
bool CStaticList<T>::isEmpty()
{
return false;
}
/*若备用链表非空,返回分配的结点的下标,否则返回0*/
template<typename T>
int CStaticList<T>::Malloc_SLL()
{
int i = m_list[0].cur; /*返回备用链表的第一个结点的下标*/
if (m_list[0].cur)
m_list[0].cur = m_list[i].cur; /*由于使用了一个元素,我们将它的后继链接元素作为备用链表的头*/
return i;
}
//插入
template<typename T>
inline void CStaticList<T>::ListInsert(int i, T data)
{
if (i < 1|| i > m_list.size()-1)return;//小标越界返回
int j, k, l;
k = m_list[1].cur; /*获取数组最后一个位置下标*/
j = Malloc_SLL(); /*获取备用链表第一个位置的下标*/
if (j) {
m_list[j].data = data;
for (l = 1; l < i - 1; l++)
k = m_list[k].cur; /*获取第i个元素之前位置的下标*/
m_list[j].cur = m_list[k].cur;
m_list[k].cur = j; /*cur值之间的重新链接*/
}
}
/* 返回m_list中有数据元素链表的个数 */
template<typename T>
inline int CStaticList<T>::ListLength()
{
int j = 0;
int i = m_list[1].cur;
while (i)
{
i = m_list[i].cur;
j++;
}
return j;
}
template<typename T>
inline int CStaticList<T>::InitArr()
{
//从备用链表中拿出一个分量作为链表头结点,返回的是这个分量的下标
int body = Malloc_SLL();
//声明一个变量,把它当指针使,指向链表的最后的一个结点,因为链表为空,所以和头结点重合
int tempBody = body;
for (int i = 1; i < 5; i++)
{
int j = Malloc_SLL(); //从备用链表中拿出空闲的分量
m_list[tempBody].cur = j; //将申请的空线分量链接在链表的最后一个结点后面
m_list[j].data = 'a' + i - 1; //给新申请的分量的数据域初始化
tempBody = j; //将指向链表最后一个结点的指针后移
}
m_list[tempBody].cur = 0; //新的链表最后一个结点的指针设置为0
return body;
}
template<typename T>
inline int CStaticList<T>::selectElem(T data)
{
int i = m_list[1].cur;
while (m_list[i].cur!=0)
{
if (m_list[i].data == data) {
return i;
}
i = m_list[i].cur;
}
if (m_list[i].data == data) {
return i;
}
return -1;
}
template<typename T>
inline void CStaticList<T>::insertArr(int add, T data)
{
int tempBody = m_list[1].cur; //tempBody做遍历结构体数组使用
//找到要插入位置的上一个结点在数组中的位置
for (int i = 1; i < add-1; i++)
{
tempBody = m_list[tempBody].cur;
}
int insert = Malloc_SLL(); //申请空间,准备插入
m_list[insert].cur = m_list[tempBody].cur; //首先要插入结点的游标等于要插入位置的上一个结点的游标
m_list[insert].data = data;
m_list[tempBody].cur = insert; //然后让上一结点的游标等于插入结点所在数组中的位置的下标
}
template<typename T>
inline void CStaticList<T>::deleteArr(T data)
{
int del = selectElem(data);
if(del<0){//表示未找到
return;
}
int tempBody = m_list[1].cur;
//找到该结点的上一个结点,做删除操作
while (m_list[tempBody].cur != del)
{
tempBody = m_list[tempBody].cur;
}
//将被删除结点的游标直接给被删除结点的上一个结点
m_list[tempBody].cur = m_list[del].cur;
freeArr(del);
}
template<typename T>
inline void CStaticList<T>::freeArr(int k)
{
m_list[k].cur = m_list[0].cur;
m_list[0].cur = k;
}