文章目录
目录
线性表(linear list)是多个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串等。
理解学习本文章需用到的知识,指针,结构体,动态内存管理。
1顺序表的定义
顺序表有两种定义方法,动态顺序表和静态顺序表。这里使用结构体来定义。
1.1静态顺序表
//静态顺序表
typedef struct SeqList
{
SlDateType a[n]; //静态版本
int size; //表示数组中有多少个数据
int capacity; //表示数组实际能存数据的空间容量有多大
}SL;
1.2动态的顺序表
动态的顺序表只需将静态顺序表的数组改成指向数组的指针即可
//动态顺序表
typedef struct SeqList
{
SlDateType* a; //指向数组的空间的首地址,动态版本
int size; //表示数组中有多少个数据
int capacity; //表示数组实际能存数据的空间容量有多大
}SL;
1.3两种顺序表的优缺点
相对于动态顺序表,静态顺序表的缺点有两个。
1.数组空间给大了容易造成空间的浪费。
2.数组空间给小了不够用。
而动态顺序表可以根据空间是否满而对空间进行扩容。
所以本文给出动态顺序表的实现
2.顺序表的增删查改接口函数的实现
为了方便对顺序表的管理,我们将函数声明都写在头文件<SeqList.h>中,然后定义两个源文件
SeqList.cpp中实现接口函数
Text.cpp中完成对顺序表的测试和使用
2.1头文件
SeqList.h具体代码如下
#pragma once
#include<iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;
typedef int SlDateType;
#define n 1000
//动态顺序表
typedef struct SeqList
{
SlDateType* a; //指向数组的空间的首地址,动态版本
int size; //表示数组中有多少个数据
int capacity; //表示数组实际能存数据的空间容量有多大
}SL;
//打印顺序表
void SeqListPrint(SL* ps);
//初始化顺序表
void SeqListInit(SL*ps);
//释放动态申请的内存空间
void SeqListDestory(SL* ps);
//检查空间是否满了
void SeqListCheckCapacity(SL* ps);
//尾插入数据
void SeqListPushBack(SL* ps, SlDateType x);
//尾删除数据
void SeqListPopBack(SL* ps);
//头插入数据
void SeqListPushFront(SL* ps, SlDateType x);
//头删除数据
void SeqListPopFront(SL* ps);
//返回x数据的下标,若没有数据则返回-1
SlDateType SeqListPopFind(SL* ps, SlDateType x);
//在指定的pos下标位置插入数据
void SeqListInsert(SL* ps, int pos, SlDateType x);
//在指定的pos下标删除数据
void SeqListErase(SL* ps, int pos);
2.2接口函数
接口函数都写在源文件SeqList.cpp中
2.2.1初始化顺序表
void SeqListInit(SL* ps)
{
asssert(ps!=NULL);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
初始化的时候让a指向空,size和capacity为0,说明顺序表中没有元素
2.2.2检查增容顺序表
检查顺序表空间是否满,对空间已满的顺序表进行增容。
//顺序表的检查增容
void SeqListCheckCapacity(SL* ps)
{
assert(ps != NULL);
//如果容量与元素数量相同说明空间已经满了
if (ps->capacity == ps->size)
{
//最开始没有空间,或者空间满了这两种情况都要增容。
int newcapacity = 0;
if (ps->capacity == 0)
newcapacity = 4;
else
newcapacity = ps->capacity * 2;
SLDatatype* tmp = (SLDatatype*)realloc(ps->a, newcapacity * sizeof(SLDatatype));
if (tmp == NULL)//此时说明开辟空间失败(概率极低)
{
cout << "tmp realloc fail!";
//C语言
//printf("tmp realloc fail!");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
上述代码的功能就是检查增容。可根据注释进行理解
2.2.3释放动态申请的内存空间
当不使用这个顺序表的时候应该对其进行销毁,避免空间的浪费
void SeqListDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
2.2.4打印顺序表中的数据
为了方便对增删查改功能的测试,首先给出数据的打印。
void SeqListPrint(SL* ps)
{
//1.如果没有数据,则提示没有数据可以打印
if (ps->capacity != 0 && ps->size == 0)
cout << "顺序表中没有数据可以打印!";
//2.打印数据
for (int i = 0; i < ps->size; i++)
{
cout << ps->a[i] << " ";
//C语言
//printf("%d ", ps->a[i]);
}
cout << endl;
}
2.2.5三种方式插入,删除数据
增删查改是基本的功能,首先来说插入数据
2.2.5.1尾插数据
尾插数据,即每个数据都放在前一个数据的尾部,如依次插入1,2,3;
那么我们依次得到的序列应该为
1
1,2
1,2,3
//尾插入数据
void SeqListPushBack(SL* ps, SLDatatype x)
{
//1.检查增容,空间满了不能插入数据
SeqListCheckCapacity(ps);
//2.在数组a中的size位置插入数据,然后让size++
ps->a[ps->size] = x;
ps->size++;
}
我们在Test.c中对这一功能进行测试
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
cout << "尾插数据前:";
SeqListPrint(&s);
//尾插数据
SeqListPushBack(&s, 0);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
cout << "尾插数据后:";
SeqListPrint(&s);
}
int main()
{
TestSeqList();
}
运行结果
2.2.5.2尾删数据
与尾插一样,如 3,2,1 删除一个3,2。
//尾删除数据
void SeqListPopBack(SL* ps)
{
//顺序表中有数据的时候才能删除数据
assert(ps->size > 0);
//只是删除数据而不是减少容量
if (ps->size > 0)
ps->size--;
}
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
cout << "尾插数据前:";
SeqListPrint(&s);
//尾插数据
SeqListPushBack(&s, 0);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
cout << "尾插数据后:" << endl;
SeqListPrint(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
cout << "尾删除两个数据后:";
SeqListPrint(&s);
}
int main()
{
TestSeqList();
}
运行结果
2.2.5.3头插数据
在数据的头部插入数据,如依次插入1 2 3
1
2 1
3 2 1
//头插入数据
void SeqListPushFront(SL* ps, SLDatatype x)
{
assert(ps != NULL);
//1.检查增容
SeqListCheckCapacity(ps);
//2.插入数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
for (int i = 0; i < 5; i++)
{
SeqListPushFront(&s, i);
}
cout << "头插入数据后:";
SeqListPrint(&s);
SeqListDestory(&s);
}
int main()
{
TestSeqList();
return 0;
}
运行结果
2.2.5.4头删数据
//头删数据
void SeqListPopFront(SL* ps)
{
//顺序表中有数据才能删除
assert(ps->size > 0);
//直接向前覆盖即可
int begin = 0;
while (begin < ps->size)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
for (int i = 0; i < 5; i++)
{
SeqListPushFront(&s, i);
}
cout << "头插入数据后:";
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
cout << "头删除数据前:";
SeqListPrint(&s);
SeqListDestory(&s);
}
int main()
{
TestSeqList();
return 0;
}
运行结果
2.2.5.5在指定的pos位置处插入数据
在序列 1 2 3 4 5的下标2处插入数据
//在pos位置插入数据
void SeqListInsert(SL* ps, int pos, SLDatatype x)
{
//1.插入数据之前要检查增容
SeqListCheckCapacity(ps);
//2.不能输入非法的pos
if (pos > ps->size - 1)
{
cout << "插入数据的下标大于元素的个数";
exit(-1);
}
//2.指定位置插入数据
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
//3.让pos位置的元素为x
ps->a[pos] = x;
ps->size++;
}
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
for (int i = 0; i < 3; i++)
{
SeqListPushFront(&s, i);
}
cout << "头插入3个数据后:";
SeqListPrint(&s);
for (int i = 0; i < 3; i++)
{
SeqListPushBack(&s, i);
}
cout << "尾插入3个数据后:";
SeqListPrint(&s);
int pos = 0, x = 0;
cout << "请输入你想插入数据的下标" << endl;
cin >> pos;
cout << "请输入你想要插入的数据x:" << endl;
cin >> x;
SeqListInsert(&s, pos, x);
cout << "在指定位置x处插入数据后";
SeqListPrint(&s);
}
int main()
{
TestSeqList();
return 0;
}
运行结果
2.2.5.6删除指定位置的数据
//删除pos位置的数据
void SeqListErase(SL* ps, int pos)
{
//1.顺序表中有数据才能删除数据
assert(ps->size > 0);
//2.要删除的数据必须存在顺序表中(pos不能大于数据的个数)
if (pos > ps->size - 1)
{
cout << "此处没有数据!!!";
}
//3.删除数据
int begin = pos;
while (begin < ps->size - 1)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
测试代码
define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
for (int i = 0; i < 3; i++)
{
SeqListPushFront(&s, i);
}
cout << "头插入3个数据后:";
SeqListPrint(&s);
for (int i = 0; i < 3; i++)
{
SeqListPushBack(&s, i);
}
cout << "尾插入3个数据后:";
SeqListPrint(&s);
int pos = 0, x = 0;
cout << "请输入你想插入数据的下标" << endl;
cin >> pos;
cout << "请输入你想要插入的数据x:" << endl;
cin >> x;
SeqListInsert(&s, pos, x);
cout << "在指定位置x处插入数据后";
SeqListPrint(&s);
cout << "输入你想要删除数据的位置";
cin >> pos;
SeqListErase(&s, pos);
SeqListPrint(&s);
}
int main()
{
TestSeqList();
return 0;
}
运行结果
2.2.6查找数据
查找顺序表中是否有这个数据
//查找数据
SLDatatype SeqListFind(SL* ps, SLDatatype x)
{
for (int i = 0; i < ps->size - 1; i++)
{
if (x == ps->a[i])
return i;
}
return-1;
}
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"
void TestSeqList()
{
SL s;
SeqListInit(&s);
for (int i = 0; i < 3; i++)
{
SeqListPushFront(&s, i);
}
cout << "头插入3个数据后:";
SeqListPrint(&s);
for (int i = 10; i < 13; i++)
{
SeqListPushBack(&s, i);
}
cout << "尾插入3个数据后:";
SeqListPrint(&s);
cout << "请输入你想要查找的数据";
int x = 0;
cin >> x;
int ret = SeqListFind(&s, x);
if (ret >= 0)
cout << ret;
else
cout << -1;
}
int main()
{
TestSeqList();
return 0;
}
运行结果
3.顺序表的缺点
1.空间不够了需要增容,扩容有代价
2.为了减少扩容的代价,一般扩容扩大两倍,这样可能会导致浪费一定的空间
3.顺序表要求从头位置开始存储,如果我们从头部或者中间开始插入或者删除数据就要挪动数据,效率低
针对顺序表的缺陷,可以设计链表
4.完整代码
https://blog.csdn.net/yzcllzx?spm=1011.2124.3001.5343
与本文代码略有修改