文章目录
- 单向链表
- 定义
- 优缺点
- 单向链表实现
- 结构体定义
- 基础函数声明
- 静态函数实现
- 基础函数实现
- 创建一个单向链表 create_slinkedlist
- 单向链表是否为空 empty_slinkedlist
- 元素个数 size_t size_slinkedlist
- 在指定位置插入一个元素 insert_slinkedlist
- 在头部插入元素 push_front_slinkedlist
- 删除指定位置元素 remove_slinkedlist
- 删除第一个元素 pop_front_slinkedlist
- 删除第n个为elem的元素 n == 0 表示全删 delete_slinkedlist
- 根据条件删除第n个元素 需要传入condition函数 delete_condition_slinkedlist
- 查找位置为pos的元素 elem_of_slinkedlist
- 查找第n个为elem的元素 find_slinkedlist
- 根据条件删除第n个元素 find_condition_slinkedlist
- 统计为elem元素的个数count_slinkedlist
- 统计满足条件元素的个数 coount_condition
- 更新pos位置的元素 update_slinkedlist
- 清空链表 clear_slinkedlist
- 销毁链表 destroy_slinkedlist
- 迭代 foreach_slinkedlist
- 单向链表常见题型
单向链表
定义
-
存储元素的同时记录下一个结点的地址
-
链表的内存地址可以不连续
-
对比顺序表需要额外的空间存储next指针
ElemType elem; // 结点存储的元素
struct SNode *next; // 下一个结点的地址
优缺点
-
优点
-
内存不需要连续,可以是零散的内存空间
-
在头部插入和删除的效率很高 O(1)
-
在指定位置后插入和删除的效率很高 O(1)
-
不需要提前分配好空间,有多少元素就需要的少空间,没有内存闲置
-
-
缺点
-
需要额外的空间来存储指针域,内存利用率不高
-
不支持随机访问,查找指定位置元素的效率为o(n)
-
在末尾插入和删除的效率差 O(n)
-
链表不能进行二分查找
-
-
什么时候用单向链表
-
数据量不确定
-
数据变化较大(插入和删除)
-
增加和删除元素经常在头部进行时
-
单向链表实现
结构体定义
有元素和指针两个部分
typedef int ElemType;
// 单向链表结点
struct SNode {
ElemType elem; // 结点存储的元素
struct SNode *next; // 下一个结点的地址
};
// 结点大小宏
#define SNODESIZE sizeof(struct SNode)
// 单向链表类型
typedef struct SNode* SLinkedList;
#define SUCCESS 0 // 成功返回值
#define FAILURE -1 // 失败返回值
基础函数声明
// 创建一个单向链表
SLinkedList create_slinkedlist(void);
// 单向链表是否为空
bool empty_slinkedlist(SLinkedList list);
// 元素个数
size_t size_slinkedlist(SLinkedList list);
// 在指定位置插入一个元素
int insert_slinkedlist(SLinkedList list, size_t pos, ElemType elem);
// 在头部插入元素
int push_front_slinkedlist(SLinkedList list, ElemType elem);
// 删除指定位置元素
int remove_slinkedlist(SLinkedList list, size_t pos, ElemType *pElem);
// 删除第一个元素
int pop_front_slinkedlist(SLinkedList list, ElemType *pElem);
// 删除第n个为elem的元素 n == 0 表示全删
int delete_slinkedlist(SLinkedList list, ElemType elem, size_t n);
// 根据条件删除第n个元素 需要传入condition函数
int delete_condition_slinkedlist(SLinkedList list, bool (condition)(ElemType), size_t n);
// 查找位置为pos的元素
ElemType* elem_of_slinkedlist(SLinkedList list, size_t pos);
// 查找第n个为elem的元素
int find_slinkedlist(SLinkedList list, ElemType elem, size_t n);
// 根据条件删除第n个元素
int find_condition_slinkedlist(SLinkedList list, bool (*condition)(ElemType), size_t n);
// 统计为elem元素的个数
size_t count_slinkedlist(SLinkedList list, ElemType elem);
// 统计满足条件元素的个数
size_t coount_condition(SLinkedList list, bool (*condition)(ElemType));
// 更新pos位置的元素为newElem
int update_slinkedlist(SLinkedList list, size_t pos, ElemType newElem);
// 清空链表
void clear_slinkedlist(SLinkedList list);
// 销毁链表
void destroy_slinkedlist(SLinkedList list);
// 迭代
void foreach_slinkedlist(SLinkedList list, void (*foreach)(ElemType*));
// 静态函数
// 获取第pos个位置的结点
static struct SNode* get_node_slinkedlist(SLinkedList list, size_t pos);
// 用elem和next创建一个单向链表的结点
static struct SNode* create_snode(ElemType elem, struct SNode *next);
// 在指定结点node后面插入一个元素
static int insert_after(struct SNode *node, ElemType elem);
// 删除指定结点后面一个结点
static int delete_after(struct SNode *prevNode, ElemType *pElem);
静态函数实现
获取第pos个位置的结点 get_node_slinkedlist
获取第pos个位置的结点 pos为0表示获取头结点
如果pos位置不存在则返回NULL
static struct SNode* get_node_slinkedlist(SLinkedList list, size_t pos) {
struct SNode *node = list;
for (size_t i = 0; i < pos && node != NULL; ++i) {
node = node->next;
}
return node;
}
用elem和next创建一个单向链表的结点 create_snode
static struct SNode* create_snode(ElemType elem, struct SNode *next) {
struct SNode *node = (struct SNode*)malloc(SNODESIZE);
if (node != NULL) {
node->elem = elem;
node->next = next;
}
return node;
}
在指定结点node后面插入一个元素 insert_after
node的next指针指向新建的结点
static int insert_after(struct SNode *node, ElemType elem) {
if (node == NULL) {
return FAILURE;
}
struct SNode *insNode = create_snode(elem, node->next);
if (insNode == NULL) {
return FAILURE;
}
node->next = insNode;
return SUCCESS;
}
删除指定结点后面一个结点 delete_after
先判断下一个结点是否存在
主要prev->next = prev->next->next
注意要释放删除的结点,否则会造成内存泄漏!
static int delete_after(struct SNode *prevNode, ElemType *pElem) {
// 前一个结点不存在 或者 要删除的结点不存在
if (prevNode == NULL || prevNode->next == NULL) {
return FAILURE;
}
// 记录要删除的结点
struct SNode *delNode = prevNode->next;
// 看调用者是否要保存删除的元素
if (pElem != NULL) {
*pElem = delNode->elem;
}
// 把delNode从单向链表中删除
prevNode->next = delNode->next;
// 释放结点的内存
free(delNode);
return SUCCESS;
}
基础函数实现
创建一个单向链表 create_slinkedlist
创建一个头结点,不用来存储数据
SLinkedList create_slinkedlist(void) {
SLinkedList list = (SLinkedList)malloc(sizeof(SNODESIZE));
if (list != NULL) {
list->next = NULL; // 单向链表为空 即第一个结点为NULL(不存在)
}
return list;
}
单向链表是否为空 empty_slinkedlist
如果第一个结点都不存在则为空
bool empty_slinkedlist(SLinkedList list) {
assert(list != NULL);
return list->next == NULL;
}
元素个数 size_t size_slinkedlist
遍历链表统计元素个数
size_t size_slinkedlist(SLinkedList list) {
assert(list != NULL);
size_t cnt = 0;
// while (list = list->next) {
// ++cnt;
// }
struct SNode *node = list->next;
for (; node != NULL; node = node ->next, ++cnt);
return cnt;
}
在指定位置插入一个元素 insert_slinkedlist
时间复杂度O(n)
int insert_slinkedlist(SLinkedList list, size_t pos, ElemType elem) {
assert(list != NULL);
if (pos == 0) {
return FAILURE;
}
// 在pos位置插入,获取第pos-1个结点
struct SNode *prev = get_node_slinkedlist(list, pos - 1);
return insert_after(prev, elem); //调用静态函数
/*
if (prev == NULL) { // 前一个位置为空,则插入失败
return FAILURE;
}
// 创建结点
struct SNode *insNode = create_slinkedlist(elem, prev->next);
if (insNode == NULL) {
return FAILURE;
}
//前结点的next指针指向插入的结点
prev->next = insNode;
return SUCCESS;
*/
}
在头部插入元素 push_front_slinkedlist
时间复杂度O(1)
int push_front_slinkedlist(SLinkedList list, ElemType elem) {
assert(list != NULL);
// 在单向链表头部插入 所以插入的结点的作为第一个结点
// 原来的第一个结点就作为插入结点的next
return insert_after(list, elem);
/*
static SNode *first = create_snode(elem, list->next);
if (first == NULL) {
return FAILURE;
}
list->next = first;
return SUCCESS;
*/
}
删除指定位置元素 remove_slinkedlist
时间复杂度O(n)
int remove_slinkedlist(SLinkedList list, size_t pos, ElemType *pElem) {
assert(list != NULL);
if (pos == 0) {
return FAILURE;
}
struct SNode *prev = get_node_slinkedlist(list, pos - 1);
/*
if (prev == NULL || prev->next == NULL) {
return FAILURE;
}
*/
return delete_after(prev, pElem);
}
删除第一个元素 pop_front_slinkedlist
时间复杂度O(1)
int pop_front_slinkedlist(SLinkedList list, ElemType *pElem) {
assert(list != NULL);
return delete_after(list, pElem);
}
删除第n个为elem的元素 n == 0 表示全删 delete_slinkedlist
时间复杂度O(n)
int delete_slinkedlist(SLinkedList list, ElemType elem, size_t n) {
assert(list != NULL);
size_t cnt = 0;
struct SNode *prev = list;
while (prev->next != NULL) {
if (prev->next->elem == elem) { // 等于要删除的结点
if (n == 0) {
delete_after(prev, NULL);
++cnt;
} else {
if (--n == 0) {
delete_after(prev, NULL);
return 1;
} else {
prev = prev->next;
}
}
} else {
prev = prev->next; // 删除元素后无需移动到下一个地址 删除后下一个元素补上来了
}
}
return cnt;
}
根据条件删除第n个元素 需要传入condition函数 delete_condition_slinkedlist
自定义回调函数condition 时间复杂度O(n)
int delete_condition_slinkedlist(SLinkedList list, bool (condition)(ElemType), size_t n) {
assert(list != NULL && condition != NULL);
size_t cnt = 0;
struct SNode *prev = list;
while (prev->next != NULL) {
if (condition(prev->next->elem)) {
if (n == 0) {
delete_after(prev, NULL);
++cnt;
} else {
if (--n == 0) {
delete_after(prev, NULL);
return 1;
} else {
prev = prev->next;
}
}
} else {
prev = prev->next;
}
}
return cnt;
}
查找位置为pos的元素 elem_of_slinkedlist
时间复杂度O(n)
ElemType* elem_of_slinkedlist(SLinkedList list, size_t pos) {
assert(list != NULL);
if (pos == 0) {
return NULL;
}
struct SNode *node = get_node_slinkedlist(list, pos);
if (node == NULL) {
return NULL;
}
return &node->elem;
}
查找第n个为elem的元素 find_slinkedlist
时间复杂度O(n)
int find_slinkedlist(SLinkedList list, ElemType elem, size_t n) {
assert(list != NULL);
if (n == 0) {
return 0; // 表示没找到
}
size_t pos = 0;
struct SNode *node = list->next;
for (; node != NULL; node = node->next) {
++pos;
if (node->elem == elem) {
if (--n == 0) {
return pos;
}
}
}
return 0;
}
根据条件删除第n个元素 find_condition_slinkedlist
时间复杂度O(n)
int find_condition_slinkedlist(SLinkedList list, bool (*condition)(ElemType), size_t n) {
assert(list != NULL && condition != NULL);
if (n == 0) {
return 0;
}
size_t pos = 0;
struct SNode *node = list->next;
for (; node != NULL; node = node->next) {
++pos;
if (condition(node->elem)) {
if (--n == 0) {
return pos;
}
}
}
return 0;
}
统计为elem元素的个数count_slinkedlist
时间复杂度O(n)
size_t count_slinkedlist(SLinkedList list, ElemType elem) {
assert(list != NULL);
size_t cnt = 0;
struct SNode *node = list->next;
for (; node != NULL; node = node->next) {
if (node->elem == elem) { // 等于要统计的元素
++cnt;
}
}
return cnt;
}
统计满足条件元素的个数 coount_condition
时间复杂度O(n)
size_t coount_condition(SLinkedList list, bool (*condition)(ElemType)) {
assert(list != NULL && condition != NULL);
size_t cnt = 0;
struct SNode *node = list->next;
for (; node != NULL; node = node->next) {
if (condition(node->elem)) { // 满足条件的元素
++cnt;
}
}
return cnt;
}
更新pos位置的元素 update_slinkedlist
时间复杂度O(n)
int update_slinkedlist(SLinkedList list, size_t pos, ElemType newElem) {
assert(list != NULL);
if (pos == 0) {
return FAILURE;
}
struct SNode *node = get_node_slinkedlist(list, pos);
if (node == NULL) {
return FAILURE;
}
node->elem = newElem;
return SUCCESS;
}
清空链表 clear_slinkedlist
时间复杂度O(n)
每个结点都要释放,否则会造成内存泄漏
void clear_slinkedlist(SLinkedList list) {
assert(list != NULL);
struct SNode *node, *next;
for (node = list->next; node != NULL; node = next) {
next = node->next; // 先记录下一个结点
free(node);
}
list->next = NULL;
}
销毁链表 destroy_slinkedlist
先清空链表,再释放头结点
void destroy_slinkedlist(SLinkedList list) {
assert(list != NULL);
clear_slinkedlist(list); // 清空所有元素
free(list); // 释放头结点
}
迭代 foreach_slinkedlist
需要传入自定义迭代函数foreach
可以用来实现打印元素的操作
void foreach_slinkedlist(SLinkedList list, void (*foreach)(ElemType*)) {
assert(list != NULL);
struct SNode *node = list->next;
for (; node != NULL; node = node->next) {
foreach(&node->elem);
}
}
单向链表常见题型
函数声明
// 单向链表逆序
void reverse_slinkedlist(SLinkedList list);
// 按count个元素进行逆序
void reverse_count_slinkedlist(SLinkedList list, size_t count);
// 合并两个升序的单向链表l1和l2为l3
void merge_slinkedlist(SLinkedList l1, SLinkedList l2, SLinkedList l3);
// 求两个单向链表的尾链长度
size_t common_tail_slinkedlist(SLinkedList l1, SLinkedList l2);
// 判断单链表是否有环
size_t circle_slinkedlist(SLinkedList list);
函数实现
单向链表逆序 reverse_slinkedlist
void reverse_slinkedlist(SLinkedList list) {
assert(list != NULL);
if (list->next == NULL || list->next->next == NULL) {
return; // NULL
}
struct SNode *prev = NULL; // 上一个结点
struct SNode *curr = list->next; // 当前结点
struct SNode *next = NULL; // 下一个结点
while (curr != NULL) {
next = curr->next; // 先记录下一个结点
curr->next = prev; // 让指向下一个结点的指针指向上一个结点
prev = curr; // 向后移动
curr = next; // 向后移动
}
list->next = prev; // 让头结点指向逆序后的第一个结点
// return list;
}
按count个元素进行逆序 reverse_count_slinkedlist
void reverse_count_slinkedlist(SLinkedList list, size_t count) {
assert(list != NULL && count != 1);
struct SNode *last = list; // 已经逆序的最后一个结点
struct SNode *prev = NULL;
struct SNode *curr = list->next;
struct SNode *next = NULL;
struct SNode *first = NULL; // 当前count个逆序结点的第一个
while (curr != NULL) {
first = curr;
prev = NULL; // 要逆序的count个结点的第一个next指向prev NULL
for (size_t i = 0; i < count && curr != NULL; ++i) { // 每count个逆序 或最后不够count个
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
last->next = prev; // 已经逆序的最后一个结点的next指向 刚刚逆序的count个结点的第一个结点
last = first; // 现在这count个的第一个结点变成逆序好的最后一个结点
}
// return list;
}
合并两个升序的单向链表l1和l2为l3 merge_slinkedlist
归并排序
void merge_slinkedlist(SLinkedList l1, SLinkedList l2, SLinkedList l3) {
assert(l1 != NULL && l2 != NULL && l3 != NULL);
struct SNode *node1 = l1->next;
struct SNode *node2 = l2->next;
struct SNode *curr = l3;
while (node1 != NULL && node2 != NULL) {
if (node1->elem < node2->elem) { // curr的next指向 较小的元素的结点
curr->next = node1;
node1 = node1->next;
} else {
curr->next = node2;
node2 = node2->next;
}
curr = curr->next; // 当前curr向后移动
}
curr->next = node1 != NULL ? node1 : node2; // 有一个不为空 把整条链接上
l1->next = NULL;
l2->next = NULL;
}
求两个单向链表的尾链长度 common_tail_slinkedlist
size_t common_tail_slinkedlist(SLinkedList l1, SLinkedList l2) {
assert(l1 != NULL && l2 != NULL);
size_t len1 = size_slinkedlist(l1); // 计算单向链表l1的长度
size_t len2 = size_slinkedlist(l2); // 计算单向链表l2的长度
struct SNode *node1 = l1->next;
struct SNode *node2 = l2->next;
if (len1 > len2) { // 先把较长的链移动到 与较短的的链一样的长度
while (len1 != len2) {
--len1;
node1 = node1->next;
}
} else {
while (len1 != len2) {
--len2;
node2 = node2->next;
}
}
while (node1 != NULL && node2 != NULL && node1 != node2) { // 一起移动到相同的结点为止
--len1;
node1 = node1->next;
node2 = node2->next;
}
return len1; // 移动后剩下的长度就时共同尾链的长度
}
判断单链表是否有环 circle_slinkedlist
size_t circle_slinkedlist(SLinkedList list) {
assert(list != NULL);
if (list->next == NULL || list->next->next == NULL) {
return 0;
}
struct SNode *quick = list->next; // 快指针 每次移动2个结点
struct SNode *slow = list->next; // 慢指针 每次移动1个结点
while (quick != NULL && quick->next != NULL) { // 如果有环则快指针会追上慢指针
quick = quick->next->next;
slow = slow->next;
if (quick == slow) {
size_t n = 0;
do { // 在相遇点在跑一圈就是环的长度
++n;
quick = quick->next;
} while (quick != slow);
return n;
}
}
return 0;
}