一.线性表的基本概念
1.1 线性表定义
线性表(List)是零个或多个数据元素的集合
线性表中的数据元素之间是有顺序的
线性表中的数据元素个数是有限的
线性表中的数据元素的类型必须相同
1.2 数学定义
线性表是具有相同类型的 n( ≥ 0)个数据元素的有限序列
(a1, a2, …, an)
ai是表项,n 是表长度。
1.3 性质
a0为线性表的第一个元素,只有一个后继
an为线性表的最后一个元素,只有一个前驱
除a0和an外的其它元素ai,既有前驱,又有后继
线性表能够逐项访问和顺序存取
1.4 线性表的操作
创建线性表
销毁线性表
清空线性表
将元素插入线性表
将元素从线性表中删除
获取线性表中某个位置的元素
获取线性表的长度
线性表在程序中表现为一种特殊的数据类型
线性表的操作在程序中的表现为一组函数:
#ifndef _MY_LIST_H_ #define _MY_LIST_H_ typedef void List; typedef void ListNode; //创建并且返回一个空的线性表 List* LinkList_Create(); //销毁一个线性表list void List_Destroy(List* list); //将一个线性表list中的所有元素清空, 线性表回到创建时的初始状态 void List_Clear(List* list); //返回一个线性表list中的所有元素个数 int List_Length(List* list); //向一个线性表list的pos位置处插入新元素node int List_Insert(List* list, ListNode* node, int pos); //获取一个线性表list的pos位置处的元素 ListNode* List_Get(List* list, int pos); //删除一个线性表list的pos位置处的元素 返回值为被删除的元素,NULL表示删除失败 ListNode* List_Delete(List* list, int pos); #endif
二.线性表的存储结构——顺序存储
2.1 顺序存储
基本概念:
设计与实现
插入元素算法 判断线性表是否合法 判断插入位置是否合法 把最后一个元素到插入位置的元素后移一个位置 将新元素插入 线性表长度加1 |
获取元素操作 判断线性表是否合法 判断位置是否合法 直接通过数组下标的方式获取元素 |
删除元素算法 判断线性表是否合法 判断删除位置是否合法 将元素取出 将删除位置后的元素分别向前移动一个位置 线性表长度减1 |
优点和缺点
优点:
无需为线性表中的逻辑关系增加额外的空间
可以快速的获取表中合法位置的元素
缺点:
插入和删除操作需要移动大量元素
当线性表长度变化较大时难以确定存储空间的容量
代码实现:
seqlist.h
#ifndef __MY_SEQLIST_H__ #define __MY_SEQLIST_H__ #define DLL_API __declspec(dllexport) //_declspec(dllexport):导出标志 typedef void SeqList; typedef void SeqListNode; // 创建线性表 DLL_API SeqList* SeqList_Create(int capacity); // 销毁线性表 DLL_API void SeqList_Destroy(SeqList *list); // 清空线性表 DLL_API void SeqList_Clear(SeqList *list); // 获得线性表的长度 DLL_API int SeqList_Length(SeqList *list); // 获得线性表的容量 DLL_API int SeqList_Capacity(SeqList *list); // 向线性表中插入一个元素 DLL_API int SeqList_Insert(SeqList *list, SeqListNode *node, int pos); // 获取线性表中某一个位置的元素 DLL_API SeqListNode* SeqList_Get(SeqList *list, int pos); // 删除线性表中某一个位置的元素 DLL_API SeqListNode* SeqList_Delete(SeqList *list, int pos); #endif
seqlist.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "seqlist.h" typedef struct _tag_SeqList { int capacity; int length; unsigned int *node; // unsigned int array[capacity] }TSeqList; // 创建线性表 SeqList* SeqList_Create(int capacity) { TSeqList *ret = NULL; if (capacity < 0) { return NULL; } ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(unsigned int)*capacity); if (ret == NULL) { return NULL; } memset(ret, 0, sizeof(TSeqList)+sizeof(unsigned int)*capacity); ret->node = (unsigned int *)(ret + 1); // ret 向后跳转sizeof(TSeqList) ret->capacity = capacity; ret->length = 0; return ret; } // 销毁线性表 void SeqList_Destroy(SeqList *list) { if (list != NULL) { free(list); } } // 清空线性表 void SeqList_Clear(SeqList *list) { TSeqList *tList = NULL; if (list == NULL) { return; } tList = (SeqList*)list; tList->length = 0; } // 获得线性表的长度 int SeqList_Length(SeqList *list) { TSeqList *tList = NULL; tList = (TSeqList*)list; if (tList == NULL) { return -1; } return tList->length; } // 获得线性表的容量 int SeqList_Capacity(SeqList *list) { TSeqList *tList = NULL; tList = (TSeqList*)list; if (tList == NULL) { return -1; } return tList->capacity; } // 向线性表中插入一个元素 DLL_API int SeqList_Insert(SeqList *list, SeqListNode *node, int pos) { int i = 0; TSeqList *tList = NULL; tList = (TSeqList*)list; // 保证传入的线性表和元素节点不能为NULL if (list == NULL || node == NULL) { return -1; } // 判断该线性表是否已满 if (tList->length >= tList->capacity) { return -2; } // 判断插入索引是否合法 if (pos < 0 || pos >= tList->capacity) { return -3; } // 若索引值大于线性表当前长度,则将元素插入到线性表的末尾 if (pos >= tList->length) { pos = tList->length; } // 插入算法 // 将pos位置后的元素移次向后移 for (i = tList->length; i > pos; i--) { // 更新后移元素的值 tList->node[i] = tList->node[i - 1]; } // 元素后移完毕后,将元素放到指定的位置 tList->node[pos] = (unsigned int)node; tList->length ++; return 0; } // 获取线性表中某一个位置的元素 SeqListNode* SeqList_Get(SeqList *list, int pos) { SeqListNode *ret = NULL; TSeqList *tList = NULL; tList = (TSeqList*)list; // 过滤非法参数 if (list == NULL || pos < 0 || pos >= tList->length) { return NULL; } ret = (SeqListNode*)tList->node[pos]; return ret; } // 删除线性表中某一个位置的元素 SeqListNode* SeqList_Delete(SeqList *list, int pos) { int i = 0; TSeqList *tList = NULL; SeqListNode *ret = NULL; tList = (TSeqList*)list; if (list == NULL || pos < 0 || pos >= tList->length) { return NULL; } ret = (SeqListNode*)tList->node[pos]; // 删除算法 for (i=pos+1; i<tList->length; i++) { tList->node[i - 1] = tList->node[i]; } tList->length--; return ret; }
test.c
#include <stdio.h> #include <stdlib.h> #include "seqlist.h" typedef struct _Teacher { char name[64]; int age; }Teacher; int main() { SeqList *list = NULL; int i = 0; Teacher t1, t2, t3; t1.age = 31; t2.age = 32; t3.age = 33; list = SeqList_Create(10); if (list == NULL) { printf("list create error"); } // 头插法 SeqList_Insert(list, (SeqListNode *)&t1, 0); SeqList_Insert(list, (SeqListNode *)&t2, 0); SeqList_Insert(list, (SeqListNode *)&t3, 0); // 循环遍历 for (i = 0; i < SeqList_Length(list); i++) { Teacher *tmp = (Teacher *)SeqList_Get(list, i); if (tmp != NULL) { printf("current age is %d ",tmp->age); } } // 循环删除 for (i = 0; i < SeqList_Length(list); i++) { SeqList_Delete(list, 0); } // 销毁线性表 SeqList_Destroy(list); system("pause"); return 0; }
运行结果:
current age is 33
current age is 32
current age is 31
三.线性表的存储结构——链式存储(单向链表)
链式存储定义:为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。
3.1.1 单向链表
表头结点
链表中的第一个结点,包含指向第一个数据元素的指针以及链表自身的一些信息
数据结点
链表中代表数据元素的结点,包含指向下一个数据元素的指针和数据元素的信息
尾结点
链表中的最后一个数据结点,其下一元素指针为空,表示无后继。
3.1.2 优点和缺点
优点:
无需一次性定制链表的容量
插入和删除操作无需移动数据元素
缺点:
数据元素必须保存后继元素的位置信息
获取指定数据的元素操作需要顺序访问之前的元素
代码实现:
linklist.h
#ifndef __MY_LINKLIST_H__ #define __MY_LINKLIST_H__ #define DLL_API __declspec(dllexport) //_declspec(dllexport):导出标志 typedef void LinkList; typedef struct _tag_LinkListNode { struct _tag_LinkListNode* next; // 当前节点需要指向下一个节点的地址 }LinkListNode; DLL_API LinkList* LinkList_Create(); DLL_API void LinkList_Destroy(LinkList *list); DLL_API void LinkList_Clear(LinkList *list); DLL_API int LinkList_Length(LinkList *list); DLL_API int LinkList_Insert(LinkList *list, LinkListNode *node, int pos); DLL_API LinkListNode* LinkList_Get(LinkList *list, int pos); DLL_API LinkListNode* ListList_Delete(LinkList *list, int pos); #endif
linklist.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "linklist.h" typedef struct _tag_LinkList { LinkListNode header; // 链表中需要包含一个头节点 int length; }TLinkList; // 创建链表 LinkList* LinkList_Create() { TLinkList *ret = (TLinkList*)malloc(sizeof(TLinkList)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(TLinkList)); ret->header.next = NULL; ret->length = 0; return ret; } // 销毁链表 void LinkList_Destroy(LinkList *list) { if (list != NULL) { free(list); } } // 清空链表 void LinkList_Clear(LinkList *list) { TLinkList *tList = NULL; if (list == NULL) return; tList = (TLinkList*)list; tList->header.next = NULL; tList->length = 0; } // 获取当前链表长度 int LinkList_Length(LinkList *list) { TLinkList *tList = NULL; if (list == NULL) return -1; tList = (TLinkList*)list; return tList->length; } // 向当前链表插入数据 int LinkList_Insert(LinkList *list, LinkListNode *node, int pos) { TLinkList *tList = NULL; LinkListNode *current = NULL; int i = 0; tList = (TLinkList*)list; // 过滤非法参数 if (list == NULL || pos < 0) { return -1; } /* * 插入要点 *1.让新插入的元素节点指向原有位置的元素节点 *2.修改原有位置的前一个元素节点的指向(更新指向为新插入的元素节点) */ // 准备环境让辅助指针变量 指向链表头节点 current = &tList->header; // 将当前链表的节点移动到待插入节点的前一个节点的位置(假如待插入的位置是3,则移动到2号位置) for (i = 0; i < pos && (current->next != NULL); i++) { current = current->next; // 将辅助节点运动到指定位置(待插入位置的前一个节点) } // 让新插入的元素节点指向原有位置的元素节点 node->next = current->next; current->next = node; tList->length++; return 0; } LinkListNode* LinkList_Get(LinkList *list, int pos) { TLinkList *tList = NULL; int i = 0; LinkListNode *current = NULL; LinkListNode *ret = NULL; tList = (TLinkList *)list; // 过滤非法参数 if (list == NULL || pos < 0 || pos >= tList->length) { return NULL; } current = &tList->header; // 将辅助指针变量运行到待获取元素的前一个节点 for (i = 0; i < pos && (current->next != NULL); i++) { current = current->next; } ret = current->next; return ret; } LinkListNode* ListList_Delete(LinkList *list, int pos) { TLinkList *tList = NULL; tList = (TLinkList *)list; LinkListNode *current = NULL; LinkListNode *ret = NULL; int i = 0; // 过滤非法参数 if (list == NULL || pos < 0 || pos >= tList->length) { return NULL; } // 移动辅助指针变量 current = &tList->header; for (i = 0; i < pos && (current->next != NULL); i++) { current = current->next; } ret = current->next; // 将待删除位置的前一个节点的指向连接到删除位置节点的指向 current->next = ret->next; // 将链表长度-1 tList->length--; return ret; }
test.c
#include <stdlib.h> #include <stdio.h> #include <string.h> #include "linklist.h" #pragma warning(disable:4996) typedef struct _Teacher { LinkListNode node; char name[64]; int age; }Teacher; int main() { Teacher t1, t2, t3; int i = 0; LinkList *list = NULL; t1.age = 31; t2.age = 32; t3.age = 33; strcpy(t1.name,"Tom"); strcpy(t2.name, "Jack"); strcpy(t3.name, "Mike"); list = LinkList_Create(); LinkList_Insert(list, (LinkListNode *)&t1, LinkList_Length(list)); // 在链表的末尾插入元素 LinkList_Insert(list, (LinkListNode *)&t2, LinkList_Length(list)); LinkList_Insert(list, (LinkListNode *)&t3, LinkList_Length(list)); // 循环获取 for (i = 0; i < LinkList_Length(list); i++) { Teacher *tmp = (Teacher *)LinkList_Get(list, i); if (tmp != NULL) { printf("current get person is %s ",tmp->name); } } while (LinkList_Length(list) > 0) { Teacher *tmp = (Teacher *)ListList_Delete(list, 0); if (tmp != NULL) { printf("current delete person is %s ",tmp->name); } } // 销毁当前链表 LinkList_Destroy(list); system("pause"); return 0; }
运行结果:
current get person is Tom
current get person is Jack
current get person is Mike
current delete person is Tom
current delete person is Jack
current delete person is Mike
四.线性表的存储结构——链式存储(循环链表)
4.1 基本概念
循环链表的定义:将单链表中最后一个数据元素的next指针指向第一个元素
循环链表拥有单链表的所有操作:
- 创建链表
- 销毁链表
- 获取链表长度
- 清空链表
- 获取第pos个元素操作
- 插入元素到位置pos
- 删除位置pos处的元素
在此基础上,循环链表增加了一个游标。
游标的定义:在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素。
循环链表的新操作:
- 获取当前游标指向的数据元素
- 将游标重置指向链表中的第一个数据元素
- 将游标移动指向到链表中的下一个数据元素
- 直接指定删除链表中的某个数据元素
4.2 优点和缺点
优点:
功能强了,循环链表只是在单链表的基础上做了一个加强。
循环链表可以完全取代单链表的使用
循环链表的Next和Current操作可以高效的遍历链表中的所有元素
缺点:
代码复杂度提高了
4.3 代码实现
circlelist.h
#ifndef __CIRCLE_LIST_H__ #define __CIRCLE_LIST_H__ typedef void CircleList; typedef struct _tag_CircleListNode { struct _tag_CircleListNode *next; }CircleListNode; // 创建循环链表 CircleList* CircleList_Create(); // 销毁循环链表 void CircleList_Destroy(CircleList* list); // 清空循环链表 void CircleList_Clear(CircleList* list); // 获取当前循环链表的长度 int CircleList_Length(CircleList* list); // 向指定位置插入数据元素 int CircleList_Insert(CircleList* list, CircleListNode* node, int pos); // 获取指定位置的数据元素 CircleListNode* CircleList_Get(CircleList* list, int pos); // 删除指定位置的数据元素 CircleListNode* CircleList_Delete(CircleList* list, int pos); // add // 删除指定位置的数据元素 CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node); // 将游标重新指向链表中的第一个数据元素 CircleListNode* CircleList_Reset(CircleList *list); // 获取当前游标指向的数据元素 CircleListNode* CircleList_Current(CircleList *list); // 将游标指向链表中的下一个数据元素 CircleListNode* CircleList_Next(CircleList* list); #endif
circlelist.c
#include <stdlib.h> #include "circlelist.h" typedef struct _tag_CircleList { CircleListNode header; // 头节点 CircleListNode* slider; // 游标,指向循环链表的业务节点,因此是一个指针 int length; }TCircleList; // 创建循环链表 CircleList* CircleList_Create() { TCircleList *ret = (TCircleList*)malloc(sizeof(TCircleList)); if (ret != NULL) { ret->length = 0; ret->header.next = NULL; ret->slider = NULL; // 建立游标 return ret; } return NULL; } // 销毁循环链表 void CircleList_Destroy(CircleList* list) { if (list != NULL) free(list); } // 清空循环链表 void CircleList_Clear(CircleList* list) { if (list != NULL) { TCircleList *tList = (TCircleList*)list; tList->length = 0; tList->header.next = NULL; tList->slider = NULL; } } // 获取当前循环链表的长度 int CircleList_Length(CircleList* list) { if (list != NULL) { TCircleList *tList = (TCircleList*)list; return tList->length; } return -1; } // 向指定位置插入数据元素 int CircleList_Insert(CircleList* list, CircleListNode* node, int pos) { TCircleList *tList = NULL; if (list == NULL || node == NULL || pos < 0) return -1; tList = (TCircleList*)list; int i = 0; // 1.将current移动到待插入节点的前一个位置 CircleListNode *current = (CircleListNode*)tList; for (i = 0; (i < pos) && (current->next != NULL); i++) { current = current->next; } // 2.将新插入的元素节点的next指向老的元素节点,将current的next指向新插入的元素节点 node->next = current->next; current->next = node; // 若第一次插入节点 if (tList->length == 0) { tList->slider = node; // 将游标指向首元素 } tList->length++; // 注意:循环链表有这样一个问题 // 若头插法(如果是头插法,还需要求出尾节点,让尾部节点指向新节点),否则尾结节保存的指针域将会发生错误 if (current == (CircleListNode*)tList) { CircleListNode *last = CircleList_Get(list, tList->length - 1); last->next = current->next; } return 1; } // 获取指定位置的数据元素 CircleListNode* CircleList_Get(CircleList* list, int pos) { TCircleList *tList = NULL; if (pos < 0 || list == NULL) return NULL; tList = (TCircleList*)list; CircleListNode *current = (CircleListNode*)tList; int i = 0; for (i = 0; i < pos; i++) { current = current->next; } return current->next; } // 删除指定位置的数据元素(待测试) CircleListNode* CircleList_Delete(CircleList* list, int pos) { TCircleList *tList = NULL; CircleListNode* ret = NULL; int i = 0; if (list == NULL || pos < 0) return NULL; tList = (TCircleList*)list; if (tList->length > 0) { // 1.将current移动到待删除元素的前一个位置 CircleListNode *current = (CircleListNode*)tList; for (i = 0; i < pos; i++) { current = current->next; } // 获取末尾元素 CircleListNode *last = NULL; last = CircleList_Get(list, CircleList_Length(list) - 1); // 2.将current->next指向待删除元素的后一个元素,若删除的是第一个元素,则还需要修改删除完之后第一个元素的指针域,将其指向尾结点 ret = current->next; // 缓存删除节点 TODO: 释放内存 current->next = ret->next; last->next = tList->header.next; // 更新末尾结点指向 tList->length--; // 更新游标 tList->slider = ret->next; // 若删除后链表长度为0 if (tList->length == 0) { tList->header.next = NULL; tList->slider = NULL; } } return ret; } // add // 删除指定位置的数据元素 CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node) { TCircleList *tList = NULL; CircleListNode* ret = NULL; int i = 0; if (list == NULL || node == NULL) return NULL; tList = (TCircleList*)list; CircleListNode *current = (CircleListNode*)tList; // 查找node在循环链表中的位置 for (i = 0; i < tList->length; i++) { if (current->next == node) { ret = current->next; break; } current = current->next; } // 如果找到ret,则根据i去删除 if (ret != NULL) { CircleList_Delete(tList, i); } return ret; } // 重置游标 (将游标重新指向链表中的第一个数据元素) CircleListNode* CircleList_Reset(CircleList *list) { TCircleList *tList = NULL; CircleListNode *ret = NULL; if (list == NULL) return NULL; tList = (TCircleList*)list; tList->slider = tList->header.next; ret = tList->slider; return ret; } // 获取当前游标指向的数据元素 CircleListNode* CircleList_Current(CircleList *list) { TCircleList *tList = NULL; CircleListNode *ret = NULL; if (list == NULL) return NULL; tList = (TCircleList*)list; ret = tList->slider; return ret; } // 将游标下移 并返回下移前的元素 CircleListNode* CircleList_Next(CircleList* list) { TCircleList *tList = NULL; CircleListNode *ret = NULL; if (list == NULL) return NULL; tList = (TCircleList*)list; if (tList->slider != NULL) { ret = tList->slider; tList->slider = ret->next; // 游标下移 } return ret; }
test.c
#include <stdlib.h> #include <stdio.h> #include "circlelist.h" struct Value { CircleListNode header; int v; }; int main() { int i = 0; CircleList *list = CircleList_Create(); struct Value v1; struct Value v2; struct Value v3; struct Value v4; struct Value v5; struct Value v6; struct Value v7; struct Value v8; v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4; v5.v = 5; v6.v = 6; v7.v = 7; v8.v = 8; CircleList_Insert(list, (CircleListNode*)&v1,CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list)); for (i = 0; i < CircleList_Length(list)*2; i++) { struct Value *pv = (struct Value*)CircleList_Get(list, i); printf("current GET value is: %d ",pv->v); } printf("-------------------------------- "); /*while (CircleList_Length(list) > 0) { struct Value *pv = (struct Value*)CircleList_Delete(list, 0); printf("current DEL value is: %d ",pv->v); }*/ if (CircleList_Length(list) > 0) { struct Value *pv = (struct Value*)CircleList_Delete(list, 0); printf("current DEL value is: %d ", pv->v); } printf("---------------- new list ---------------- "); for (i = 0; i < CircleList_Length(list)*2; i++) { struct Value *pv = (struct Value*)CircleList_Get(list, i); printf("current GET value is: %d ", pv->v); } printf("---------------- reset slider ---------------- "); // 重置游标 struct Value *pv_reset = (struct Value*)CircleList_Reset(list); printf("current RESET value is: %d ", pv_reset->v); printf("---------------- move slider ---------------- "); // 将游标下移 struct Value *pv_move = CircleList_Next(list); printf("current MOVE value is: %d ",pv_move->v); printf("---------------- current slider ---------------- "); // 获取当前游标指向的数据元素 struct Value *pv_cur = (struct Value*)CircleList_Current(list); printf("current CUR value is: %d ",pv_cur->v); printf("---------------- delete node ---------------- "); printf("-------delete before "); for (i = 0; i < CircleList_Length(list) * 2; i++) { struct Value *pv = (struct Value*)CircleList_Get(list, i); printf("current GET value is: %d ", pv->v); } printf(" "); struct Value *pv_delete = (struct Value*)CircleList_DeleteNode(list, &v3); printf("current DEL value is: %d ",pv_delete->v); printf("-------delete over "); for (i = 0; i < CircleList_Length(list) * 4; i++) { struct Value *pv = (struct Value*)CircleList_Get(list, i); printf("current GET value is: %d ", pv->v); } // 销毁该循环链表 CircleList_Destroy(list); system("pause"); return 0; }
运行结果:
current GET value is: 1
current GET value is: 2
current GET value is: 3
current GET value is: 4
current GET value is: 1
current GET value is: 2
current GET value is: 3
current GET value is: 4
--------------------------------
current DEL value is: 1
---------------- new list ----------------
current GET value is: 2
current GET value is: 3
current GET value is: 4
current GET value is: 2
current GET value is: 3
current GET value is: 4
---------------- reset slider ----------------
current RESET value is: 2
---------------- move slider ----------------
current MOVE value is: 2
---------------- current slider ----------------
current CUR value is: 3
---------------- delete node ----------------
-------delete before
current GET value is: 2
current GET value is: 3
current GET value is: 4
current GET value is: 2
current GET value is: 3
current GET value is: 4
current DEL value is: 3
-------delete over
current GET value is: 2
current GET value is: 4
current GET value is: 2
current GET value is: 4
current GET value is: 2
current GET value is: 4
current GET value is: 2
current GET value is: 4
请按任意键继续. . .
循环链表的典型应用——约瑟夫问题
n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出其顺序
Joseph.c
#include <stdio.h> #include <stdlib.h> #include "circlelist.h" typedef struct _Student{ CircleListNode header; int name; }Student; int main() { Student s1; Student s2; Student s3; Student s4; Student s5; Student s6; Student s7; Student s8; s1.name = 1; s2.name = 2; s3.name = 3; s4.name = 4; s5.name = 5; s6.name = 6; s7.name = 7; s8.name = 8; int i = 0; // 设n为8,m为3 CircleList *list = CircleList_Create(); if (list != NULL) { CircleList_Insert(list, (CircleListNode*)&s1, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s2, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s3, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s4, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s5, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s6, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s7, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&s8, CircleList_Length(list)); for (i = 0; i < CircleList_Length(list); i++) { Student *pStu = (Student*)CircleList_Next(list); printf("currnet name is: %d ",pStu->name); } printf(" "); // 重置游标 CircleList_Reset(list); while (CircleList_Length(list)>0) { Student *pStu = NULL; for (i = 0; i < 2; i++) { CircleList_Next(list); } pStu = (Student*)CircleList_Current(list); printf("last name is: %d ",pStu->name); CircleList_DeleteNode(list, (CircleListNode*)pStu); } // 销毁当前链表 CircleList_Destroy(list); } system("pause"); return 0; }
运行结果:
currnet name is: 1
currnet name is: 2
currnet name is: 3
currnet name is: 4
currnet name is: 5
currnet name is: 6
currnet name is: 7
currnet name is: 8
last name is: 3
last name is: 6
last name is: 1
last name is: 5
last name is: 2
last name is: 8
last name is: 4
last name is: 7
请按任意键继续. . .
五.线性表的存储结构——链式存储(双向链表)
5.1 基本概念
双向链表的定义:在单链表的结点中增加一个指向其前驱的pre指针
双向链表拥有单链表的所有操作:
- 创建链表
- 销毁链表
- 获取链表长度
- 清空链表
- 获取第pos个元素操作
- 插入元素到位置pos
- 删除位置pos处的元素
同时,双向链表还拥有一些新的操作:
- 获取当前游标指向的数据元素
- 将游标重置指向链表中的第一个数据元素
- 将游标移动指向到链表中的下一个数据元素
- 将游标移动指向到链表中的上一个数据元素
- 直接指定删除链表中的某个数据元素
5.2 应用场景
单链表的结点都只有一个指向下一个结点的指针
单链表的数据元素无法直接访问其前驱元素
逆序访问单链表中的元素是极其耗时的操作!
len = LinkList_Length(list); for (i=len-1; len>=0; i++) //O(n) { LinkListNode *p = LinkList_Get(list, i); //O(n) //访问数据元素p中的元素 // }
5.3 优点和缺点
优点
双向链表在单链表的基础上增加了指向前驱的指针
功能上双向链表可以完全取代单链表的使用
双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素
缺点
代码复杂
5.4 代码实现
dlinklist.h
#ifndef __MY_DLINKLIST_H__ #define __MY_DLINKLIST_H__ typedef void DLinkList; typedef struct _tag_DLinkListNode { struct _tag_DLinkListNode *next; struct _tag_DLinkListNode *pre; }DLinkListNode; // 创建链表 DLinkList* DLinkList_Create(); // 销毁链表 void DLinkList_Destroy(DLinkList* list); // 清空链表 void DLinkList_Clear(DLinkList* list); // 获取当前链表长度 int DLinkList_Length(DLinkList* list); // 向链表插入元素 int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos); // 获取当前链表元素 DLinkListNode* DLinkList_Get(DLinkList* list, int pos); // 删除指定位置元素 DLinkListNode* DLinkList_Delete(DLinkList* list, int pos); // add // 删除节点 DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node); // 重置游标 DLinkListNode* DLinkList_Reset(DLinkList* list); // 获取当前游标指向元素 DLinkListNode* DLinkList_Current(DLinkList* list); // 将游标后移 DLinkListNode* DLinkList_Next(DLinkList* list); // 游标前移 DLinkListNode* DLinkList_Pre(DLinkList* list); #endif
dlinklist.c
#include <stdlib.h> #include "dlinklist.h" typedef struct _tag_DLinkList { DLinkListNode header; DLinkListNode *slider; int length; }TDLinkList; // 创建链表 DLinkList* DLinkList_Create() { TDLinkList *ret = (TDLinkList*)malloc(sizeof(TDLinkList)); if (ret != NULL) { ret->length = 0; ret->header.next = NULL; ret->header.pre = NULL; ret->slider = NULL; } return ret; } // 销毁链表 void DLinkList_Destroy(DLinkList* list) { if (list != NULL) free(list); } // 清空链表 void DLinkList_Clear(DLinkList* list) { TDLinkList *tList = NULL; if (list != NULL) { tList = (TDLinkList*)list; tList->length = 0; tList->slider = NULL; tList->header.next = NULL; tList->header.pre = NULL; } } // 获取当前链表长度 int DLinkList_Length(DLinkList* list) { TDLinkList *tList = NULL; if (list != NULL) { tList = (TDLinkList*)list; return tList->length; } return -1; } // 向链表插入元素 int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos) { TDLinkList *tList = NULL; DLinkListNode *current = NULL; DLinkListNode *next = NULL; int i = 0; if (list == NULL || node == NULL || pos < 0) return -1; tList = (TDLinkList*)list; current = (DLinkListNode*)tList; for (i = 0; (i < pos)&&(current->next!= NULL); i++) { current = current->next; } next = current->next; // 1.将current的next指向新插入的元素 // 2.将新插入元素的next指向原有位置的元素 // 3.更新原有位置和新插入元素的pre current->next = node; node->next = next; node->pre = current; if (next != NULL) next->pre = node; // 当链表插入第一个元素的时候处理游标 if (tList->length == 0) tList->slider = node; // 如果在0号位置插入,需要特殊处理(双向链表的第1个节点的前驱和最后一个节点的后驱都必须是NULL) if (current == (DLinkListNode*)tList) { node->pre = NULL; } tList->length++; return 1; } // 获取当前链表元素 DLinkListNode* DLinkList_Get(DLinkList* list, int pos) { TDLinkList *tList = NULL; DLinkListNode *ret = NULL; DLinkListNode *current = NULL; int i = 0; if (list == NULL || pos < 0) return NULL; tList = (TDLinkList*)list; current = (DLinkListNode*)tList; for (i = 0; i < pos; i++) { current = current->next; } ret = current->next; return ret; } // 删除指定位置元素 DLinkListNode* DLinkList_Delete(DLinkList* list, int pos) { TDLinkList *tList = NULL; DLinkListNode *current = NULL; DLinkListNode *ret = NULL; DLinkListNode *next = NULL; int i = 0; if (list == NULL || pos < 0) return NULL; tList = (TDLinkList*)list; current = (DLinkListNode*)tList; for (i = 0; i < pos; i++) { current = current->next; } ret = current->next; // 待删除的元素 next = ret->next; // 1.将current的next指向pos的下一个元素 // 2.修改pos所在元素的pre current->next = next; // 异常处理 if (next != NULL) { next->pre = current; if (current == (DLinkListNode*)tList) { next->pre = NULL; } } // 更新游标 if (tList->slider == ret) { tList->slider = next; } tList->length--; return ret; } // add // 删除节点 DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node) { TDLinkList *tList = NULL; DLinkListNode *ret = NULL; DLinkListNode *current = NULL; int i = 0; if (list == NULL || node == NULL) return NULL; tList = (TDLinkList*)list; current = (DLinkListNode*)tList; for (i = 0; i < tList->length; i++) { if (current->next == node) { ret = current->next; break; } current = current->next; } if (ret != NULL) { DLinkList_Delete(tList, i); } return ret; } // 重置游标 DLinkListNode* DLinkList_Reset(DLinkList* list) { TDLinkList *tList = NULL; DLinkListNode *ret = NULL; if (list == NULL) return NULL; tList = (TDLinkList*)list; tList->slider = tList->header.next; ret = tList->slider; return ret; } // 获取当前游标指向元素 DLinkListNode* DLinkList_Current(DLinkList* list) { DLinkListNode *ret = NULL; TDLinkList *tList = NULL; if (list == NULL) return NULL; tList = (TDLinkList*)list; ret = tList->slider; return ret; } // 将游标后移 DLinkListNode* DLinkList_Next(DLinkList* list) { DLinkListNode *ret = NULL; TDLinkList *tList = NULL; if (list == NULL) return NULL; tList = (TDLinkList*)list; if (tList->slider == NULL) return NULL; ret = tList->slider; tList->slider = ret->next; return ret; } // 游标前移 DLinkListNode* DLinkList_Pre(DLinkList* list) { DLinkListNode *ret = NULL; TDLinkList *tList = NULL; if (list == NULL) return NULL; tList = (TDLinkList*)list; if (tList->slider == NULL) return NULL; ret = tList->slider; tList->slider = ret->pre; return ret; }
test.c
#include <stdio.h> #include <stdlib.h> #include "dlinklist.h" typedef struct _Value { DLinkListNode node; int v; }Value; void printListContent(DLinkList *list) { int i = 0; for (i = 0; i < DLinkList_Length(list); i++) { Value *pv = (Value*)DLinkList_Get(list, i); printf("current GET value is: %d ", pv->v); } } int main() { Value v1; Value v2; Value v3; Value v4; Value v5; v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4; v5.v = 5; int i = 0; Value *pv = NULL; DLinkList *list = DLinkList_Create(); if (list != NULL) { DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list)); DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list)); DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list)); DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list)); DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list)); printListContent(list); printf(" "); // 删除末尾元素 DLinkList_Delete(list, DLinkList_Length(list) - 1); printListContent(list); printf(" "); // 删除第1个元素 DLinkList_Delete(list, 0); printListContent(list); printf(" "); // 将游标下移 pv = (Value *)DLinkList_Next(list); // 获取游标当前位置元素 pv = (Value *)DLinkList_Current(list); printf("current CUR value is: %d ",pv->v); printf(" "); pv = DLinkList_DeleteNode(list, (DLinkListNode*)&v4); printf("current DEL value is: %d ",pv->v); printf(" "); printListContent(list); printf(" "); // 将游标向前移 DLinkList_Pre(list); pv = (Value*)DLinkList_Current(list); printf("current PRE value is: %d ",pv->v); printf(" "); // 获取当前链表长度 int length = DLinkList_Length(list); printf("current length is: %d ",length); // 销毁当前链表 DLinkList_Destroy(list); } system("pause"); return 0; }
运行结果:
current GET value is: 1
current GET value is: 2
current GET value is: 3
current GET value is: 4
current GET value is: 5
current GET value is: 1
current GET value is: 2
current GET value is: 3
current GET value is: 4
current GET value is: 2
current GET value is: 3
current GET value is: 4
current CUR value is: 3
current DEL value is: 4
current GET value is: 2
current GET value is: 3
current PRE value is: 2
current length is: 2