在数据结构中,有一种逻辑结构是线性结构,常用的存储结构便是顺序存储结构和链式存储结构,对应的名称便是顺序表和链表。这两种数据结构在操作系统内核中的应用是非常多的,比如内存管理中的动态分区分配策略,就是使用顺序表,Linux系统内核也有内核链表。
顺序表简介
可以通俗理解,顺序表就是数组,支持随机存取(随便访问哪个地方都可以,但有越界访问的风险)以及下标访问,对于数据结构中几乎所有的结构来说,我们的通常操作便是增、删、改、查,顺序表也不例外。但是在增加元素以及删除元素的时候,顺序表有个极其致命的缺点,那就是需要进行数据的移动,想象一下,把一个顺序表看作是一列火车,若火车的某一节车厢要换,那应该把这节车厢先去掉,然后把后面所有的车厢往前移动一格才能保持整列火车的连续性,当车厢数量较多的时候,移动起来就比较费劲了,需要对删除插入操作进行改进。
示意图如下:
改进方法
将每个数据都做一个标记,有效数据标记为True,脏数据(即已被删除的数据)标记为False,删除就只对删除的数据的标记做更改,当一节车厢要被替换的时候,就将它标记一下,不使用它,当有新的车厢到来也就是有数据要进行插入时,就可以用新车厢代替旧车厢,只需更改数据和标志位即可,不涉及到别的元素的运动,插入删除的整体效率便会提高。使用两个整形变量分别记录数据总个数、脏数据个数,当脏数据太多时(两者比值大于某个设定值),我们可以将整个顺序表进行内存整理,类似于OS中可变分区分配的紧凑技术。
示意图如下:
删除:
新增数据:
接下来看代码实现
头文件及结构体定义
#include<iostream>
#include<Windows.h>
#define MAXSIZE 1024
using namespace std;
typedef struct Sq {
int data;
bool flag;
}sq;
typedef struct SqList {
sq* elems;
int Trnum;//当前有效个数
int Fanum;//已被删除个数
}SL;
初始化
void init_SqList(SL*&s)
{
s = new SL;
s->elems = new sq[MAXSIZE];
if (!s->elems)
{
fprintf(stderr, "分配内存失败!\n");
exit(1);
}
s->Trnum = 0;
s->Fanum = 0;
}
**添加元素 **
//添加元素 末尾添加 默认插入方法
void Add_elems(SL*& s, int data)
{
if ((s->Fanum + s->Trnum) == MAXSIZE)
{
fprintf(stderr, "内存空间已满!\n");
return;
}
s->elems[s->Fanum + s->Trnum].data = data;
s->elems[s->Fanum + s->Trnum].flag = true;
s->Trnum++;
}
覆盖第一个已删除元素或者最末尾添加
void insert(SL*&s,int data)
{
if ((s->Trnum+s->Fanum)==MAXSIZE)
{
fprintf(stderr, "内存空间已满!\n");
return;
}
int i = 1;
while (i<MAXSIZE)
{
if (i>(s->Fanum+s->Trnum))
{
break;
}
else if (!s->elems[i-1].flag)
{
s->Fanum--;
break;
}
i++;
}
s->elems[i - 1].flag = true;
s->elems[i - 1].data = data;
s->Trnum++;
}
指定位置插入 插入到第loca个位置
void Loca_insert(SL*&s,int data,int loca)
{
if ((s->Fanum+s->Trnum)==MAXSIZE)
{
fprintf(stderr, "内存空间已满!\n");
return;
}
if ((loca<=0)||(loca>(s->Fanum+s->Trnum)))
{
fprintf(stderr, "插入位置不合法!\n");
return;
}
if (!s->elems[loca-1].flag)//插入位置恰好是已被标记删除
{
s->elems[loca - 1].data = data;
s->elems[loca - 1].flag = true;
s->Fanum--;
s->Trnum++;
}
else//普通插入情况 即在所有节点数范围内插入 不支持在这范围之外添加
{
for (int i = (s->Fanum+s->Trnum); i >=loca; i--)
{
s->elems[i].data = s->elems[i - 1].data;
s->elems[i].flag = s->elems[i - 1].flag;
}
s->elems[loca - 1].data = data;
s->elems[loca - 1].flag = true;
s->Trnum++;
}
}
默认删除 删除第一个值为data的 只通过改变标记来实现删除
void Delete_One(SL*&s,int data)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
for (int i = 0; i < (s->Fanum+s->Trnum); i++)
{
if (s->elems[i].data==data)
{
s->elems[i].flag = false;
s->Fanum++;
s->Trnum--;
break;
}
}
}
默认删除 删除所有值为data的 只通过改变标记来实现删除
void Delete_ALL(SL*& s, int data)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
for (int i = 0; i < (s->Fanum + s->Trnum); i++)
{
if (s->elems[i].data == data)
{
s->elems[i].flag = false;
s->Fanum++;
s->Trnum--;
}
}
}
删除指定位置的元素 只通过改变标记来实现删除
void Delete_loca(SL*&s,int loca)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
s->Trnum -= s->elems[loca - 1].flag ? 1 : 0;
s->Fanum += s->elems[loca - 1].flag ? 1 : 0;
s->elems[loca - 1].flag = false;
}
内存整理 将所有已删除节点进行覆盖并改变已删除节点数大小
void Tidying_up(SL*&s)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
int n = 0;
for (int i = 0; i < (s->Fanum+s->Trnum); i++)
{
if (!s->elems[i].flag)//计第i个元素之前的已标记删除的数的个数
{
n++;
}
else //将有效元素进行移动
{
s->elems[i - n].data = s->elems[i].data;
s->elems[i - n].flag = true;
}
}
s->Fanum = 0;
}
重载输出运算符
ostream&operator << (ostream&os,const SL&s)
{
os << "总数据量为:" << s.Fanum + s.Trnum << endl << "有效数据个数:" << s.Trnum << '\t' << "脏数据个数:" << s.Fanum << '\t' << endl;;
string A;
for (int i = 0; i < (s.Fanum+s.Trnum); i++)
{
A = s.elems[i].flag ? "有效数据" : "脏数据";
os << s.elems[i].data << "\t标记:" << A<<'\n';
}
os << endl;
return os;
}
完整代码
#include<iostream>
#include<Windows.h>
#define MAXSIZE 1024
using namespace std;
typedef struct Sq {
int data;
bool flag;
}sq;
typedef struct SqList {
sq* elems;
int Trnum;//当前有效个数
int Fanum;//已被删除个数
}SL;
//初始化
void init_SqList(SL*&s)
{
s = new SL;
s->elems = new sq[MAXSIZE];
if (!s->elems)
{
fprintf(stderr, "分配内存失败!\n");
exit(1);
}
s->Trnum = 0;
s->Fanum = 0;
}
//添加元素 末尾添加 默认插入方法
void Add_elems(SL*& s, int data)
{
if ((s->Fanum + s->Trnum) == MAXSIZE)
{
fprintf(stderr, "内存空间已满!\n");
return;
}
s->elems[s->Fanum + s->Trnum].data = data;
s->elems[s->Fanum + s->Trnum].flag = true;
s->Trnum++;
}
//覆盖第一个已删除元素或者最末尾添加
void insert(SL*&s,int data)
{
if ((s->Trnum+s->Fanum)==MAXSIZE)
{
fprintf(stderr, "内存空间已满!\n");
return;
}
int i = 1;
while (i<MAXSIZE)
{
if (i>(s->Fanum+s->Trnum))
{
break;
}
else if (!s->elems[i-1].flag)
{
s->Fanum--;
break;
}
i++;
}
s->elems[i - 1].flag = true;
s->elems[i - 1].data = data;
s->Trnum++;
}
//指定位置插入 插入到第loca个位置
void Loca_insert(SL*&s,int data,int loca)
{
if ((s->Fanum+s->Trnum)==MAXSIZE)
{
fprintf(stderr, "内存空间已满!\n");
return;
}
if ((loca<=0)||(loca>(s->Fanum+s->Trnum)))
{
fprintf(stderr, "插入位置不合法!\n");
return;
}
if (!s->elems[loca-1].flag)//插入位置恰好是已被标记删除
{
s->elems[loca - 1].data = data;
s->elems[loca - 1].flag = true;
s->Fanum--;
s->Trnum++;
}
else//普通插入情况 即在所有节点数范围内插入 不支持在这范围之外添加
{
for (int i = (s->Fanum+s->Trnum); i >=loca; i--)
{
s->elems[i].data = s->elems[i - 1].data;
s->elems[i].flag = s->elems[i - 1].flag;
}
s->elems[loca - 1].data = data;
s->elems[loca - 1].flag = true;
s->Trnum++;
}
}
//默认删除 删除第一个值为data的 只通过改变标记来实现删除
void Delete_One(SL*&s,int data)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
for (int i = 0; i < (s->Fanum+s->Trnum); i++)
{
if (s->elems[i].data==data)
{
s->elems[i].flag = false;
s->Fanum++;
s->Trnum--;
break;
}
}
}
//默认删除 删除所有值为data的 只通过改变标记来实现删除
void Delete_ALL(SL*& s, int data)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
for (int i = 0; i < (s->Fanum + s->Trnum); i++)
{
if (s->elems[i].data == data)
{
s->elems[i].flag = false;
s->Fanum++;
s->Trnum--;
}
}
}
//删除指定位置的元素 只通过改变标记来实现删除
void Delete_loca(SL*&s,int loca)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
s->Trnum -= s->elems[loca - 1].flag ? 1 : 0;
s->Fanum += s->elems[loca - 1].flag ? 1 : 0;
s->elems[loca - 1].flag = false;
}
//内存整理 将所有已删除节点进行覆盖并改变已删除节点数大小
void Tidying_up(SL*&s)
{
if (!s->Trnum)
{
fprintf(stderr, "可删除节点数为0!\n");
return;
}
int n = 0;
for (int i = 0; i < (s->Fanum+s->Trnum); i++)
{
if (!s->elems[i].flag)//计第i个元素之前的已标记删除的数的个数
{
n++;
}
else //将有效元素进行移动
{
s->elems[i - n].data = s->elems[i].data;
s->elems[i - n].flag = true;
}
}
s->Fanum = 0;
}
ostream&operator << (ostream&os,const SL&s)
{
os << "总数据量为:" << s.Fanum + s.Trnum << endl << "有效数据个数:" << s.Trnum << '\t' << "脏数据个数:" << s.Fanum << '\t' << endl;;
string A;
for (int i = 0; i < (s.Fanum+s.Trnum); i++)
{
A = s.elems[i].flag ? "有效数据" : "脏数据";
os << s.elems[i].data << "\t标记:" << A<<'\n';
}
os << endl;
return os;
}
int main(void) {
int data[] = { 50,98,96,43,56,87,82,30,12,25,45,44,46 };
int len = sizeof(data) / sizeof(data[0]);
SL* s = nullptr;
init_SqList(s);
for (int i = 0; i < len; i++)
{
Add_elems(s, data[i]);
}
cout << *s;
Loca_insert(s, 1024, 5);
cout << *s;
Delete_One(s, 50);
cout << *s;
insert(s, 66);
cout << *s;
Delete_loca(s, 9);
cout << *s;
for (int i = 1; i < 4; i++)
{
Loca_insert(s, 566, i * i);
}
cout << *s;
Delete_ALL(s, 566);
cout << *s;
Tidying_up(s);
cout << *s;
delete[]s->elems;
delete s;
system("pause");
return 0;
}