线性表的特点:
- 存在唯一的一个被称作“第一个”的数据元素;
- 存在唯一的一个被称作“最后一个”的数据元素;
- 除第一个之外,结构中的每个数据元素均只有一个前驱;
- 除最后一个之外,结构中的每个数据元素均只有一个后继。
顺序表
线性表的顺序表示是指,用一组地址连续的存储单元,依次存储线性表的数据元素,这种表示也称作线性表的顺序存储结构或顺序映像。通常称这种存储结构的线性表为顺序表
静态顺序表
数据元素储存空间大小固定的顺序表,称为静态顺序表,其储存空间一旦被确定就无法修改。下面是静态顺序表的实现
定义数据元素的结构体和顺序表的结构体
1 2 3 4 5 6 7 8 9 10 11
| #define MAX_LENGTH 100
typedef int ElemType;
typedef struct List{ ElemType data[MAX_LENGTH]; int length; } List;
|
初始化表
1 2 3 4 5 6
|
void initList(List *list) { list->length = 0; }
|
输出表中所有数据
1 2 3 4 5 6 7 8 9
|
void printList(List list) { for (int i = 0; i < list.length; i++) { ElemType elem = list.data[i]; printf("data = %d\n", elem); } }
|
插入数据元素到指定位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
bool listInsert(List *list, int index, ElemType elem) { if (index < 0 || index > list->length + 1) return false; if (list->length >= MAX_LENGTH) return false; for (int i = list->length; i >= index; i--) list->data[i] = list->data[i - 1]; list->data[index - 1] = elem; list->length++; return true; }
|
删除指定位置的数据元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
bool listDelete(List *list, int index, ElemType *elem) { if (index < 1 || index >= list->length) return false; *elem = list->data[index - 1]; for (int i = index; i < list->length; i++) list->data[i - 1] = list->data[i]; list->length--; return true; }
|
按值查找数据元素
1 2 3 4 5 6 7 8 9 10 11 12 13
|
int locateElem(List list, ElemType elem) { for (int i = 0; i < list.length; i++) if (list.data[i] == elem) return i + 1; return 0; }
|
动态顺序表
数据元素储存空间大小可以修改的顺序表,称为动态顺序表,其储存空间可以动态增加。下面是动态顺序表的实现
修改上面静态顺序表结构体,使用指针代替固定长度的数组,并添加一个size
参数,用于记录当前数据表中的储存空间占用的内存大小
如下代码,仅列出与静态顺序表的不同之处。相同之处不提
1 2 3 4 5 6 7 8 9 10 11 12
| #define INIT_LENGTH 100
typedef int ElemType;
typedef struct List { ElemType *data; int length; int MaxLength; } List;
|
初始化表
1 2 3 4 5 6 7 8
|
void initList(List *list) { list->length = 0; list->MaxLength = INIT_LENGTH; list->data = (ElemType*) malloc(sizeof(ElemType) * INIT_LENGTH); }
|
动态增加数据表储存空间
1 2 3 4 5 6 7 8 9
|
void expandList(List *list) { list->MaxLength += INIT_LENGTH; ElemType *newData = (ElemType*) realloc(list->data, sizeof(ElemType) * list->MaxLength); if (newData != NULL) list->data = newData; }
|
在插入数据时,检查储存空间是否已满,若已满则动态增加空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
bool listInsert(List *list, int index, ElemType elemType) { if (index < 1 || index > list->length + 1) return false; if (list->length >= list->MaxLength) expandList(list); for (int i = list->length; i >= index; i--) list->data[i] = list->data[i - 1]; list->data[index - 1] = elemType; list->length++; return true; }
|
链表
用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的,这种储存单元称为结点,每个结点包括两个域,其中存储数据元素信息的域称为数据域,存储直接后继存储位置的域称为指针域,指针域中存储的信息称作指针或链,n个结点通过指针链结成一个链表,即为线性表
根据链表结点所含指针个数、指针指向和指针连接方式,可将链表分为单链表、循环链表、双向链表、二叉链表、十字链表、邻接表、邻接多重表等
单链表
对首元结点、头结点、头指针三个容易混淆的概念加以说明
- 首元结点是指链表中存储第一个数据元素结点。
- 头结点是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息。
- 头指针是指向链表中第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。
定义结点和结点中数据的结构体
1 2 3 4 5 6 7 8 9 10 11 12
| #define INIT_LENGTH 100
typedef int ElemType;
typedef struct Node { ElemType data; struct Node *nextNode; } Node, *LinkList;
|
使用头结点
使用头结点时,LinkList
本身作为指针将指向头结点,其指针域指向首元结点,数据域不储存数据
输出链表中的数据
1 2 3 4 5 6 7 8 9 10 11
|
void printList(LinkList linkList) { Node *p = linkList; while (p->nextNode != NULL) { ElemType elemType = p->nextNode->data; printf("data = %d\n", elemType); p = p->nextNode; } }
|
初始化链表
1 2 3 4 5 6 7 8 9
|
void initList(LinkList *linkList) { (*linkList) = (Node*)malloc(sizeof(Node)); (*linkList)->nextNode = NULL; }
|
求链表的表长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
int Length(LinkList linkList) { int length = 0; Node *p = linkList; while (p->nextNode != NULL) { length++; p = p->nextNode; } return length; }
|
查找链表中指定序号代表的结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
Node* getElement(LinkList linkList, int i) { int j = 0; Node *p = linkList; while (p != NULL && j < i) { j++; p = p->nextNode; } return p; }
|
查找对应值的结点
1 2 3 4 5 6 7 8 9 10 11 12
|
Node* locateElement(LinkList linkList, ElemType elemType) { Node *p = linkList->nextNode; while (p != NULL && p->data != elemType) p = p->nextNode; return NULL; }
|
向链表中插入结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
bool listInsert(LinkList linkList, int index, ElemType elemType) { int j = 0; Node *p = linkList; while (p != NULL && j < index - 1) { j++; p = p->nextNode; } if (p == NULL) return false; Node *newNode = (Node*)malloc(sizeof(Node)); newNode->nextNode = p->nextNode; newNode->data = elemType; p->nextNode = newNode;
return true; }
|
从链表中删除结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
bool listDelete(LinkList linkList, int index, ElemType *elemType) {
Node *p = linkList; int j = 0;
while (p != NULL && j < index - 1) { j++; p = p->nextNode; }
if (p ==NULL || p->nextNode == NULL) return false;
(*elemType) = p->nextNode->data;
Node *temp = p->nextNode; p->nextNode = p->nextNode->nextNode;
free(temp);
return true; }
|
不使用头结点
在上面的代码中,Node
代表结点,LinkList
代表单链表;若使用头结点,则LinkList
本身作为指针将指向头结点,否则指向首元结点
输出链表中的数据
1 2 3 4 5 6 7 8 9 10 11
|
void printList(LinkList linkList) { Node *p = linkList; while (p != NULL) { ElemType elemType = p->data; printf("data = %d\n", elemType); p = p->nextNode; } }
|
初始化链表
1 2 3 4 5 6 7 8
|
void initList(LinkList *linkList) { (*linkList) = NULL; }
|
求链表的表长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
int Length(LinkList linkList) { if (linkList == NULL) return 0;
int length = 1; Node *p = linkList; while (p->nextNode != NULL) { length++; p = p->nextNode; } return length; }
|
查找链表中指定序号代表的结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
Node* getElement(LinkList linkList, int i) { int j = 1; Node *p = linkList; while (p != NULL && j < i) { j++; p = p->nextNode; } return p; }
|
查找对应值的结点
1 2 3 4 5 6 7 8 9 10 11 12
|
Node* locateElement(LinkList linkList, ElemType elemType) { Node *p = linkList; while (p != NULL && p->data != elemType) p = p->nextNode; return NULL; }
|
向链表中插入结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
bool listInsert(LinkList *linkList, int index, ElemType elemType) { if (index < 1 || index > Length(*linkList) + 1) return false;
Node *newNode = (Node*)malloc(sizeof(Node));
if (index == 1) { newNode->data = elemType; newNode->nextNode = (*linkList); (*linkList) = newNode; return true; }
int j = 1; Node *p = (*linkList); while (p != NULL && j < index - 1) { j++; p = p->nextNode; } if (p == NULL) return false; newNode->nextNode = p->nextNode; newNode->data = elemType; p->nextNode = newNode;
return true; }
|
从链表中删除结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
bool listDelete(LinkList *linkList, int index, ElemType *elemType) { if (index < 1 || index > Length(*linkList) + 1) return false;
Node *temp = NULL;
if (index == 1) { temp = (*linkList); (*linkList) = (*linkList)->nextNode; free(temp); return true; }
Node *p = (*linkList); int j = 1;
while (p != NULL && j < index - 1) { j++; p = p->nextNode; }
if (p ==NULL || p->nextNode == NULL) return false;
(*elemType) = p->nextNode->data;
temp = p->nextNode; p->nextNode = p->nextNode->nextNode;
free(temp);
return true; }
|
循环单链表
带头结点的循环单链表,其最后一个结点的指针不是NULL
,而是指向头结点, 从而使单链表在逻辑上形成一个环
双链表
双链表中的每个结点拥有两个指针,分别指向前驱结点和后继结点
1 2 3 4 5 6 7
|
typedef struct Node { ElemType data; struct Node *nextNode; struct Node *preNode; } Node, *LinkList;
|