【背景】
考研复习到后期,因为专业课的需要开始对数据结构学科的复习,把之前的实验重新用C语言实现了一遍,其中线性表部分是复习的第一部分,编码过程中也发现之前的潜在问题,以前的编码利用C/C++混编,个人在纯C编程上一定程度上碰了壁,如指针与引用与取地址符号等问题在C语言上的区分,更加明确了C与C++之间的界限。而今天的主角是线性表。
线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储),但是把最后一个数据元素的尾指针指向了首位结点)。
【源码运行环境】
操作系统:MAC OS X 10.13.4
编译环境:CLion (C++ 14) cmake
【源码实现】
List.h 源码部分
// // Created by WellLee on 2018/10/27. // #ifndef LIST_LIST_H #define LIST_LIST_H #include <stdio.h> #include <stdlib.h> /* * 说明:define 定义一些常用的符号特性 * 作用:增加代码可读性 * Author: WellLee * Last Modified: 2018年10月26日23:27:51 */ #define OVERFLOW -1 #define TRUE 1 #define FALSE 0 #define SqListMAXSIZE 20 typedef int Status; typedef int Elem; /* * 结构:顺序线性表 * 作用:定义了顺序线性表的一些结构特性,封装了常用的线性表操作 * Author: WellLee * Last modified: 2018年10月26日23:26:36 */ typedef struct SequenceList{ Elem elements[SqListMAXSIZE]; int Cur_length; int Max_Length; }SqList; Status InitList(SqList *L); Status ListEmpty(SqList *L); Status ClearList(SqList *L); Status GetElem(SqList *L, int i, Elem *e); Status LocateElem(SqList *L, Elem e, int *l); Status SequenceListInsert(SqList *L, int i, Elem e); Status SequenceListDelete(SqList *L, int i, Elem *e); Status ListLength(SqList *L,int *length); void showElements(SqList *L); /* * 结构:链式线性表 * 作用:定义了链式线性表的一些结构特性,封装了常用的线性表操作 * Author: WellLee * Last modified: 2018年10月28日00:15:42 */ typedef struct Node{ Elem e; struct Node *next; }Node; typedef struct Node *Link; Status InitLinkList(Link *L); Status LinkListEmpty(Link L); void ShowLinkList(Link L); Status DestroyList(Link *L); Status LinkListLength(Link L,int *length); Status GetElemFromLinkList(Link L, int i,Elem *e); Status LocateElemFromLinkList(Link L,Elem e, int *i); Status LinkListInsert(Link *L, int i, Elem e); Status LinkListDelete(Link *L, int i, Elem *e); Status LinkListReverse(Link *L); #endif //LIST_LIST_H
SequenceList.c 源码部分:
// // Created by WellLee on 2018/10/26. // /* * 文件名:SequenceList.c * 作用:补充对顺序线性表的操作 * Author: WellLee * Last Modified: 2018年10月26日23:30:59 */ #include "List.h" #include "memory.h" /* * 方法名:InitList * 作用:对顺序线性表进行初始化操作 * Author: WellLee * Last Modified:2018年10月27日23:27:19 */ Status InitList(SqList *L) { int total; L->Cur_length = 0; L->Max_Length = SqListMAXSIZE; printf("请输入您要插入的元素个数(不超过20个)\n"); scanf("%d", &total); if(total < 0 || total > 20) { printf("数组溢出\n"); return OVERFLOW; } if(total == 0) { printf("There is nothing to do \n"); return FALSE; } for(int i = 0; i < total; i++) { printf("请输入第%d个元素: ",i+1); scanf("%d",&L->elements[i]); } L->Cur_length = total; return TRUE; } /* * 方法名:showElements * 作用:显示顺序线性表的元素 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ void showElements(SqList *L) { if(ListEmpty(L)) { printf("这是一张空表\n"); return; } printf("线性表元素如下:总元素为%d\n", L->Cur_length); for(int i = 0; i < L->Cur_length; i++) { printf("%d ",L->elements[i]); } printf("\n"); } /* * 方法名:ListEmpty * 作用:判断线性表是否为空 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status ListEmpty(SqList *L) { if(L->Cur_length == 0) { return TRUE; } return FALSE; } /* * 方法名:ClearList * 作用:清空顺序线性表 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status ClearList(SqList *L) { memset(L->elements,0, sizeof(L->elements)); L->Max_Length = 0; L->Cur_length = 0; return TRUE; } /* * 方法名:GetElem * 作用:获取顺序线性表指定位置的元素 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status GetElem(SqList *L, int i, Elem *e) { if(i >= L->Cur_length || i < 0) { return OVERFLOW; } Elem a = L->elements[i]; *e = a; printf("e = %d\n", *e); return TRUE; } /* * 方法名:LocateElem * 作用:定位元素在线性表的第一次出现位置 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status LocateElem(SqList *L, Elem e, int *l) { if(ListEmpty(L)) { return OVERFLOW; } for(int i = 0; i < L->Cur_length; i++) { if(e == L->elements[i]) { *l = i + 1; return TRUE; } } return FALSE; } /* * 方法名:SequenceListInsert * 作用:顺序线性表插入操作 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status SequenceListInsert(SqList *L, int i, Elem e) { if(i < 0 || L->Cur_length > (SqListMAXSIZE - 1)) { return OVERFLOW; } if(i > L->Cur_length - 1) { printf("当前线性表长度为:%d ,无法满足您的插入需求\n"); return FALSE; } for(int j = L->Cur_length - 1;j >= i; j--) { L->elements[j + 1] = L->elements[j]; } L->elements[i] = e; L->Cur_length += 1; return TRUE; } /* * 方法名:SequenceListDelete * 作用:删除顺序线性表中的元素 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status SequenceListDelete(SqList *L, int i, Elem *e) { if(ListEmpty(L) || i > L-> Cur_length - 1) { return OVERFLOW; } *e = L->elements[i]; for (int j = i; j < L->Cur_length - 1; j++) { L->elements[j] = L->elements[j+1]; } L->elements[L->Cur_length - 1] = 0; L->Cur_length -= 1; return TRUE; } /* * 方法名:ListLength * 作用:获取线性表的长度 * Author: WellLee * Last Modified:2018年10月27日23:28:13 */ Status ListLength(SqList *L,int *length) { *length = L->Cur_length; return TRUE; }
LinkList.c 源码如下:
// // Created by WellLee on 2018/10/28. // #include "List.h" #include "time.h" /* * 方法名:InitLinkList * 作用:初始化链式线性表 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status InitLinkList(Link *L) { Link p,r; int length; srand(time(0)); printf("请输入链表长度: "); scanf("%d",&length); *L = (Link)malloc(sizeof(Node)); r = *L; for (int i = 0; i < length; i++) { p = (Node *)malloc(sizeof(Node)); p->e = rand() % 100 + 1; r->next = p; r = p; } r->next = NULL; printf("链表创建完成\n"); } /* * 方法名:ShowLinkList * 作用:显示链式线性表中的元素 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ void ShowLinkList(Link L) { Link temp = L->next; if(!temp) { printf("这是一张空表\n"); return; } printf("链式线性表元素如下:\n"); while(temp != NULL) { printf("%d ",temp->e); temp = temp->next; } printf("\n"); } /* * 方法名:LinkListEmpty * 作用:判断链式线性表是否为空 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status LinkListEmpty(Link L) { if(L->next == NULL) { return TRUE; } return FALSE; } /* * 方法名:DestroyList * 作用:销毁链式线性表 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status DestroyList(Link *L) { Link temp = *L; Link freePoint; freePoint = temp; temp = temp->next; freePoint->next = NULL; while(temp != NULL) { freePoint = temp; temp = temp->next; printf("释放元素为%d 的节点内存\n",freePoint->e); free(freePoint); } return TRUE; } /* * 方法名:LinkListLength * 作用:获取链式线性表长度 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status LinkListLength(Link L,int *length) { Link temp = L->next; *length = 0; while(temp != NULL) { *length += 1; temp = temp->next; } } /* * 方法名:GetElemFromLinkList * 作用:从链式线性表中获取指定位置的元素 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status GetElemFromLinkList(Link L, int i, Elem *e) { int cur_length; LinkListLength(L, &cur_length); if(!L || i > cur_length || i <= 0){ printf("越界!\n"); return OVERFLOW; } Link temp = L->next; for(int j = 0; j < i - 1; j++) { temp = temp->next; } *e = temp->e; return TRUE; } /* * 方法名:LocateElemFromLinkList * 作用:定位元素在链式线性表中的位置 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status LocateElemFromLinkList(Link L,Elem e,int *i) { int cur_length= 0; LinkListLength(L, &cur_length); if(!L) { printf("越界\n"); return OVERFLOW; } Link temp = L->next; int flag = FALSE; int c; for(c = 0; c < *i; c++){ if(temp->e != e){ temp = temp->next; continue; } flag = TRUE; break; } if(flag) { *i = c + 1; } else{ *i = -1; } return TRUE; } /* * 方法名:LinkListInsert * 作用:向链式线性表中指定位置插入元素 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status LinkListInsert(Link *L, int i, Elem e) { Link temp = *L; Link p = *L; temp = temp->next; int length = 0; LinkListLength(*L, &length); if(i < 0 || i > length + 1) { printf("越界\n"); return OVERFLOW; } for(int c = 0; c < i - 1; c++) { p = temp; temp = temp->next; } Link node = (Link)malloc(sizeof(Node)); node->e = e; node->next = temp; p->next = node; return TRUE; } /* * 方法名:LinkListDelete * 作用:删除链式线性表中指定位置的元素 * Author: WellLee * Last Modified:2018年10月28日12:21:18 */ Status LinkListDelete(Link *L, int i, Elem *e) { Link temp = *L; Link p = *L; temp = temp->next; int length; LinkListLength(p,&length); if(i < 0 || i > length) { printf("越界\n"); return OVERFLOW; } for(int c = 0; c < i-1; c++) { p = temp; temp = temp->next; } Link freepoint; freepoint = temp; p->next = temp->next; *e = freepoint->e; free(freepoint); return TRUE; }
【分析/总结】
顺序线性表具有如下优点:
-
无需为表示表中元素之间的逻辑关系增加额外的存储空间;
-
考研快速地存储表中任一位置的元素;
顺序线性表具有以下缺点:
-
插入和删除操作需要移动大量元素;
-
当线性表长度变化较大时,难以确定存储空间的容量;
-
造成存储空间的碎片;
链式线性表具有以下优点:
-
插入和删除操作只需要改变指针指向,时间效率高;
-
不需要提前分配空间,随用随分配,灵活性高;
-
在内存空间分配上灵活性高;
链式线性表具有以下缺点:
-
查找效率慢;
-
需要为地址开辟额外的存储空间;
总结:
从功能需求上看若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。反之宜采用链式存储结构。
而从空间分配上看,当线性表元素变化较大或者根本不知道有多大的时候,宜采用单链表存储结构。反之宜采用顺序存储结构。
【参考文献】
《大话数据结构》.程杰.清华大学出版社