线性表是一种简单的线性结构,特色是在非空的有限集合中,且第一个元素没有直接前驱元素,最后一个元素没有直接后继元素,其余元素都有惟一的前驱和后继元素。线性表有顺序存储结构和链式存储结构。node
指将线性表中的各个元素依次存放在一组地址连续的存储单元中,一般将这种方法存储的线性表称为顺序表。
逻辑相邻,物理存储地址也相邻数组
定义以下:markdown
/* ElemType类型根据实际状况而定,这里假设为int */ typedef int ElemType; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef int Status; /*线性结构使用顺序表的方式存储*/ //顺序表结构设计 typedef struct { ElemType *data; int length; }Sqlist; 复制代码
其中,ElemType表示数据元素类型,data用于存储线性表中的数据元素的首地址,length用来表示线性表中数据元素的个数,Sqlist是结构体类型名。定义一个顺序表代码:Sqlist L; 指向顺序表的指针:Sqlist *L;函数
Status InitList(Sqlist *L){ //为顺序表分配一个大小为MAXSIZE 的数组空间 L->data = malloc(sizeof(ElemType) * MAXSIZE); // 存储分配失败退出 if(!L->data) exit(ERROR); // 初始化,空表长度为0 L->length = 0; return OK; } 复制代码
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L); 操做结果:在L中第i个位置以前插入新的数据元素e,L的长度加1 */ Status ListInsert(Sqlist *L,int i,ElemType e){ // i值不合法判断 if((i<1) || (i>L->length+1)) return ERROR; // 存储空间已满 if(L->length == MAXSIZE) return ERROR; // 插入数据不在表尾,则先移动出空余位置 if(i <= L->length){ for(int j = L->length-1; j>=i-1;j--){ //插入位置以及以后的位置后移动1位 L->data[j+1] = L->data[j]; } } // 将新元素e 放入第i个位置上 L->data[i-1] = e; // 长度+1; ++L->length; return OK; } 复制代码
Status GetElem(Sqlist L,int i, ElemType *e){ //判断i值是否合理, 若不合理,返回ERROR if(i<1 || i > L.length) return ERROR; //data[i-1]单元存储第i个数据元素. *e = L.data[i-1]; return OK; } 复制代码
int ListEmpty(Sqlist L){ if(L.length == 0) { return OK; } return ERROR; } 复制代码
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 操做结果: 删除L的第i个数据元素,L的长度减1 */ Status ListDelete(Sqlist *L,int i){ //线性表为空 if(L->length == 0) return ERROR; //i值不合法判断 if((i<1) || (i>L->length+1)) return ERROR; for(int j = i; j < L->length;j++){ //被删除元素以后的元素向前移动 L->data[j-1] = L->data[j]; } //表长度-1; L->length --; return OK; } 复制代码
*清空顺序表性能
/* 初始条件:顺序线性表L已存在。操做结果:将L重置为空表 */ Status ClearList(Sqlist *L) { L->length=0; return OK; } 复制代码
Status TraverseList(Sqlist L) { int i; for(i=0;i<L.length;i++) printf("%d\n",L.data[i]); printf("\n"); return OK; } 复制代码
/* 初始条件:顺序线性表L已存在 */ /* 操做结果:返回L中第1个与e知足关系的数据元素的位序。 */ /* 若这样的数据元素不存在,则返回值为0 */ int LocateElem(Sqlist L,ElemType e) { int i; if (L.length==0) return 0; for(i=0;i<L.length;i++) { if (L.data[i]==e) break; } if(i>=L.length) return 0; return i+1; } 复制代码
在解决实际问题时,有时并不适合采用线性表的顺序存储结构,例如两个一元多项式的相加、相乘,这就须要另外一种存储结构——链式存储。它是采用一组任意的连续或非连续存储单元存储线性表的元素。为了表示每一个元素ai与其直接后继ai+1的逻辑关系,链式存储不只须要存储元素自己,还要存储一个指向其直接后继元素的地址。这种存储结构被称之为结点(node)。存储元素的叫数据域,存储地址的叫指针域。结点元素的逻辑顺序称之为线性链表或单链表。spa
链式存储最大的特色就是不连续的,因此每一个数据与数据之间的关系是经过指针域来进行链接的。设计
由于第一个结点没有直接前驱结点,所以须要一个头指针L指向它。为了方便操做放在第一个元素结点以前一个结点称之为头结点,头指针变成指向头结点,其数据域能够存放如线表长度等信息,而指针域则存放第一个元素结点的地址信息。若该链表为空,则头结点指针域为空。 最后一个元素没有直接后继元素,因此将其指针域设置为“Null”空。指针
(图片来自逻辑教育) code
便于首元结点处理和空表和⾮空表的统一处理orm
用C语言描述以下:
typedef struct Node{ ElemType data; struct Node *next; }Node; typedef struct Node * LinkList; 复制代码
Status InitList(LinkList *L) { //产生头结点,并使用L指向此头结点 *L = (LinkList)malloc(sizeof(Node)); //存储空间分配失败 if(*L == NULL) return ERROR; //将头结点的指针域置空 (*L)->next = NULL; return OK; } 复制代码
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L); 操做结果:在L中第i个位置以后插入新的数据元素e,L的长度加1; */ Status ListInsert(LinkList *L,int i,ElemType e){ int j; LinkList p,s; p = *L; j = 1; //寻找第i-1个结点 while (p && j < i) { p = p-> next; ++j; } //第i个元素不存在 if(!p || j>i) return ERROR; //生成新结点s s = (LinkList)malloc(sizeof(Node)); //将e赋值给s的数值域 s->data = e; //将p的后继结点赋值给s的后继 s->next = p->next; //将s赋值给p的后继 p->next = s; return OK; } 复制代码
注意:须要先将p的next赋值给s的next,而后在将p的next指向s,若是操做反了,会形成p的next也就是图中的Hank老师丢失,形成野指针。
/* 初始条件: 顺序线性表L已存在,1≤i≤ListLength(L); 操做结果:用e返回L中第i个数据元素的值 */ Status GetElem(LinkList L,int i,ElemType *e){ // j: 计数. int j; // 声明结点p; LinkList p; // 将结点p 指向链表L的第一个结点; p = L->next; // j计算=1; j = 1; // p不为空,且计算j不等于i,则循环继续 while (p && j < i) { //p指向下一个结点 p = p->next; ++j; } // 若是p为空或者j>i,则返回error if(!p || j > i) return ERROR; // e = p所指的结点的data *e = p->data; return OK; } 复制代码
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 操做结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ Status ListDelete(LinkList *L,int i,ElemType *e) { int j; LinkList p,q; p = (*L)->next; j = 1; // 查找第i-1个结点,p指向该结点 while (p->next && j< (i-1)) { p = p->next; ++j; } // 当i>n 或者 i<1 时,删除位置不合理 if (!(p->next) || (j > i-1)) return ERROR; // q指向要删除的结点 q = p->next; // 将q的后继赋值给p的后继 p->next = q->next; // 将q结点中的数据给e *e = q->data; // 让系统回收此结点,释放内存; free(q); return OK; } 复制代码
Status ClearList(LinkList *L) { LinkList p,q; /* p指向第一个结点 */ p = (*L)->next; /* 没到表尾 */ while(p) { q=p->next; free(p); p=q; } /* 头结点指针域为空 */ (*L)->next=NULL; return OK; } 复制代码
/* 随机产生n个元素值,创建带表头结点的单链线性表L(前插法)*/ void CreateListHead(LinkList *L, int n){ LinkList p; // 创建1个带头结点的单链表 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; // 循环前插入随机数据 for(int i = 0; i < n;i++) { //生成新结点 p = (LinkList)malloc(sizeof(Node)); //i赋值给新结点的data p->data = i; // p->next = 头结点的L->next p->next = (*L)->next; //将结点P插入到头结点以后; (*L)->next = p; } } 复制代码
/* 随机产生n个元素值,创建带表头结点的单链线性表L(后插法)*/ void CreateListTail(LinkList *L, int n){ LinkList p,r; // 创建1个带头结点的单链表 *L = (LinkList)malloc(sizeof(Node)); // r指向尾部的结点 r = *L; for (int i=0; i<n; i++) { // 生成新结点 p = (Node *)malloc(sizeof(Node)); p->data = i; // 将表尾终端结点的指针指向新结点 r->next = p; // 将当前的新结点定义为表尾终端结点 r = p; } // 将尾指针的next = null r->next = NULL; } 复制代码