线性表(linear_list)是最常用且最简单的一种数据结构。
一个线性表是n个数据元素的有限序列,它有两种表示形式:顺序表示跟链式表示。
线性表的顺序表示和实现
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,这种数据结构称之为顺序表。
下面顺序表的一种实现
#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
typedef char ElemType;
typedef struct
{
ElemType *elem; //存储空间的基址
int length; //当前长度
int listsize; //当前分配的存储容量(以sizeof(ElemType)为单位)
}SqList;
//初始化顺序表
int InitList(SqList &L)
{
L.elem=(ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem) return OVERFLOW; //存储分配失败
L.length=0; //空表长度为0
L.listsize=LIST_INIT_SIZE; //初始存储容量
return OK;
}
//销毁顺序表
void DestroyList(SqList &L)
{
free(L.elem);
}
//在顺序表L中第i个位置之前插入新的元素e
int ListInsert(SqList &L,int i,ElemType e)
{
//i的合法值为1≤i≤L.length+1
if(i<1||i>L.length+1) return ERROR;
//当前存储空间已满,重新分配空间
if(L.length>=L.listsize)
{
ElemType *newbase=NULL;
newbase=(ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase) return OVERFLOW;
L.elem=newbase;
L.listsize+=LISTINCREMENT;
}
//p为插入位置
ElemType *p=&(L.elem[i-1]);
//插入位置及之后的元素右移 q为最后一个元素的位置
for(ElemType *q=(L.elem+L.length-1);q>=p;--q) *(q+1)=*q;
*p=e; //插入e
++L.length; //表长加1
}
//在顺序表L中删除第i个元素,并用e返回其值
int ListDelete(SqList &L,int i,ElemType &e)
{
//i的合法值为1≤i≤L.length
if(i<1||i>L.length) return ERROR;
ElemType *p=&(L.elem[i-1]); //p为被删除位置元素的位置
e=*p; //将删除元素的值赋给e
ElemType *q=L.elem+L.length-1; //q为表尾元素的位置
for(;p<=q;++p) *p=*(p+1);
--L.length;
return OK;
}
//判断顺序表是否为空表
bool ListEmpty(SqList &L)
{
return (L.length==0);
}
//求顺序表的长度
int ListLength(SqList &L)
{
return (L.length);
}
//输出顺序表
void DispList(SqList &L)
{
if(ListEmpty(L)) return;
for(int i=0;i<L.length;++i)
printf("%c",L.elem[i]);
printf("\n");
}
//获取顺序表中索引位置的元素值
bool GetElem(SqList &L,int i,ElemType &e)
{
if(i<1||i>L.length) return false;
e=L.elem[i-1];
return true;
}
//按元素值查找
int LocateElem(SqList &L,ElemType e)
{
int i=0;
//查找元素e
while(i<L.length&&L.elem[i++]!=e);
//未找到
if(i>L.length) return -1;
return i;
}
线性表的链式表示和实现
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。
为了表示元素之间的逻辑(先后)关系,对于任一数据元素,除了存储元素本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。
每个数据元素包含两个域:其中存储数据元素信息的域称为数据域,存储直接后继存储位置的域称为指针域。
下面是单链表(带头节点)的一种实现
#include <stdio.h>
#include <malloc.h>
typedef char ElemType;
//定义单链表节点类型
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LinkList;
//初始化
void InitList(LinkList *&L)
{
//创建头节点
L=(LinkList *)malloc(sizeof(LinkList));
L->next=NULL;
}
//销毁单链表
void DestroyList(LinkList *&L)
{
LNode *p=L;
LNode *q=p->next;
while(q!=NULL)
{
free(p);
p=q;
q=q->next;
}
free(p);
}
//判断是否为空表
bool ListEmpty(LinkList *L)
{
return (L->next==NULL);
}
//求单链表的长度
int ListLength(LinkList *L)
{
int i=0;
LNode *p=L;
while(p->next!=NULL)
{
i++;
p=p->next;
}
return i;
}
//输出单链表
void DispList(LinkList *L)
{
LNode *p=L->next;
while(p!=NULL)
{
printf("%c",p->data);
p=p->next;
}
printf("\n");
}
//按逻辑索引值找数据元素值 该值通过e返回
bool GetElem(LinkList *L,int i,ElemType &e)
{
int j=0;
LNode *p=L;
while(j<i&&p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
return false;
e=p->data;
return true;
}
//按元素值查找逻辑索引值
int LocateElem(LinkList *L,ElemType e)
{
LNode *p=L->next;
int i=1;
while(p!=NULL&&p->data!=e)
{
i++;
p=p->next;
}
if(p==NULL)
return 0;
return i;
}
//插入数据元素
bool ListInsert(LinkList *&L,int i,ElemType e)
{
int j=0;
LNode *p=L;
//找到索引为i-1的节点
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
return false;
LNode *node=(LNode *)malloc(sizeof(LNode));
node->data=e;
node->next=p->next;
p->next=node;
return true;
}
//删除数据元素
bool ListDelete(LinkList *&L,int i,ElemType &e)
{
int j=0;
LNode *p=L;
//找到索引为i-1的节点
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
//未找到第i-1个节点
if(p==NULL)
return false;
//不存在第i个节点
if(p->next==NULL)
return false;
//记录要删除的节点
LNode *q=p->next;
e=q->data;
p->next=q->next;
free(q);
return true;
}
双链表中的元素比单链表的元素多出一个指针域,这个指针域指向该元素的前驱。
双链表的实现跟单链表类似,只是多出了对前驱指针域的操作。
下面是双链表(带头节点)的一种实现(只给出与单链表不同的代码)
#include <stdio.h>
#include <malloc.h>
typedef char ElemType;
//定义双链表节点类型
typedef struct DNode
{
ElemType data;
struct DNode *prior;
struct DNode *next;
}DLinkList;
//初始化
void InitList(DLinkList *&L)
{
//创建头结点
L=(DLinkList *)malloc(sizeof(DLinkList));
L->prior=L->next=NULL;
}
//插入数据元素
bool ListInsert(DLinkList *&L,int i,ElemType e)
{
int j=0;
DNode *p=L;
//找到索引为i-1的节点
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
return false;
//将*node插入到*p之后
DNode *node=(DNode *)malloc(sizeof(DNode));
node->data=e;
node->next=p->next;
//如果*p不是最后一个节点
if(p->next!=NULL)
p->next->prior=node;
p->next=node;
node->prior=p;
return true;
}
//删除数据元素
bool ListDelete(DLinkList *&L,int i,ElemType &e)
{
int j=0;
DNode *p=L;
//找到索引为i-1的节点
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
//未找到第i-1个节点
if(p==NULL)
return false;
//不存在第i个节点
if(p->next==NULL)
return false;
//删除*q节点
DNode *q=p->next;
e=q->data;
p->next=q->next;
//如果*q不是最后一个节点
if(q->next!=NULL)
q->next->prior=q->prior;
free(q);
return true;
}
循环链表是另一种形式的链式存储的结构,它的特点是表中最后一个节点的指针域指向头节点,整个链表形成一个环。
下图是一个循环单链表
下面是循环单链表的实现,跟单链表不同的是要处理尾节点的指针域
#include <stdio.h>
#include <malloc.h>
typedef char ElemType;
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LinkList;
//初始化循环链表
void InitList(LinkList *&L)
{
//创建头结点
L=(LinkList *)malloc(sizeof(LinkList));
L->next=L;
}
//销毁循环链表
void DestoryList(LinkList *&L)
{
LNode *p=L;
LNode *q=L->next;
while(q!=L)
{
free(p);
p=q;
q=q->next;
}
free(p);
}
//判断是否为空表
bool ListEmpty(LinkList *L)
{
return (L->next==L);
}
//求表长
int ListLength(LinkList *L)
{
int i=0;
LNode *p=L->next;
while(p!=L)
{
i++;
p=p->next;
}
return i;
}
//输出线性表
void DispList(LinkList *L)
{
LNode *p=L->next;
while(p!=L)
{
printf("%c",p->data);
p=p->next;
}
printf("\n");
}
//找指定位置的元素
bool GetElem(LinkList *L,int i,ElemType &e)
{
LNode *p=L->next;
int j=0;
while(j<i-1&&p!=L)
{
j++;
p=p->next;
}
if(p==L)
return false;
e=p->data;
return true;
}
//查找元素位置
int LocateElem(LinkList *L,ElemType e)
{
LNode *p=L->next;
int i=1;
while(p!=L&&p->data!=e)
{
i++;
p=p->next;
}
if(p==L)
return 0;
return i;
}
//插入元素
bool ListInsert(LinkList *&L,int i,ElemType e)
{
LNode *p=L;
int j=0;
//找到索引为i-1的节点
while(p->next!=L&&j<i-1)
{
j++;
p=p->next;
}
if(j!=i-1)
return false;
LNode *node=(LNode *)malloc(sizeof(LNode));
node->data=e;
//*p是最后一个元素
if(p->next==L)
{
p->next=node;
node->next=L;
}
else
{
node->next=p->next;
p->next=node;
}
return true;
}
//删除元素
bool ListDelete(LinkList *&L,int i,ElemType &e)
{
LNode *p=L;
int j=0;
//找到索引为i-1的节点
while(p->next!=L&&j<i-1)
{
j++;
p=p->next;
}
if(j!=i-1)
return false;
//如果第i-1个节点是最后一个节点
if(p->next==L)
return false;
LNode *q=p->next;
e=q->data;
//如果第i个节点是最后一个节点
if(q->next==L)
{
p->next=L;
free(q);
}
else
{
p->next=q->next;
free(q);
}
return true;
}
下图是一个循环双链表,它的实现可以参照循环单链表跟普通双链表