数据结构之线性表的链式存储——链表
为了弥补顺序表插入和删除操作需要移动大量元素的缺点,一般采用链表存储。通俗理解的话就是让所有元素不必考虑存放位置,任意位置存放即可,哪里有空位就存到哪,只是需要每个元素知道其下一个元素的位置在哪里就好。这样我们就可以通过第一个元素找到第二个元素,从而通过遍历找到所有元素。
为了表示每个数据元素ai与其直接后继元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或链。这两部分信息组成数据元素ai的存储映像,称为结点。n个结点 链接成一个链表,称为线性表的链式存储结构。
我们把链表中的第一个结点的存储位置叫做头指针。为了更加方便的对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储信息,头结点的指针域存储指向第一个结点的指针。
头结点与头指针的比较:
头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针;
头指针具有标识作用,所以常用头指针冠以链表的名字;
无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般不存储信息;
有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了;
头结点不一定是链表的必须要素。
以不带头结点的单链表为例,常见的几种操作为:
1.插入操作
[code]bool LinkListInsert(LinkList* head, int i, ElemType e) { //插入位置不合法 if (i < 0) { return false; } //头插 if (i == 0) { NODE* pos = (NODE*)malloc(sizeof(NODE)); pos->data = e; pos->next = *head; *head = pos; return true; } //寻找插入位置i的前一个结点 LinkList pos = *head;//声明一个结点,从当前结点开始 for (int j = 0; pos != NULL && j < i - 1; j++) { pos = pos->next; } if (pos == NULL) { return false; } NODE* s = (NODE*)malloc(sizeof(NODE)); s->data = e; s->next = pos->next; pos->next = s; return true; }
2.删除操作
[code]bool LinkListDelete(LinkList* head, int i) { if (i < 0) { return false; } //头删 if (i == 0) { NODE* pos = *head; *head = pos->next; free(pos); pos = NULL; return true; } else { //寻找删除位置的前一个结点 LinkList pos = *head; for (int j = 0; j < i - 1 && pos != NULL; j++) { pos = pos->next; } //如果没找到 if (pos == NULL || pos->next == NULL) { return false; } else { NODE* s = pos->next; pos->next = s->next; free(s); s = NULL; } } }
3.求单链表长度
[code]int LengthLinkList(LinkList head) { int len = 0; NODE* pos = head; while (pos) { len++; pos = pos->next; } return len; }
4.单链表反转
[code]void ListReverse(LinkList* head) { //如果单链表中没有元素或者只有一个元素 if (*head == NULL || (*head)->next == NULL) { return; } //定义三个指针,p1,p2,p3 NODE* p1 = *head; NODE* p2 = p1->next; NODE* p3 = p2->next; //p2的next指向p1 p2->next = p1; while (p3 != NULL) { p1 = p2; p2 = p3; p3 = p3->next; p2->next = p1; } (*head)->next = NULL; *head = p2; }
5.找出单链表中倒数第i个元素
[code]NODE* FindLast(LinkList head, int i) { //快慢指针 NODE* pFast; NODE* pSlow; //判断i的合法性 if (i <= 0) { return 0; } int j = 0; for (pFast = head; pFast != NULL && j < i - 1; j++) { pFast = pFast->next; } if (pFast == NULL) { return pFast; } pSlow = head; while (pFast->next != NULL) { pSlow = pSlow->next; pFast = pFast->next; } return pSlow; }
6.找出单链表的中间元素
[code]NODE* GetMiddle(LinkList head) { NODE* pFast = head; NODE* pSlow = head; if (pFast == NULL) { return pSlow; } while (pFast->next != NULL) { pFast = pFast->next; if (pFast->next == NULL) { break; } pFast = pFast->next; pSlow = pSlow->next; } return pSlow; }
7.获取单链表中第i个位置的元素
[code]NODE* GetElem(LinkList head, int i) { if (i < 0) { return NULL; } NODE* pos = head;//声明一个结点指向L的第一个结点 for (int j = 0; j < i && pos != NULL; j++) { pos = pos->next; } return pos; }
8.清空操作
[code]void ClearList(LinkList* head) { //当单链表不为空时,头删 while (*head) { LinkListDelete(head, 0); } }
9.打印
[code]void ShowList(LinkList head) { NODE* pos = head;//声明一个结点s指向L的第一个节点 while (pos) { printf("%d ", pos->data); pos = pos->next; } puts(""); }
测试函数和声明部分:
[code]//shengmingbufen typedef int ElemType; typedef struct _NODE { ElemType data; _NODE* next; }NODE; typedef NODE* LinkList; //测试函数 int main() { LinkList head = NULL; LinkListInsert(&head, 0, 11); LinkListInsert(&head, 1, 33); LinkListInsert(&head, 2, 55); LinkListInsert(&head, 3, 77); LinkListInsert(&head, 4, 99); LinkListInsert(&head, 5, 10); printf("%d\n", LengthLinkList(head)); ShowList(head); LinkListInsert(&head, 6, 100); printf("%d\n", LengthLinkList(head)); ShowList(head); //删除 LinkListDelete(&head, 6); printf("%d\n", LengthLinkList(head)); ShowList(head); //反转 ListReverse(&head); ShowList(head); //获取i位置的元素,如果找到则将该位置的元素置为88 //NODE* pos = GetElem(head, 0); //if (pos!=NULL) //{ // pos->data = 88; //} //ShowList(head); //获取倒数第i个元素,如果找到则将该位置的元素置为88 //NODE* pos = FindLast(head, 1); //if (pos != NULL) //{ // pos->data = 88; //} //ShowList(head); //获取链表中间元素,并将该元素置为88 //NODE* pos = GetMiddle(head); //if (pos != NULL) //{ // pos->data = 88; //} //ShowList(head); ClearList(&head); printf("%d\n", LengthLinkList(head)); ShowList(head); return 0; }
以上即为我对单链表的内容的总结~~
- 数据结构之线性表——链表的链式存储(链式描述)注释版
- 数据结构之线性表——链表的链式存储(链式描述)
- [SDUT](2117)数据结构实验之链表二:逆序建立链表 ---链式存储(线性表)
- 数据结构之线性表——链表的链式存储(链式描述)
- 数据结构之线性表-链式存储之静态链表(二)
- C语言 数据结构 线性表 单链表 线性表的链式存储结构之一
- 数据结构——线性表的伪链表存储(顺序存储链式遍历)
- [SDUT](2116)数据结构实验之链表一:顺序建立链表 ---链式存储(线性表)
- JAVA数据结构之线性表的链式存储结构——单链表
- 数据结构算法代码实现——线性表的链式表示与实现(单链表)(三 )
- 数据结构(八)线性表链式存储结构
- 数据结构2----线性表顺序存储和链式存储的实现(霜之小刀)
- 数据结构——线性表之链表存储学习
- 线性表的链式存储结构--链表
- 数据结构之线性表——链表的顺序存储(数组描述)
- 线性表的链式存储结构——单链表
- 数据结构之线性表代码实现顺序存储,链式存储,静态链表(选自大话数据结构)
- 线性表之链式存储结构_单链表相关算法
- 结构之美:线性表的链式存储结构——链表
- 线性表的链式存储结构-单链表