作为一种基础的数据结构可以生成其他类型的数据结构。通常由一连串节点组成,每个节点包含任意的实例数据,和指明上下节点的链接。
常规数组排列相关联项目的方式可能会与这些数据项目在存储设备上的顺序,数据在访问时往往要在不同的排列顺序中转换。而链表是一种自我指示数据类型,它包含一个指向相同类型数据的指针。链表允许删除和插入表上任意位置的数据但不允许随机存储。链表并不会按顺存储数据,而是在每一个节点里存到下一个节点的指针。
链表可以克服数组需要提前知道数据长度的缺点,且可以充分利用计算机内存空间,实现灵活的动态管理内存,也失去了数组随机读取数据的优点,同时增加了节点的指针域空间消耗较大。因此,链表常用与组织遍历较少,而添加、删除较多的数据。
数据域(data):表示数据元素内容的部分。
指针域(next):表示直接后继元素存储地址的部分。
节点:表示每个数据元素两部分信息组合在一起。
(1)线性表中的数据元素才存储单元的存放顺序与逻辑顺序不一定一致。
(2)在进行操作时只能由头指针进入链表,并通过每个节点的指针域向后依次扫描其余节点,因此寻找第一个指针的节点与寻找最后一个指针的节点所花费时间不同。
链表的最基本结构是每个节点的数据和指向下一个节点地址的指针,在最后一个节点保存一个特殊的结束标记,另外在一个位置保存第一个节点地址的指针。单链表每一个节点只有一个指针,在查找结点时要从第一个结点开始依次向下一个节点扫描,知道搜索到目标节点位置。
(1)公用存储空间
链表节点可以与其他数据公用存储空间,因此具可以存储无限多内容但是回受到处理器的限制,不需要提前分配内存,缺点是内容分散不方便调试
(2)独立存储空间
链表也可独立使用存储空间,可由数组类似结构实现,优点是可以获得唯一的编号方便调试,但是不能动态分配内存。不过可以通过在上面附加一层块状链表分配内存。
#include
#include
#include
//定义常量
typedef int ElemType;
#define TRUE 1
#define FALSE 0
typedef enum //枚举类型
{
ERROR = 0,
OK = 1
} Status;
//线性表的单链表存储结构
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
//操作结果:构造一个空的线性表L
void InitList(LinkList *L)
{
*L = (LinkList)malloc(sizeof(struct LNode));//产生节点头,并使L指向此节点
if(!*L)//分配内存失败
exit(OVERFLOW);
(*L)->next = NULL;//指针域为空
}
//初始条件:线性表L已存在
//操作结果:销毁线性表L
void DestoryList(LinkList *L)
{
LinkList q;
while(*L)
{
q = (*L)->next;
free(*L);
*L = q;
}
}
//初始条件:线性表L已存在
//操作结果:将L重置为空表
void ClearList(LinkList L)//不改变L
{
LinkList p, q;
p = L->next;//p指向第一个节点
while(p)//没到表尾
{
q = p->next;
free(p);
p = q;
}
L->next = NULL;//头节点指针域为空
}
//初始条件:线性表L已存在
//操作结果:若L表为空测返回TRUE,否侧返回FALSE
int ListEmpty(LinkList L)
{
return (L->next) ? FALSE : TRUE;
}
//初始条件:线性表L已存在
//操作结果:返回L中数据元素的个数
int LinkLength(LinkList L)
{
int i = 0;
LinkList p = L->next;
while(p)//表没到尾
{
i++;
p = p->next;
}
return i;
}
//L为带头节点的单链表的头指针
//当第i个元素存在时,其赋值给e并返回OK,否则返回ERROR
Status GetElem(LinkList L, int i, ElemType *e)
{
Status flag = OK;
int j = 1;//j为计数器
LinkList p = L->next;//p指向第一个节点
while(p&&j<i)//顺指针向后查找,直到p指向第i个元素或p为空
{
p = p->next;
j++;
}
if(!p||j>i)
flag = ERROR;//第i个元素不在
else
*e = p->data;//取第i个元素
return flag;
}
//初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
//操作结果:返回L中第1个与e满足关系compare()的数据元素的位序
//若这样的数据元素不存在,测返回值为0
int LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType))
{
int i = 0;
int flag = 0;
LinkList p = L->next;
while(p)
{
i++;
if(compare(p->data, e))//找到这样的元素
{
flag = i;
break;
}
p = p->next;
}
return flag;
}
//初始条件:线性表L已存在
//操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回他的前驱,返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE
Status PriorElem(LinkList L, ElemType cur_e, ElemType *pre_e)
{
Status flag = ERROR;
LinkList q, p = L->next; //p指向第一个结点
while(p->next)//p所指结点有后继
{
q = p->next;//q为p的后继
if(q->data==cur_e)
{
*pre_e = p->data;
flag = OK;
break;
}
p = q;//p向后移
}
return flag;
}
//初始条件:线性表L已存在
//操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回他的后继,返回OK;否则操作失败,next_e无定义,返回INFEASIBLE
Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e)
{
Status flag = ERROR;
LinkList p = L->next; // p指向第一个结点
while (p->next) // p所指结点有后继
{
if (p->data == cur_e)
{
*next_e = p->next->data;
flag = OK;
break;
}
p = p->next; // p向后移
}
return flag;
}
//在带头结点的单链线性表L中第i个位置之前插入元素e
Status ListInsert(LinkList L, int i, ElemType e)
{
Status flag = OK;
int j = 0;
LinkList p = L, s;
while (p&&j<i-1)//寻找第i-1个结点
{
p = p->next;
j++;
}
if(!p||j>i-1)
flag = ERROR;//i小于1或大于表长
else
{
s = (LinkList)malloc(sizeof(struct LNode));//生成新结点
s->data = e;//插入L中
s->next = p->next;
p->next = s;
}
return flag;
}
//在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
Status ListDele(LinkList L, int i, ElemType *e)
{
Status flag = OK;
int j = 0;
LinkList p = L, q;
while (p->next && j < i - 1) //寻找第i-1个结点,并令指针指向其前驱
{
p = p->next;
j++;
}
if (!p->next || j > i - 1)//删除位置不合理
flag = ERROR;
else
{
q = p->next;//删除并释放节点
p->next = q->next;
*e = q->data;
free(q);
}
return flag;
}
//初始条件:线性表L已存在
//操作结果:一次对L的每个数据调用函数vi()
void ListTraverse(LinkList L, void(*vi)(ElemType))
{
LinkList p = L->next;
while (p)
{
vi(p->data);
p = p->next;
}
printf("\n");
}
//初始条件:线性表L已存在。打印链表的data域
void ListPrint(LinkList L)
{
LinkList p = L->next;
while(p)
{
printf("%d", p->data);
p = p->next;
}
printf("\n");
}
//插入排序
void ListSort(LinkList L)
{
LinkList first, p, q; //为原链表剩下用于直接插入排序的节点头指针
LinkList t; //临时指针变量:插入节点
first = L->next; //为原链表剩下用于直接插入排序
L->next = NULL;//只含有一个节点的链表的
while(first!=NULL)
{
for (t = first, q = L;((q!= NULL) && (q->data < t->data));p = q, q = q->next);//无序结点在有序链表中找插入位置
first = first->next;
p->next = t;
t->next = q;//完成插入动作
}
}