链式表示
线性表的链式存储结构的特点是用一组任意的得存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示ai和ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继存储位置的信息。这两部分信息组成ai的存储映像,称为结点(node)。
结点包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称作指针或链。n个结点链组成一个链表,即为线性表的链式存储结构。由于每个节点中只包含一个指针域,故又称线性链表或单链表。
元素定义
typedef int ElemType;
单链表定义
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList; // 带头结点线性链表
我们在单链表的第一个结点之前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息,也可以存储如线性表长度等类的附加信息,头结点的指针指向第一个元素结点的存储位置。
链式存储
在单链表中,任何两个元素的存储位置之间没有固定的联系。然后,每个元素的存储位置都包含在其直接前驱结点的信息之中,由此在单链表中,去得第i个数据元素必须从头指针出发寻找。因此单链表是非随机存取的存储结构,这种存储结构相对于顺序存储结构,具有空间利用合理,插入和删除时不需要移动等优点,因此在很多场合下,链式存储是线性表的首选存储结构。
基本操作
#include <stdio.h>
#include <stdlib.h>
#define FAILURE -1
#define SUCCESS 1
typedef int ElemType; // 元素定义
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList; // 带头结点
int compare(ElemType e1, ElemType e2){
// 比较e1和e2元素是否相同,具体实现和ElemType类型有关
if(e1 == e2)
return SUCCESS;
else
return FAILURE;
}
int visit(ElemType e){
// 访问e元素,具体实现和ElemType类型有关
printf("%d ", e);
return SUCCESS;
}
void InitList_L(LinkList *L){
// 初始化线性链表,L指向data为空的头结点
*L = (LinkList)malloc(sizeof(LNode));
if (!*L) exit(-1); //创建头结点失败
(*L)->next = NULL;
}
void DestroyList_L(LinkList *L){
// 销毁单链表, 不仅释放数据节点,也销毁头结点
LinkList p = *L; //头结点
LinkList np;
while(p){
np = p->next;
free(p);
p = np;
}
*L = NULL;
}
void ClearList_L(LinkList *L){
// 带头结点的链表,仅清除数据节点
LinkList p = (*L)->next;
LinkList np;
while(p){
np = p->next;
free(p);
p = np;
}
(*L)->next = NULL;
}
int ListEmpty_L(LinkList L){
if(L->next == NULL)
return SUCCESS;
else
return FAILURE;
}
int ListLength_L(LinkList L){
// 返回单链表的长度
int i = 0;
while(L->next){
i++;
L = L->next;
}
return i;
}
int GetElem_L(LinkList L, int i, ElemType *e){
// 用e返回线性表L中第i位置元素
// 其中1<=i<=L.length
int p = 1;
L = L->next;
while(L && p < i){
p++;
L = L->next;
}
if(!L || p > i) return FAILURE;
*e = L->data;
return SUCCESS;
}
int LocateElem_L(LinkList L, ElemType e, int (*compare)(ElemType, ElemType)){
// 从L中查找第一个与e元素满足compare函数关系的元素
// 如果不存在则返回0
int p = 1;
L = L->next;
while(L){
if(compare(L->data, e) > 0){ // 找到
return p;
} else{
L = L->next;
p++;
}
}
return FAILURE;
}
int PriorElem_L(LinkList L, ElemType cur_e, ElemType *pre_e){
// 若cur_e是L中元素且不是第一个,则用pre_e返回它的前驱,否则失败
LinkList pre, cur;
pre = L;
cur = L->next;
if(cur->data == cur_e) return FAILURE; //第一个位置
while(cur && cur->data != cur_e){
pre = cur;
cur = cur->next;
}
if(!cur) return FAILURE;
*pre_e = pre->data;
return SUCCESS;
}
int NextElem_L(LinkList L, ElemType cur_e, ElemType *next_e){
// cur_e是L中元素且不是最后一个,则用next_e返回它的后继元素
LinkList cur, next;
cur = L;
next = cur->next;
while(next && cur->data != cur_e){
cur = next;
next = next->next;
}
if(!next) return FAILURE;
*next_e = next->data;
return SUCCESS;
}
int InsertList_L(LinkList L, int i, ElemType e){
// 在带头结点的L中第i位置之前插入e
LNode *p = L; // p指向L头结点
int j=0;
while(p && j<i-1){
p = p->next;
j++;
}
if(!p || j > i-1) return FAILURE; // i值不合理
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return SUCCESS;
}
int DeleteList_L(LinkList L, int i, ElemType *e){
// 删除线性链表L中第i位置节点,并由e返回
LNode *p = L;
int j = 0;
while(p && j<i-1){
p = p->next;
j++;
}
if(!p || j>i-1) return FAILURE;
// 此时p指向第i-1位置节点
// q指向待删除节点
LNode *q = p->next;
// 移除节点
p->next = p->next->next;
// 将值返回
*e = q->data;
free(q);
return SUCCESS;
}
int ListTraverse_L(LinkList L, int (*visit)(ElemType)){
// 对L中每一个元素调用visit函数,一旦visit失败,则操作失败
L = L->next;
while(L){
if(visit(L->data) < 0) return FAILURE;
L = L->next;
}
return SUCCESS;
}
void MergeList_L(LinkList La, LinkList Lb, LinkList *Lc){
// 归并两个有序线性链表,要求结果也有序排列
LNode *pa = La->next, *pb = Lb->next;
LNode *pc;
*Lc = pc = La; // Lc以La头结点为头结点
while(pa && pb){
if(pa->data <= pb->data){
pc->next = pa;
pa = pa->next;
} else{
pc->next = pb;
pb = pb->next;
}
pc = pc->next;
}
// 归并剩余结点
pc->next = pa ? pa:pb;
free(Lb); // 释放Lb头结点
}
void difference(LinkList La, LinkList Lb){
// 计算带头结点的线性链表La和Lb的差集,并将结果保存在La链表中
LinkList pa = La;
while(pa->next){
LinkList pb = Lb->next;
while(pb && pb->data != pa->next->data) pb = pb->next;
if(!pb) pa = pa->next; // 没有在pb中找到
else { // 在pb中找到
LinkList tem = pa->next;
pa->next = pa->next->next;
free(tem);
}
}
}
void main(){
// 声明线性列表L
LinkList L;
// 初始化线性列表,令L指向头结点
printf("****************** init list *************\n");
InitList_L(&L);
// 头结点位置
printf("L position is: %p\n", L);
// 头结点中data值
printf("L data is: %d\n", L->data);
// 插入值
printf("*********** insert value ************\n");
int a[] = {1, 3, 4, 5, 10, 5, 7};
int i;
for(i=0;i<7;i++){
InsertList_L(L, i+1, a[i]);
}
printf("插入值后:\n");
ListTraverse_L(L, visit);
// get elem
printf("\n****************** get elem from L*************\n");
ElemType e1, e2;
if(GetElem_L(L, 0, &e1) > 0){
printf("get elem at index 0 success, reutrn value: %d\n", e1);
} else {
printf("get elem at index 0 failure\n");
}
if(GetElem_L(L, 4, &e2) > 0){
printf("get elem at index 4 success, return value: %d\n", e2);
} else {
printf("get elem at index 4 failure\n");
}
if(GetElem_L(L, 20, &e2) >0){
printf("get elem at index 20 success, return value: %d\n", e2);
} else {
printf("get elem at index 20 failure.\n");
}
// locate elem
printf("\n***************** locate elem *************** \n");
int index1, index2;
index1 = LocateElem_L(L, -10, compare);
index2 = LocateElem_L(L, 7, compare);
if(index1 > 0){
printf("locate -10 in L success, index at: %d\n", index1);
} else {
printf("locate -10 in L failure!\n");
}
if(index2 > 0){
printf("locate 7 in L success, index at: %d\n", index2);
} else {
printf("locate 7 in L failure!\n");
}
// prior elem
printf("\n*********** prior elem *************\n");
ElemType pre_e1, pre_e2;
if(PriorElem_L(L, 1, &pre_e1) > 0){
printf("find prior elem of 1 in L success, value is: %d\n",
pre_e1);
} else {
printf("find prior elem of 1 in L failure!\n");
}
if(PriorElem_L(L, 7, &pre_e2) > 0){
printf("find prior elem of 7 in L success, value is: %d\n",
pre_e2);
} else {
printf("find prior elem of 7 in L failure!\n");
}
// next elem
printf("\n************ next elem ************\n");
ElemType next_e1, next_e2;
if(NextElem_L(L, 1, &next_e1) > 0){
printf("find next elem of 1 in L success, valueu is: %d\n",
next_e1);
} else {
printf("find next elem of 1 in L failure!\n");
}
if(NextElem_L(L, 7, &next_e2) > 0){
printf("find next elem of 7 in L success, value is: %d\n",
next_e2);
} else {
printf("find next elem of 7 failure!\n");
}
// traverse
printf("\n***********traverse************\n");
if(ListTraverse_L(L, visit) > 0){
printf("traverse L success!\n");
} else {
printf("traverse L failure!\n");
}
// 删除一个元素
ElemType e;
printf("\n**************delete elem******************\n");
if(DeleteList_L(L, 1, &e)>0){
printf("delete success at index 1, deleted value is: %d\n", e);
} else {
printf("delete failure at index 1\n");
}
printf("now elem: ");
ListTraverse_L(L, visit);
printf("\n");
if(DeleteList_L(L, 0, &e)>0){
printf("delete success at index 0, deleted value is: %d\n", e);
} else {
printf("delete failure at index 0\n");
}
if(DeleteList_L(L, 6, &e)>0){
printf("delete success at index 6, deleted value is: %d\n", e);
} else {
printf("delete failure at index 6\n");
}
// 销毁
printf("********** destroy **************\n");
DestroyList_L(&L);
printf("L position is: %p\n", L);
// 归并两个顺序表
printf("\n********* start Merge ****************\n");
LinkList La, Lb, Lc;
InitList_L(&La);
InitList_L(&Lb);
InitList_L(&Lc);
int la[] = {2, 4, 5, 10, 22};
int lb[] = {1, 3, 8, 10, 12, 22, 34};
for(i=0;i<5;i++){
InsertList_L(La, i+1, la[i]);
}
for(i=0;i<7;i++){
InsertList_L(Lb, i+1, lb[i]);
}
printf("La is: ");
ListTraverse_L(La, visit);
printf("\nLb is: ");
ListTraverse_L(Lb, visit);
MergeList_L(La, Lb, &Lc);
printf("\nthe merge result Lc is: ");
ListTraverse_L(Lc, visit);
printf("\n");
// 求差集
LinkList Ld, Le;
InitList_L(&Ld);
InitList_L(&Le);
int ld[] = {5, 10, 20, 15, 25, 30};
int le[] = {5, 15, 35, 25, 30};
for(i=0;i<6;i++){
InsertList_L(Ld, i+1, ld[i]);
}
for(i=0;i<5;i++){
InsertList_L(Le, i+1, le[i]);
}
printf("\n************** compute difference*****\n");
printf("Ld is: ");
ListTraverse_L(Ld, visit);
printf("\n");
printf("Le is: ");
ListTraverse_L(Le, visit);
printf("\nthe result that value in Ld and not in Le is: ");
difference(Ld, Le);
ListTraverse_L(Ld, visit);
printf("\n");
}
运行结果
****************** init list *************
L position is: 0x24e6010
L data is: 0
*********** insert value ************
插入值后:
1 3 4 5 10 5 7
****************** get elem from L*************
get elem at index 0 failure
get elem at index 4 success, return value: 5
get elem at index 20 failure.
***************** locate elem ***************
locate -10 in L failure!
locate 7 in L success, index at: 7
*********** prior elem *************
find prior elem of 1 in L failure!
find prior elem of 7 in L success, value is: 5
************ next elem ************
find next elem of 1 in L success, valueu is: 3
find next elem of 7 failure!
***********traverse************
1 3 4 5 10 5 7 traverse L success!
**************delete elem******************
delete success at index 1, deleted value is: 1
now elem: 3 4 5 10 5 7
delete failure at index 0
delete success at index 6, deleted value is: 7
********** destroy **************
L position is: 0x7ffe356bc5f8
********* start Merge ****************
La is: 2 4 5 10 22
Lb is: 1 3 8 10 12 22 34
the merge result Lc is: 1 2 3 4 5 8 10 10 12 22 22 34
************** compute difference*****
Ld is: 5 10 20 15 25 30
Le is: 5 15 35 25 30
the result that value in Ld and not in Le is: 10 20