线性表之链式存储(单链表)
线性表的链式存储结构
链式存储结构的线性表(链表),将采用一组地址任意的存储单元存放信息表中的数据元素,他不会按照线性的逻辑顺序来保存数据,它需要在每一个数据元素中保存一个引用下一个数据引用(也叫指针)。
由于不必须按照顺序存储。链表的插入删除数据元素时比顺序线性表快的多,但是查到一个节点或者访问特定编号的节点则比顺序链表慢得多。
链表可以充分利用计算机内存空间,实现灵活的内存动态管理。但链表失去了数组随机存储的优点,同时链表多了指针域,增加了空间开销。
对于链式存储的线性表而言,它的每个节点都必须包含数据元素本身和一个引用指向下一个节点的引用。并且有一种结构存在两个引用,分别指向上一个节点和下一个节点,这种结构我们称其为双向链表。
单链表
单链表指每个节点只有一个指向其他节点的引用,并且该节点指向当前节点的下一节点。没有节点指向头节点,尾节点指向NULL。
对于单链表的节点添加,有两种方法: 头插法,尾插法。
头插法: 添加的新节点做为头节点,并让新添加的头节点指向原头节点。
尾插法: 添加的新节点做为尾节点,并让原尾节点指向新节点。因此该方法需要定义一个引用变量来保存链表最后一个元素。
头插法虽然简单,但生成的链表中节点的次序和输入的次序却相反。如果希望二者次序一致,则应该采用尾插法建立链表。
线性表的链式存储和顺序存储基本相同,因为它们都是线性表。只是底层实现不同而已。因此链式和顺序在性能上有所差异,顺序表再随机存取时性能很好,但插入删除时性能就不如链表。链表在删除,插入时性能很好,但随机存取的性能就不如顺序表。
线性表的实现性能分析:
空间性能:
顺序表的存储空间是静态分布的,需要一个长度固定的数组,因此总有部分数组元素被浪费。
链表的存储空间是动态分布的,因此不会有空间被浪费。但由于链表需要额外的空间来为每个节点保存指针,因此也要牺牲一部分空间。
时间性能:
顺序表中元素的逻辑顺序与物理存储顺序保持一致,而且支持随机存取。因此数据表在查找,读取时性能很好。
链表采用链式结构来保存表内元素,因此在插入删除元素时性能较好。
C语言实现单链表基本操作
#include<stdio.h>
#include<stdlib.h>
#define NULL 0
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#pragma warning(disable:4996)
typedef int Status;//Status是函数的类型,其值是函数结果状态代码
typedef int ElemType;
//----线性表的单链表存储结构----
typedef struct LNode {
ElemType data;
struct LNode* next;
}LNode,*LinkList;
Status InitList_L(LinkList& L);
//操作结果:构造一个空表
int ListEmpty(LinkList L);
//初始条件:链表存在
//操作结果:判断链表为空,若为空返回1,非空则返回0
Status DestroyList_L(LinkList& L);
//初始条件:链表存在
//操作结果:销毁链表
Status ClearList_L(LinkList& L);
//初始条件:链表存在
//操作结果:清空链表
void CreateList_L_head(LinkList& L, int n);
//操作结果:逆位序输入n个元素的值,建立带头结点的单链线性表L
void CreateList_L_tail(LinkList& L, int n);
//操作结果:顺位序输入n个元素的值,建立带头结点的单链线性表L
int Length_L(LinkList L);
//初始条件:链表存在
//操作结果:返回链表的长度
Status GetElem_L(LinkList L, int i, ElemType& e);
//初始条件:L为带头结点的单链表的头指针
//操作结果:当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
Status ListInsert_L(LinkList& L, int i, ElemType e);
//初始条件:L为带头结点的单链表的头指针
//操作结果:在带头结点的单链线性表L中第i个位置之前插入元素e
Status ListDelete_L(LinkList& L, int i, ElemType& e);
//初始条件:L为带头结点的单链表的头指针
//操作结果:在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
void ListPrint_L(LinkList L);
//初始条件:链表存在
//操作结果:按序打印链表
Status InitList_L(LinkList& L){
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
return OK;
}//InitList_L
int ListEmpty(LinkList L){
if (L->next)//非空
return 0;
else
return 1;
}//ListEmpty
Status DestroyList_L(LinkList& L){
LNode* p;
while (L){
p = L;
L = L->next;
free(p);
}
return OK;
}//DestroyList
Status ClearList_L(LinkList& L){
LNode* p, * q;
p = L->next;
while (p){
q = p->next;
free(p);
p = q;
}
L->next = NULL;
return OK;
}//ClearList
//头插法创建单链表
void CreateList_L_head(LinkList& L, int n){
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
for (int i = n; i > 0; i--){
LNode* p = (LNode*)malloc(sizeof(LNode));
printf("请输入线性表L1中的元素值:\n");
scanf("%d", &p->data);//输入元素值
p->next = L->next;//输入元素值
L->next = p;//插入到表头
}
}//CreateList_L
//尾插法创建单链表
void CreateList_L_tail(LinkList& L, int n){
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
LinkList s = L;
for (int i = n; i > 0; i--){
LNode* p = (LNode*)malloc(sizeof(LNode));
printf("请输入线性表L2的元素值:\n");
scanf("%d", &p->data);//输入元素值
p->next = NULL;
s->next = p;
s = p;
}
}//CreateList_L_tail
int Length_L(LinkList L){
int num = 0;
while (L->next){
L = L->next;
num++;
}
return num;
}//Length_L
Status GetElem_L(LinkList L, int i, ElemType& e){
LinkList p = L->next;//初始化,p指向第一个节点
int j = 1;//j为计数器
while (p && j < i){
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;//第i个元素不存在
e = p->data;//取第i个元素
return OK;
}//GetElem_L
Status ListInsert_L(LinkList& L, int i, ElemType e){
LinkList p = L;
int j = 0;
while (p && j < i - 1){
p = p->next; // 寻找第i-1个结点
++j;
}
if (!p || j > i - 1)
return ERROR;
LinkList s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}//ListInsert_L
Status ListDelete_L(LinkList& L, int i, ElemType& e){
LinkList p = L;
int j = 0;
while (p->next && j < i - 1){
p = p->next;
++j;
}
if (!(p->next) || j > i - 1)
return ERROR;
LinkList q = p->next;
p->next = q->next;
e = q->data;
free(q);
return OK;
}//ListDelete_L
void ListPrint_L(LinkList L){
while (L->next){
L = L->next;
printf("%d ", L->data);
}
printf("\n");
}//ListPrint_L
int main(){
LinkList L1;
if (InitList_L(L1))
printf("线性表L1创建成功!\n");
printf("头插法建立单链表:\n");
CreateList_L_head(L1, 5);
printf("链表L1长度为%d\n", Length_L(L1));
printf("遍历线性表L1:\n");
ListPrint_L(L1);
ElemType e1;
GetElem_L(L1, 3, e1);
if (GetElem_L(L1, 3, e1))
printf("线性表L1中的第3个元素是:%d\n", e1);
else
printf("查询错误!\n");
ElemType e11;
printf("请输入要向线性表L1中插入的元素:\n");
scanf("%d", &e11);
if (ListInsert_L(L1, 4, e11))
printf("插入成功!\n");
else
printf("插入失败!\n");
printf("再次遍历线性表L1:\n");
ListPrint_L(L1);
ElemType del1;
ListDelete_L(L1, 2, del1);
printf("删除L1中的元素为:%d\n", del1);
printf("再次遍历线性表L1:\n");
ListPrint_L(L1);
printf("删除该元素之后线性表L1的长度为:%d\n", Length_L(L1));
printf("清空线性表L1!\n");
ClearList_L(L1);
ListEmpty(L1);
if (ListEmpty(L1))
printf("线性表L1为空!\n");
else
printf("线性表L1非空!\n");
DestroyList_L(L1);
printf("线性表L1已销毁!\n");
LinkList L2;
if (InitList_L(L2))
printf("线性表L2创建成功!\n");
printf("尾插法建立单链表:\n");
CreateList_L_tail(L2, 5);
printf("链表长度为%d\n", Length_L(L2));
printf("遍历线性表L2:\n");
ListPrint_L(L2);
ElemType e2;
GetElem_L(L2, 6, e2);
if (GetElem_L(L2, 6, e2))
printf("线性表L1中的第6个元素是:%d\n", e2);
else
printf("查询错误!\n");
ElemType del2;
ListDelete_L(L2, 1, del2);
printf("删除L2中的元素为:%d\n", del2);
printf("删除该元素之后线性表L2的长度为:%d\n", Length_L(L2));
printf("清空线性表L2!\n");
ClearList_L(L2);
ListEmpty(L2);
if (ListEmpty(L2))
printf("线性表L2为空!\n");
else
printf("线性表L2非空!\n");
DestroyList_L(L2);
printf("线性表L2已销毁!\n");
return 0;
}