概念
线性表(linear list) 是 数据结构 的一种,一个线性表是n个具有相同特性的数据元素的 有限序列 。 线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。(来自百度)
基本含义解析及特点
- 除第一个元素外,每一个元素都有一个直接前驱元素,除最后一个元素外,每一个元素都有一个直接后驱元素,如图:
- 数据元素是组成数据的基本单位,是数据集合的个体,在计算机中通常作为整体进行处理,也称结点或记录。数据项是有独立含义的数据最小单位,也称项或字段
- 数据对象是性质相同的数据元素的集合,是数据的一个子集
- 线性表由有限个元素组成,且由同类型数据元素组成,每一个数据元素都属于同一个数据对象
分类(根据底层存储空间)
具体应用及功能代码
- 顺序表
用一段地址连续的存储单元依次存储线性表的数据元素。顺序存储结构在逻辑结构和物理存储结构中都是依次序互相紧挨着。每个数据元素的地址=首元素的地址+一个该数据类型所占空间大小*数组下标。具体如图:
优点:内存空间连续,尾插尾删效率较高,支持随机访问(用下标操作访问)
缺点:插入或删除都伴随元素的移动,效率较低,顺序表长度固定,有时需要扩容
头文件及宏定义
#include <stdio.h>
#include <stdlib.h>
//返回真或返回假
#define TRUE 1
#define FALSE 0
//返回执行状态
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 //内存分配失败,用于exit语句
typedef int Status; //定义函数返回类型
typedef int ElemType; //定义元素类型
#define LIST_INIT_SIZE 100 //顺序表【初始空间分配量】
#define LISTINCREMENT 10 //顺序表空间分配【增量】
(1)创建顺序表及顺序表结构体定义:
typedef struct
{
ElemType *elem; //存储表元素的数组指针
int length; //表长度【实际】
int listsize; //数组尺寸【最大】
} SqList;
(2)顺序表初始化:
Status InitList(SqList *L)
{
L->elem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType)); //为顺序表分配内存空间
if (!L->elem) //检测是否内存溢出
exit(OVERFLOW);
L->length = 0; //将表长初始化为0(因为里面还没有元素)
L->listsize = LIST_INIT_SIZE; //设置顺序表最初的最大空间
return OK;
}
附:malloc函数用法:link
(3)打印输出整个顺序表
void printList(SqList L)
{
for (int i = 0; i < L.length; i++) //遍历整个表
printf("%d ", L.elem[i]);
printf("\n");
return;
}
(4)在顺序表中插入
Status ListInsert(SqList *L, int i, ElemType e)
{
if (i < 1 || i > L->length + 1) //检查i是否合法,小于1则i-1不能作为数组下标,大于length+1则数组越界
return ERROR;
if (L->length >= L->listsize) //检查顺序表是否已经最大,若最大,则进行下面操作来为顺序表增加内存,以便于继续进行插入操作
{
ElemType *newbase = (ElemType *)realloc(L->elem, (L->listsize + LISTINCREMENT) * sizeof(ElemType)); //新建一个数组指针,为其分配listsize+LISTINCREMENT的内存空间
if (!newbase) //检查内存是否分配成功
exit(OVERFLOW);
L->elem = newbase; //将表转移到新的内存空间里去
L->listsize += LISTINCREMENT; //更新空间最大值
}
ElemType j;
for (j = L->length - 1; j >= i - 1; j--) //将所要插入的位置之后的元素全部向后挪一位
L->elem[j + 1] = L->elem[j]; // 元素后移
L->elem[i - 1] = e; // 插入e
++L->length; //更新表长
return OK;
}
(5)在顺序表中删除
Status ListDelete(SqList *L, int i)
{
if (i < 1 || (i > L->length)) //检查i是否合法
return ERROR;
ElemType *p = &(L->elem[i - 1]); //将指针p定位到要删除的位置
ElemType *q;
q = L->elem + L->length - 1; //将指针q定位到顺序表的最后一位
for (++p; p <= q; ++p) //将要删除的元素的后面元素以此向前挪动(相当于覆盖掉要删除的元素)
*(p - 1) = *p;
--L->length; //更新表长
return OK;
}
(6)在顺序表中查找
Status GetElem(SqList *L, int i, ElemType &e)
{
ElemType *p;
p = L->elem + i - 1; //将指针p定位到要查找的元素位置
e = *p; //将此元素赋值给e
return OK;
}
(7)返回顺序表中元素个数
int ListLength(SqList *L)
{
return L->length;
}
- 链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。它不像顺序表那样连续存储元素,而是在每一个节点里存到下一个节点的指针(Pointer)。如图:
优点:内存空间不连续,头插头删效率高,没有空间限制,不会溢出,可以存储很多元素
缺点:不支持随机访问,查找效率低,需遍历查找
头文件及宏定义
#include <stdio.h>
#include <stdlib.h>
//返回真或返回假
#define TRUE 1
#define FALSE 0
//返回执行状态
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 //内存分配失败,用于exit语句
typedef int Status; //定义函数返回类型
typedef int ElemType; //定义元素类型
#define LIST_INIT_SIZE 100 //顺序表【初始空间分配量】
#define LISTINCREMENT 10 //顺序表空间分配【增量】
(1)创建链表与结点
typedef struct LNode
{
ElemType data; //结点的数据
struct LNode *next; //结点所指向的下一个结点的地址
} LNode, *LinkList;
(2)对链表进行初始化
LinkList initList()
{
LinkList L; //代表整个链表的结构体指针L(也就是表头L)
L = (LinkList)malloc(sizeof(LNode)); //为L分配内存,大小为一个结点
L->next = NULL; //表头L暂时指向空,因为才初始化这个链表,目前还没有元素存在
return L; //返回表头L
}
(3)在链表头部插入元素
Status insertHead(LinkList L, ElemType e)
{
LinkList node; //新建一个结点指针
node = (LinkList)malloc(sizeof(LNode)); //为该结点分配内存空间
node->data = e; //将该结点的数据赋值为e
node->next = L->next; //将这个结点指向L的下一个结点【注意:此时,表头L与结点同时指向相同结点】
L->next = node; //将表头指向node结点【注意:此时,表头指向node,node指向下一个结点,就相当于将node结点插到了表头后面,完成了插入操作】
return OK;
}
(4)查找元素的值
Status getElem(LinkList L, int i, ElemType &e)
{
LinkList p = L; //定义一个p指针代表整个链表
while (i > 0) //通过遍历指针,找到要查找的元素
{
p = p->next; //向后遍历
i--; //i持续自减直到找到元素所在的位置
}
e = p->data; //将所要寻找的元素的值赋值给e
return OK;
}
(5)删除元素
Status deleteElem(LinkList L, int i, ElemType &e)
{
LinkList p = L; //定义一个p指针代表整个链表
while (i > 1) //通过遍历指针,找到要查找的元素的前一个元素
{
p = p->next;
i--;
}
e = p->next->data; //将所要寻找的元素的值赋值给e
p->next = p->next->next; //将要删除的元素的前一个元素的指针直接指向要删除的元素的下一个元素,相当于没有指针指向要删除的元素,此时就删除了元素
return OK;
}
(6)查找链表长度
int listLength(LinkList L)
{
LinkList p = L; //定义一个p指针代表整个链表
int count = -1;
while (p != NULL) //遍历链表直到指针指向为空,说明此时已经遍历到最后一个元素
{
p = p->next;
count++; //每向后挪一位,计数器就加一
}
return count; //返回计数器的值
}
(7)摧毁链表
Status destroyList(LinkList L)
{
LinkList p = L; //定义一个p指针代表整个链表
LinkList q;
while (p)
{
q = p->next; //让q指向p的下一个结点
free(p); //释放p结点
p = q; //又让p继续指向下一个结点
}
L->next = NULL;
return OK;
}
(8)打印链表
Status printList(LinkList L)
{
LinkList p = L->next;
if (!p)
{
printf("Empty list.\n");
return OK;
}
printf("List:");
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return OK;
}