目录
1. 链式存储结构
1.1 定义
1.2 实现方式
1.3 与链式存储有关的术语
1.4 链表(链式存储结构)的特点和优缺点
1.4.1 特点
1.4.2 优点
1.4.3 缺点
2. 单链表的实现
2.1 单链表的存储结构定义
2.2 初始化(构造一个空表 )
2.2.1 算法步骤
2.2.2 算法描述
2.3 销毁(删除整个表)
2.4 清空(删除表中所有数据)
2.5 求表长
2.6 判断表是否为空
2.7 取值(根据位置i获取相应位置数据元素的内容)
2.7.1 算法步骤
2.7.2 算法描述(用e返回i位置上的数据)
2.8 按值查找(返回位置i,不存在返回0)
2.8.1 算法步骤
2.8.2 算法描述
2.8.3 复杂度分析
2.9 插入(插在第 i 个结点之前)
2.9.1 算法步骤
2.9.2 算法描述
2.9.3 复杂度分析
2.10 删除(删除第 i 个结点)
2.10.1 算法步骤
2.10.2 算法描述
2.10.3 复杂度分析
2.11 单链表的建立(头插法)
2.11.1 算法步骤
2.11.2 算法描述
2.12 单链表的建立(尾插法)
2.12.1 算法步骤
2.12.2 算法描述
2.13 main函数调用
3. 完整代码
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。线性表的链式表示又称为非顺序映像或链式映像。
各节点有两个域组成:数据域和指针域
数据域:存储数据
指针域:存储后续节点的地址
1、结点:数据元素的存储映像。由数据域和指针域两部分组成
2、链表: n 个结点由指针链组成一个链表。它是线性表的链式存储映像,称为线性表的链式存储结构
3、单链表、双链表、循环链表:结点只有一个指针域的链表,称为单链表或线性链表;有两个指针域的链表,称为双链表;首尾相接的链表称为循环链表
4、头指针是指向链表中第一个结点的指针;
首元结点是指链表中存储第一个数据元素a1的结点;
头结点是在链表的首元结点之前附设的一个结点,数据域内只放空表标志和表长等信息
5. 单链表有头结点和无头结点的结构示意
思考:
(1)如何表示空表?
有头结点时,当头结点的指针域为空时表示空表。
无头结点时,头指针为空时为空表。
(2)在链表中设置头结点有什么好处?
①便于首元结点的处理
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理;
②便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
(3)头结点的数据域内装的是什么?
头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。
这种存取元素的方法被称为顺序存取法
1.数据元素的个数可以自由扩充
2.插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高
typedef int ElemType; typedef int Status; enum StatusCode { TRUE = 1, FALSE = 0, OK = 1, ERROR = 0, INFEASIBLE = -1, OVERFLOW = -2 }; typedef struct LNode { ElemType data; struct LNode* next; }LNode, *LinkList;
生成新结点作头结点,用头指针L指向头结点。
头结点的指针域置空。
Status InitList(LinkList* pL) { *pL = (LinkList)malloc(sizeof(LNode)); if ((*pL) == NULL) { perror("InitList"); return ERROR; } (*pL)->next = NULL; return OK; }
其中,参数使用的是LinkList*类型的参数,是二级指针,因为初始化需要对表进行修改,而表是用头指针表示的,所以需要用到二级指针,将表地址传进去。解引用就获得头指针。
Status DestoryList(LinkList* pL) { LNode* p = (LinkList)malloc(sizeof(LNode)); while (*pL) { p = *pL; *pL = (*pL)->next; free(p); } p = NULL; return OK; }
Status ClearList(LinkList* pL) { LNode* p = (LNode*)malloc(sizeof(LNode)); LNode* q = (LNode*)malloc(sizeof(LNode)); p = (*pL)->next; //p指向首元结点 while (p) { q = p->next; free(p); //释放当前p所指向的空间,避免内存溢出 p = q; } (*pL)->next = NULL; return OK; }
int ListLength(LinkList L) { LNode* p = L->next; int length = 0; while (p != NULL) { p = p->next; length++; } return length; }
Status IsEmpty(LinkList L) { if (L->next == NULL) return TRUE; return FALSE; }
Status GetElem(LinkList L, int i, ElemType* e) { LNode* p = (LNode*)malloc(sizeof(LNode)); p = L->next; int j = 1; while (p && j < i) { p = p->next; j++; } //找到第i-1位置 if (!p || j > i)return ERROR; *e = (p->data); return OK; }
int LocateElem(LinkList L, ElemType e) { LNode* p = (LNode*)malloc(sizeof(LNode)); p = L->next; int loc = 1; while (p && (p->data != e)) { p = p->next; loc++; } if (p) return loc; else return 0; }
因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为 O(n)
生成一个新结点*s
将新结点*s的数域置为插入的元素
新结点*s的指针域指向第i元素
令结点*p的指针域指向新结点*s
Status InsertList(LinkList* pL, int i, ElemType e) { LNode* p = (LNode*)malloc(sizeof(LNode)); p = *pL; int j = 0; while (p && (j < i - 1)) { p = p->next; j++; } if (!p || j > i - 1)return ERROR; LNode* s = (LNode*)malloc(sizeof(LNode)); s->data = e; s->next = p->next; p->next = s; return OK; }
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为 O(1)。
Status DeleteList(LinkList* pL, int i, ElemType* e) { LNode* p = *pL; int j = 0; while ((p->next) && j < (i - 1)) { p = p->next; j++; } if (!(p->next) || (j > i - 1)) return ERROR; LNode* q = p->next; p->next = q->next; *e = q->data; //保存被删除的数据 free(q); q = NULL; return OK; }
思考:能不能直接p->next = p->next->next ?
可以,但是第i个元素空间没有释放,并且也不知道删除了什么元素
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为 O(1)。
void CreateList_H(LinkList* pL, int n) { *pL = (LinkList)malloc(sizeof(LNode)); (*pL)->next = NULL; for (int i = 0; i < n; i++) { LNode* p = (LNode*)malloc(sizeof(LNode)); scanf("%d", &(p->data)); p->next = (*pL)->next; (*pL)->next = p; } }
void CreateList_T(LinkList* pL, int n) { *pL = (LinkList)malloc(sizeof(LNode)); (*pL)->next = NULL; LNode* tail = (LNode*)malloc(sizeof(LNode));//尾指针 tail = *pL; for (int i = 0; i < n; i++) { LNode* p = (LNode*)malloc(sizeof(LNode)); scanf("%d", &(p->data)); p->next = NULL; tail->next = p; tail = p; //让尾指针始终指向最后一个元素 } }
int main() { LinkList La; //定义链表 CreateList_T(&La, 20);//1,2,3,...,20 printf("%d\n", IsEmpty(La)); int La_len = ListLength(La); for (int i = 1; i <= La_len; i++) { int e; GetElem(La, i, &e); printf("%d ", e); } printf("%d\n", La_len); printf("%d\n", LocateElem(La, 10)); int a = 0; DeleteList(&La, 8, &a); printf("%d\n", a); printf("%d\n", ListLength(La)); ClearList(&La); printf("%d\n", ListLength(La)); printf("%d\n", DestoryList(&La)); return 0; }
#pragma warning(disable:4996) #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef int Status; enum StatusCode { TRUE = 1, FALSE = 0, OK = 1, ERROR = 0, INFEASIBLE = -1, OVERFLOW = -2 }; typedef struct LNode { ElemType data; struct LNode* next; }LNode, *LinkList; //LinkList为指向结构体Lnode的指针类型 //初始化链表 Status InitList(LinkList* pL) { *pL = (LinkList)malloc(sizeof(LNode)); if ((*pL) == NULL) { perror("InitList"); return ERROR; } (*pL)->next = NULL; return OK; } //判断单链表是否为空 Status IsEmpty(LinkList L) { if (L->next == NULL) return TRUE; return FALSE; } //销毁单链表,头结点也销毁 Status DestoryList(LinkList* pL) { LNode* p = (LinkList)malloc(sizeof(LNode)); while (*pL) { p = *pL; *pL = (*pL)->next; free(p); } p = NULL; return OK; } //清空单链表 Status ClearList(LinkList* pL) { LNode* p = (LNode*)malloc(sizeof(LNode)); LNode* q = (LNode*)malloc(sizeof(LNode)); p = (*pL)->next; //p指向首元结点 while (p) { q = p->next; free(p); p = q; } (*pL)->next = NULL; return OK; } //求单链表的长度 int ListLength(LinkList L) { LNode* p = L->next; int length = 0; while (p != NULL) { p = p->next; length++; } return length; } //单链表的取值 Status GetElem(LinkList L, int i, ElemType* e) { LNode* p = (LNode*)malloc(sizeof(LNode)); p = L->next; int j = 1; while (p && j < i) { p = p->next; j++; } if (!p || j > i)return ERROR; *e = (p->data); return OK; } //按值查找 返回地址 //LNode* LocateElem(LinkList L,ElemType e) //{ // LNode* p = L->next; // while (p&&(p->data != e)) // p = p->next; // return p; //} // 按值查找 返回位置序号 int LocateElem(LinkList L, ElemType e) { LNode* p = (LNode*)malloc(sizeof(LNode)); p = L->next; int loc = 1; while (p && (p->data != e)) { p = p->next; loc++; } if (p) return loc; else return 0; } //在第i个位置插入元素e Status InsertList(LinkList* pL, int i, ElemType e) { LNode* p = (LNode*)malloc(sizeof(LNode)); p = *pL; int j = 0; while (p && (j < i - 1)) { p = p->next; j++; } if (!p || j > i - 1)return ERROR; LNode* s = (LNode*)malloc(sizeof(LNode)); s->data = e; s->next = p->next; p->next = s; return OK; } // Status DeleteList(LinkList* pL, int i, ElemType* e) { LNode* p = *pL; int j = 0; while ((p->next) && j < (i - 1)) { p = p->next; j++; } if (!(p->next) || (j > i - 1)) return ERROR; LNode* q = p->next; p->next = q->next; *e = q->data; //保存被删除的数据 free(q); q = NULL; return OK; } //头插法创建链表 void CreateList_H(LinkList* pL, int n) { *pL = (LinkList)malloc(sizeof(LNode)); (*pL)->next = NULL; for (int i = 0; i < n; i++) { LNode* p = (LNode*)malloc(sizeof(LNode)); scanf("%d", &(p->data)); p->next = (*pL)->next; (*pL)->next = p; } } //尾插法创建链表 void CreateList_T(LinkList* pL, int n) { *pL = (LinkList)malloc(sizeof(LNode)); (*pL)->next = NULL; LNode* tail = (LNode*)malloc(sizeof(LNode));//尾指针 tail = *pL; for (int i = 0; i < n; i++) { LNode* p = (LNode*)malloc(sizeof(LNode)); scanf("%d", &(p->data)); p->next = NULL; tail->next = p; tail = p; //让尾指针始终指向最后一个元素 } } int main() { LinkList La; //定义链表 CreateList_T(&La, 20);//1,2,3,...,20 printf("%d\n", IsEmpty(La)); int La_len = ListLength(La); for (int i = 1; i <= La_len; i++) { int e; GetElem(La, i, &e); printf("%d ", e); } printf("%d\n", La_len); printf("%d\n", LocateElem(La, 10)); int a = 0; DeleteList(&La, 8, &a); printf("%d\n", a); printf("%d\n", ListLength(La)); ClearList(&La); printf("%d\n", ListLength(La)); printf("%d\n", DestoryList(&La)); return 0; }