线性表是具有相同特性的数据元素的一个有限序列
对应的逻辑结构图形:
从线性表的定义中可以看出它的特性:
(1)有穷性:一个线性表中的元素个数是有限的
(2)一致性:一个线性表中所有元素的性质相同,即数据类型相同
(3)序列性:各个元素的相对位置是线性的
(如下图所示)
那为什么要引进这个数据结构呢?那就不得不谈谈它的作用了。
线性表的作用体现在两个方面:
a. 当一个线性表实现后,程序员加油直接使用它来存放数据,即作为存放数据的容器
b.使用线性表的基本运算来完成更复杂的运算
线性表的顺序存储结构简称为顺序表
(如图为线性表到顺序表的映射)
需要注意的是顺序表采用数组进行实现,但是不是任意数组可以作为一个顺序表,二者运算是不同的
下图为顺序表的存储示意图
(1)结构体SqList定义
//数据元素
typedef int ElemType;
//结构体
typedef struct
{
ElemType data[MaxSize];
//数据长度
int length;
}SqList;
(2)建立顺序表
//建立顺序表
void CreateList(SqList*& L, ElemType a[], int n)
{
int i = 0, k = 0;
//记得一定要分配内存空间
L = (SqList*)malloc(sizeof(SqList));
while (i < n)
{
//将a[i]存储到L中
L->data[k] = a[i];
k++; i++;
}
L->length = k;
}
(3)初始化线性表
void InitList(SqList*& L)
{
L = (SqList*)malloc(sizeof(SqList));
L->length = 0; //置空线性表的长度为0
}
(4)销毁线性表
void DestroyList(SqList*& L)
{
free(L);//释放L所指的顺序表空间
}
(5)判断是否为空
bool ListEmpty(SqList* L)
{
return(L->length == 0);
}
(6)求线性表长度
int ListLength(SqList* L)
{
return (L->length);
}
(7)求表中某个值
bool GetElem(SqList* L, int i, ElemType& e)
{
if (i<1 || i > L->length)
return false;
e = L->data[i - 1];
return true; //成功找到元素返回true
}
(8)按照元素值查找
int LocateElem(SqList* L,ElemType e)
{
int i = 0;
while (i < L->length && L->data[i] != e)
i++;
if (i >= L->length)
return 0;
else
return i + 1; //返回逻辑序号
}
(9)输出线性表
void DisplayList(SqList* L)
{
for (int i = 0; i < L->length; i++)
printf("%d", L->data[i]);
printf("\n");
}
(10)完整代码
#include
using namespace std;
const int MaxSize = 1005;
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize];
int length;
}SqList;
//建立顺序表
void CreateList(SqList*& L, ElemType a[], int n)
{
int i = 0, k = 0;
//记得一定要分配内存空间
L = (SqList*)malloc(sizeof(SqList));
while (i < n)
{
L->data[k] = a[i];
k++; i++;
}
L->length = k;
}
//初始化线性表
void InitList(SqList*& L)
{
L = (SqList*)malloc(sizeof(SqList));
L->length = 0; //置空线性表的长度为0
}
void DestroyList(SqList*& L)
{
free(L);//释放L所指的顺序表空间
}
bool ListEmpty(SqList* L)
{
return(L->length == 0);
}
int ListLength(SqList* L)
{
return (L->length);
}
void DisplayList(SqList* L)
{
for (int i = 0; i < L->length; i++)
printf("%d", L->data[i]);
printf("\n");
}
bool GetElem(SqList* L, int i, ElemType& e)
{
if (i<1 || i > L->length)
return false;
e = L->data[i - 1];
return true; //成功找到元素返回true
}
int LocateElem(SqList* L,ElemType e)
{
int i = 0;
while (i < L->length && L->data[i] != e)
i++;
if (i >= L->length)
return 0;
else
return i + 1; //返回逻辑序号
}
bool ListInsert(SqList*& L, int i, ElemType e)
{
int j;
if (i < 1 || i >L->length + 1 || L->length == MaxSize)
return false;
i--;
for (j = L->length; j > i; j--)
L->data[j] = L->data[j - 1];
L->data[i] = e;
L->length++;
return true;
}
bool ListDelete(SqList*& L, int i, ElemType& e)
{
int j;
//特判是否符合
if (i < 1 || i>L->length)
return false;
i--;
for (int j = i; j < L->length - 1; j++)
L->data[j] = L->data[j + 1];
L->length--;
return true;
}
void delnodel(SqList*& L, ElemType x)
{
int k = 0;
for (int i = 0; i < L->length; i++)
if (L->data[i] != x)
{
L->data[k] = L->data[i];
k++;
}
L->length = k;
}
}
int main() {
SqList* L;
int a[10] = { 7,5,7,7,9,1,6,2,4,8 };
CreateList(L, a, 10);
DisplayList(L);
}
线性表的链式存储就是链表,每个链表存储点不仅包括数据域,还有指针域。
链表示意图如下:
(1)数据结构声明
typedef int ElemType;
typedef struct LinkNode
{
ElemType data; //存放元素值
struct LinkNode* next; //指向后继结点
}LinkNode;
建立单链表有两种方法:头插法和尾插法
(2)头插法
//建立单链表
void CreatListF(LinkNode*& L, ElemType a[], int n)
{
LinkNode* s;
//创建头结点
L = (LinkNode*)malloc(sizeof(LinkNode));
L->next = NULL; //头结点指针域置NULL
for (int i = 0; i < n; i++)
{
s = (LinkNode*)malloc(sizeof(LinkNode));//重新分配空间
s->data = a[i];
s->next = L->next;
L->next = s;
}
}
(3)尾插法
void CreatListR(LinkNode*& L, ElemType a[], int n)
{
LinkNode* s, * r;
//创建头结点
L = (LinkNode*)malloc(sizeof(LinkNode));
r = L; //r始终指向尾结点,初始时指向头结点
for (int i = 0; i < n; i++)
{
s = (LinkNode*)malloc(sizeof(LinkNode));//重新分配空间
s->data = a[i];
r->next = s;
r = s;
}
//尾结点的nextt域置为NULL
r->next = NULL;
}
(4)初始化
void InitList(LinkNode*& L)
{
L = (LinkNode*)malloc(sizeof(LinkNode));
L->next = NULL;
}
(5)销毁表
void DestroyList(LinkNode*& L)
{
LinkNode* pre = L, * p = L->next; //pre指向p的前驱结点
while (p != NULL)
{
free(pre);
pre = p;
p = pre->next;
}
free(pre); //循环结束时p为NULL,pre指向尾结点
}
(6)输出表
void DispList(LinkNode* L)
{
LinkNode* p = L->next;//指向头结点
while (p!=NULL)
{
printf("%d", p->data);
p = p->next;
}
printf("\n");
}
重点!!!
(7)链表的插入
bool ListInsert(LinkNode*& L, int i, ElemType e)
{
int j = 0;
LinkNode* p = L, * s;
if (i <= 0)return false;//首结点的次序是一
while (j < i - 1 && p != NULL)//找到第i-1个结点
{
j++;
p = p->next;
}
if (p == NULL)
return false;
else
{
s = (LinkNode*)malloc(sizeof(LinkNode));
//典中典
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
}
(8)删除某个元素
bool ListDelete(LinkNode*& L, int i, ElemType& e)
{
int j = 0;
LinkNode* p = L, * q;
if (i <= 0)return false;//头结点是不计入其中的
while (j < i - 1 && p != NULL)
{
j++;
p = p->next;
}
if (p == NULL)
return false;
else {
q = p->next;
if (q == NULL)
return false;
e = q->data;
p->next = q->next;
free(q);
return true;
}
}
最后介绍一下双链表
(1)建立双链表
typedef int ElemType;
// 定义DlinkNode结构体
struct DlinkNode {
int data; // 数据域
DlinkNode* prior; // 前驱指针
DlinkNode* next; // 后继指针
};
同样的,双链表的建立也有头插法和尾插法
(2)头插法
// 定义CreateListF函数
void CreateListF(DlinkNode*& L, ElemType a[], int n)
{
DlinkNode* s;
L = (DlinkNode*)malloc(sizeof(DlinkNode)); // 创建头结点
L->prior = L->next = NULL; // 先后指针域置NULL
for (int i = 0; i < n; i++) // 循环创建数据结点
{
s = (DlinkNode*)malloc(sizeof(DlinkNode)); // 创建数据结点s
s->data = a[i];
s->next = L->next; // 将S插到头结点之后
if (L->next != NULL)
L->next->prior = s;
L->next = s;
s->prior = L;
}
}
(3)尾插法
void CreateListR(DlinkNode*& L, ElemType a[], int n)
{
DlinkNode* s,*r;
L = (DlinkNode*)malloc(sizeof(DlinkNode)); // 创建头结点
r = L; //r始终指向尾结点,开始时指向头结点
for (int i = 0; i < n; i++) // 循环创建数据结点
{
s = (DlinkNode*)malloc(sizeof(DlinkNode)); // 创建数据结点s
s->data = a[i];
r->next = s; s->prior = r; //将s结点插入到r结点之后
r = s; //r指向尾结点
}
r->next = NULL;
}
线性表是一种基础且重要的数据结构,常见的线性表有三种实现方式:顺序表、单链表和双链表。
本文对这三种线性表的实现方式及特点做一个简要总结:
一、顺序表顺序表是将逻辑顺序上相邻的数据元素存储在物理位置上也相邻的存储单元中,通常使用数组来实现。- 特点:定位直接,通过下标操作即可快速访问任一位置的节点;实现简单。
缺点:插入删除需要移动大量元素,效率低;存储空间固定,扩容不灵活。
二、单链表单链表通过链式存储结构实现,每个节点除存储数据外,还储存下一个节点的地址信息。
-特点:存储灵活,可动态扩展,插入删除简单,效率高。-
缺点:访问任一节点需要从头结点遍历,无法直接定位
三、双链表双链表相比单链表,每个节点增加一个指向前驱节点的指针,实现双向链表。-
特点:可双向遍历链表,操作更灵活。-
缺点:每个节点存储空间略大于单链表。
综上,三种线性表各有特点,使用需根据具体场景需求选择合适的数据结构。顺序表操作简单,链表存储灵活,双链表可以双向访问。开发时需要权衡效率与实现难度,选择最优实现。