1. 定义
线性表是由同一类型的数据元素构成的有序序列的线性结构。
2.实现
2.1 顺序存储:在内存中用地址连续的一块存储空间顺序存放线性表中的各个元素,用一维数组表示顺序存储的数据区域。
MAXSIZE: 定义的数组的容量,线性表中数据从Data[0]开始存放。
Last : 记录线性表中最后一个元素的位置(数组下标)。表空时Last = -1。
struct LNode: 为了体现数据组织的整体性,将数组Data和Last封装成结构作为顺序表的类型。
typedef PtrToLNode List:由此,可以利用List定义线性表 List L;通过L可以访问相应内容 L->Data[i] , L ->Last
#define MAXSIZE 10
#define ERROR -1
// 线性表的顺序存储
typedef int ElementType;// Data数组中存放数据的类型
typedef int Position;
typedef struct LNode *PtrToLNode;
typedef PtrToLNode List; // List 定义为结构体指针
struct LNode {
ElementType Data[MAXSIZE];
Position Last;
};
基本操作:
- List MakeEmpty() : 初始化线性表
- void PrintData(List L) : 打印线性表Data中存储的所有内容
- bool Insert(List L,ElementType X, int i): 插入元素,i为制定位序
- bool Delete(List L,int i): 删除元素,i为指定位序
- Position Find(List L, ElementType X): 查找元素
- int Length:返回线性表长度
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAXSIZE 10
#define ERROR -1
// 线性表的顺序存储
typedef int ElementType;// Data数组中存放数据的类型
typedef int Position;
typedef struct LNode *PtrToLNode;
typedef PtrToLNode List; // List 定义为结构体指针
// 将数组Data和变量Last分装成一个结构体
struct LNode {
ElementType Data[MAXSIZE];
Position Last;
};
// 初始化线性表
List MakeEmpty() {
List L; // 利用List定义线性表L,构建一个空表
L = (List) malloc(sizeof(struct LNode)); // 动态分配表结构所需要的存储空间
L->Last = -1; // 将表中Last指针置-1
return L; // 返回结构体指针
}
// 打印线性表中的数据
void PrintData(List L) {
int i = 0;
if(L->Last==-1) printf("表为空");
while (i < L->Last + 1) {
printf("%d ", L->Data[i]);
i++;
}
printf("\n");
}
// 插入元素: 在L的制定位序i前插入一个新元素X,成功返回true,失败返回false(位序i元素的数组下标为i-1)
bool Insert(List L, ElementType X, int i) {
// 先判断插入是否合法 1) 表空间是否已满 2)插入位序的合法性
if (L->Last == MAXSIZE - 1 || i < 1 || i > L->Last + 2) {
printf("插入失败\n");
return false;
}
// i-1后的数都往后挪一位
else{
for (int j = L->Last; j >= i - 1; j--)
L->Data[j + 1] = L->Data[j];
L->Data[i - 1] = X;
L->Last++;
return true;
}
}
// 删除元素: 给定位序i 删除数组中的i-1
bool Delete(List L,int i){
// 先判断删除合法性
if(i<1||i>L->Last){
printf("表中不存在位序为 %d 的元素,删除失败",i);
return false;
}
// 删除元素
else{
for(int j = i-1;j<=L->Last;j++)
L->Data[j-1] = L->Data[j];
L->Last --;
return true;
}
}
// 查找元素: 查找和X相等的元素, 返回数组下标
Position Find(List L, ElementType X){
Position i = 0;
while(i<=L->Last){
if(L->Data[i] == X) return i;
i++;
}
return ERROR;
}
// 返回列表长度
int Length(List L){
return L->Last+1;
}
int main() {
int findResult;
int len;
List list;
list = MakeEmpty();
Insert(list, 10, 1);
Insert(list, 20, 2);
Insert(list, 30, 3);
Insert(list, 40, 4);
Insert(list, 50, 5);
PrintData(list);
findResult = Find(list,30);
printf("%d\n",findResult);
Delete(list,1);
PrintData(list);
len = Length(list);
printf("%d\n",len);
}
2.2 链式存储:与顺序存储相比,它不需要用地址连续的存储单元来实现,通过一个指针来连接各个分散的结点形成链状结构。每个结点存放一个元素以及指向下一节点的指针。链表可分为带头结点的链表和不带头结点的链表。
完整代码(带头节点的链表)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ERROR -1
typedef int ElementType;// Data数组中存放数据的类型
typedef struct LNode *PtrToLNode;
typedef PtrToLNode Position; // 这里的位置是结点的地址
typedef PtrToLNode List; // List 定义为结构体指针
// 将数组Data和变量Last分装成一个结构体
struct LNode {
ElementType Data; // 保存当前元素
PtrToLNode Next; // 指向下一个结点的指针
};
List MakeEmpty() {
List L; // 利用List定义线性表L,构建一个空表
L = (List) malloc(sizeof(struct LNode)); // 动态分配表结构所需要的存储空间
L->Next = NULL; // 将表中Last指针置-1
return L; // 返回结构体指针
}
// 插入数据
bool Insert(List L, ElementType X, int index) {
Position tmp, pre;
int cnt = 0;
pre = L; // pre指向表头
while (pre && cnt < index - 1) {
pre = pre->Next;
cnt++;
}
if (pre == NULL || cnt != index - 1) {
printf("插入位置错误\n");
return ERROR;
} else {
tmp = (Position) malloc(sizeof(struct LNode));
tmp->Data = X;
tmp->Next = pre->Next;
pre->Next = tmp;
return true;
}
}
// 删除数据
bool Delete(List L, int index) {
Position tmp, pre;
int cnt = 0;
pre = L; // pre指向表头
// 查找位序为index-1的结点
while (pre && cnt < index - 1) {
pre = pre->Next;
cnt++;
}
if (pre == NULL || cnt != index - 1) {
printf("删除位置错误\n");
return ERROR;
} else {
tmp = pre->Next;
pre->Next = tmp->Next;
free(tmp);
return true;
}
}
// 返回线性表的长度
int Length(List L) {
int cnt = 0;
while (L) {
L = L->Next;
cnt++;
}
return cnt;
}
// 打印线性表中的数据
void PrintData(List L) {
// 表头不放数据, 跳过表头读取
while (L->Next) {
L = L->Next;
printf("%d", L->Data);
}
printf("\n");
}
int main() {
List list;
list = MakeEmpty();
printf("线性表长度为 %d \n", Length(list));
Insert(list, 1, 1);
PrintData(list);
Insert(list, 2, 2);
PrintData(list);
Insert(list, 4, 3);
PrintData(list);
printf("线性表长度为 %d \n", Length(list));
Delete(list, 1);
PrintData(list);
printf("线性表长度为 %d \n", Length(list));
}
3 顺序表 vs 链表
- 链表在随机访问元素时需要通过遍历来完成,而顺序表利用数组特性直接访问得到
- 顺序表在插入元素时需要移动后续所有元素,而链表只需要修改结点指向即可完成插入
总结: 当读取数据多于插入删除数据时,用顺序表;当插入删除数据多于读取时,用链表。