数据结构(链表)(3)
- 这节的内容对于上一节理解的比较好的同学是非常简单的. 这节的主要任务就是接着完善几个函数接口. 单链表这个内容就差不多了.
- 数据结构之间的功能都是差不多的, 不过区别多是效率上的区别, 所以剩下的这些接口无非就是查找数据, 还有指定位置插入数据. 还有指定位置删除数据.
- 不过实现起来肯定是会有所不同, 毕竟是不同的结构.
那么第一个函数, 查找数据:
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) { SLTNode* cur = phead; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
- 然后就是指定位置插入数据, 这里链表说的更直接点, 其实就是指定位置之前插入数据, 其实顺序表里面的指定位置插入数据也是一个意思, 也是指定位置之前插入数据.
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) { assert(pphead); assert(pos); if (pos == *pphead) { SLTPushFront(pphead, x); } else { SLTNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } SLTNode* newnode = BuySListNode(x); prev->next = newnode; newnode->next = pos; } }
- 然后链表还多了一个就是指定位置之后插入数据
// 在pos以后插入x void SLTInsertAfter(SLTNode* pos, SLTDataType x) { assert(pos); SLTNode* newnode = BuySListNode(x); pos->next = newnode; newnode->next = pos->next; }
- 有插入数据就有删除数据, 一样的两种, 先是第一种:
// 删除pos位置 void SLTErase(SLTNode** pphead, SLTNode* pos) { assert(pphead); assert(pos); if (pos == *pphead) { SLTPopFront(pphead); } else { SLTNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } prev->next = pos->next; free(pos); //pos = NULL; } }
- 最后一个函数, 指定位置之后删除数据:
// 删除pos的后一个位置 void SLTEraseAfter(SLTNode* pos) { assert(pos); // 检查pos是否是尾节点 assert(pos->next); SLTNode* posNext = pos->next; pos->next = posNext->next; free(posNext); posNext = NULL; }
- 链表头文件: SList.h
#pragma once #include<stdio.h> #include<stdlib.h> typedef int SLTDatatype; //链表节点的结构: typedef struct SListNode { SLTDatatype data; struct SListNode* next; }SLTNode; //循环遍历打印链表 void PrintSList(SLTNode* phead); //动态申请一个节点 SLTNode* BuySListNode(SLTDatatype x); //尾部插入数据 void SLTPushBack(SLTNode** pphead, SLTDatatype x); //头部插入数据 void SLTPushFront(SLTNode** pphead, SLTDatatype x); //尾部删除数据 void SLTPopBack(SLTNode** pphead); //头部删除数据 void SLTPopFront(SLTNode** pphead); //查找数据 SLTNode* SLTFind(SLTNode* phead, SLTDatatype x); // 在pos之前插入x void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x); // 在pos以后插入x void SLTInsertAfter(SLTNode* pos, SLTDatatype x); // 删除pos位置 void SLTErase(SLTNode** pphead, SLTNode* pos); // 删除pos的后一个位置 void SLTEraseAfter(SLTNode* pos);
- 函数实现文件, SList.c
#include"SList.h" //循环遍历打印链表 void PrintSList(SLTNode* phead) { //把头结点的值赋给临时变量cur,不要用phead来遍历, //因为等会可能还会用到phead,结果被改了,就不好弄了. SLTNode* cur = phead; while (cur != NULL) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); } //动态申请一个节点 SLTNode* BuySListNode(SLTDatatype x) { //动态开辟一个节点的结构体空间 SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode)); //判断有没有创建成功 if (newnode == NULL) { perror("malloc failed"); exit(-1); } //创建成功之后就赋值 newnode->data = x; newnode->next = NULL; //返回结构体指针 return newnode; } //尾部插入数据 void SLTPushBack(SLTNode** pphead, SLTDatatype x) { assert(pphead); SLTNode* newnode = SLTBuyNode(x); //链表为空,新节点作为phead if (*pphead == NULL) { *pphead = newnode; return; } //链表不为空,找尾节点 SLTNode* ptail = *pphead; while (ptail->next) { ptail = ptail->next; } //ptail就是尾节点 ptail->next = newnode; } //头部插入数据 void SLTPushFront(SLTNode** pphead, SLTDatatype x) { SLTNode* newnode = BuySListNode(x); newnode->next = *pphead; *pphead = newnode; } //尾部删除数据 void SLTPopBack(SLTNode** pphead) { assert(pphead); // 1、空 assert(*pphead); // 2、一个节点 // 3、一个以上节点 if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { //SLTNode* tailPrev = NULL; //SLTNode* tail = *pphead; //while (tail->next) //{ // tailPrev = tail; // tail = tail->next; //} //free(tail); tail = NULL; //tailPrev->next = NULL; SLTNode* tail = *pphead; while (tail->next->next) { tail = tail->next; } free(tail->next); tail->next = NULL; } } //头部删除数据 void SLTPopFront(SLTNode** pphead) { assert(pphead); // 空 assert(*pphead); // 非空 SLTNode* newhead = (*pphead)->next; free(*pphead); *pphead = newhead; } SLTNode* SLTFind(SLTNode* phead, SLTDatatype x) { SLTNode* cur = phead; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; } void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x) { assert(pphead); assert(pos); if (pos == *pphead) { SLTPushFront(pphead, x); } else { SLTNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } SLTNode* newnode = BuySListNode(x); prev->next = newnode; newnode->next = pos; } } // 在pos以后插入x void SLTInsertAfter(SLTNode* pos, SLTDatatype x) { assert(pos); SLTNode* newnode = BuySListNode(x); pos->next = newnode; newnode->next = pos->next; } // 删除pos位置 void SLTErase(SLTNode** pphead, SLTNode* pos) { assert(pphead); assert(pos); if (pos == *pphead) { SLTPopFront(pphead); } else { SLTNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } prev->next = pos->next; free(pos); //pos = NULL; } } // 删除pos的后一个位置 void SLTEraseAfter(SLTNode* pos) { assert(pos); // 检查pos是否是尾节点 assert(pos->next); SLTNode* posNext = pos->next; pos->next = posNext->next; free(posNext); posNext = NULL; }
- 到这里, 咱们的单链表的内容就告一段落了.
- 其实可以发现, 单链表的做事效率也不咋高, 就好比如, 尾部插入个数据还得遍历链表找尾巴, 指定位置之前插入, 等等都需要遍历链表, 什么的, 所以说效率不高, 咱们如果到实际应用上, 这种简单的单链表使用起来也不会很多, 这部分主要是让大家学习链表这个数据结构用的.
- 后面还会有带头双向循环链表这些更好用的链表, 功能实现起来也很简单, 这就放到后面再讲解了.