封装链表
原来的单链表添加效率低,非法下标的判断效率也很低
1、单链表
节点:
数据域
指针域
链表结构数据项:
头指针
尾指针
节点数量
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
// 设计节点
typedef struct Node
{
TYPE data;
struct Node* next;
}Node;
// 创建节点
Node* create_node(TYPE data)
{
Node* node = malloc(sizeof(Node));
node->data = data;
node->next = NULL;
return node;
}
// 设计带头节点的单链表结构
typedef struct List
{
Node* head; // 头指针
Node* tail; // 尾指针
size_t size; // 节点数量
}List;
// 创建单链表结构
List* create_list(void)
{
List* list = malloc(sizeof(List));
list->head = create_node(0);
list->tail = NULL;
list->size = 0;
return list;
}
// 头添加
void add_head_list(List* list,TYPE data)
{
Node* node = create_node(data);
if(0 == list->size)
{
list->head->next = node;
list->tail = node;
}
else
{
node->next = list->head->next;
list->head->next = node;
}
list->size++;
}
// 尾添加
void add_tail_list(List* list,TYPE data)
{
Node* node = create_node(data);
if(0 == list->size)
{
list->head->next = node;
list->tail = node;
}
else
{
list->tail->next = node;
list->tail = node;
}
list->size++;
}
// 头删除
bool del_head_list(List* list)
{
if(0 == list->size) return false;
Node* temp = list->head->next;
list->head->next = temp->next;
if(1 == list->size) list->tail = NULL;
free(temp);
list->size--;
return true;
}
// 尾删除
bool del_tail_list(List* list)
{
if(0 == list->size) return false;
free(list->tail);//释放了使用权
if(1 == list->size)
{
list->head->next = NULL;
list->tail = NULL;
}
else
{
Node* prev = list->head->next;
while(prev->next != list->tail) prev = prev->next; //空指针不能解引用,这里只是看一下地址
prev->next = NULL;
list->tail = prev;
}
list->size--;
return true;
}
// 插入
bool insert_list(List* list,int index,TYPE data)
{
if(0 >= index || index >= list->size) return false;
Node* node = create_node(data);
Node* prev = list->head->next;
for(int i=1; i<index; i++)
{
prev = prev->next;
}
node->next = prev->next;
prev->next = node;
list->size++;
return true;
}
// 按值删除
bool del_value_list(List* list,TYPE data)
{
if(data == list->head->next->data) return del_head_list(list);
if(data == list->tail->data) return del_tail_list(list);
for(Node* n=list->head->next; n->next; n=n->next)
{
if(n->next->data == data)
{
Node* temp = n->next;
n->next = temp->next;
free(temp);
list->size--;
return true;
}
}
return false;
}
// 按位置删除
bool del_index_list(List* list,int index)
{
if(0 > index || index >= list->size) return false;
if(0 == index) return del_head_list(list);
if(index == list->size-1) return del_tail_list(list);
Node* prev = list->head->next;
for(int i=1; i<index; i++)
{
prev = prev->next;
}
Node* temp = prev->next;
prev->next = temp->next;
free(temp);
list->size--;
return true;
}
// 按位置修改
bool modify_index_list(List* list,int index,TYPE data)
{
if(0 > index || index >= list->size) return false;
Node* n = list->head->next;
for(int i=0; i<index; i++)
{
n = n->next;
}
n->data = data;
return true;
}
// 按值修改 修改全部
bool modify_value_list(List* list,TYPE old,TYPE new)
{
bool flag = false;
for(Node* n=list->head->next; n; n=n->next)
{
if(n->data == old)
{
n->data = new;
flag = true;
}
}
return flag;
}
// 查询
int query_list(List* list,TYPE data)
{
Node* n = list->head->next;
for(int i=0; n; n=n->next,i++)
{
if(n->data == data) return i;
}
return -1;
}
// 排序
void sort_list(List* list)
{
for(Node* n = list->head->next; n->next; n=n->next)
{
for(Node* m = n->next; m; m=m->next)
{
if(n->data > m->data)
{
TYPE temp = n->data;
n->data = m->data;
m->data = temp;
}
}
}
}
// 访问
bool access_list(List* list,int index,TYPE* val)
{
if(0 > index || index >= list->size) return false;
Node* n = list->head->next;
while(index--) n=n->next;
*val = n->data;
return true;
}
// 清空
void clean_list(List* list)
{
// while(list->size) del_head_list(list);
Node* n = list->head->next;
while(n)
{
Node* temp = n;
n = temp->next;
free(temp);
}
list->head->next = NULL;
list->tail = NULL;
list->size = 0;
}
// 销毁
void destory_list(List* list)
{
clean_list(list);
free(list->head);
free(list);
}
// 遍历
void show_list(List* list)
{
for(Node* n = list->head->next; n; n=n->next)
{
printf("%d ",n->data);
}
printf("\n");
}
int main(int argc,const char* argv[])
{
List* list = create_list();
for(int i=0; i<10; i++)
{
add_tail_list(list,rand()%100);
}
show_list(list);
//insert_list(list,10,100);
//del_value_list(list,490);
//del_index_list(list,1);
// modify_index_list(list,9,100);
// modify_value_list(list,77,999);
// printf("index:%d\n",query_list(list,100));
// sort_list(list);
TYPE num = -100;
access_list(list,10,&num);
printf("num=%d\n",num);
clean_list(list);
show_list(list);
}
2、静态链表
节点:
数据域
游标
静态链表的节点存储在连续的内存,通过游标来访问下一个节点
这种链表在插入、删除时只需要修改游标的值,而不用申请、释放内存从而达到链式结构的效果
但是也牺牲了随机访问的功能,主要是给没有指针的编译实现的一种单链表效果
3、循环链表
链表的最后一个节点的next不再指向NULL,而是指向头节点,这种链表叫做单向循环链表,
简称循环链表,它的好处是可以通过任意节点遍历整个链表。
4、双向链表
节点:
前趋指针
数据域
后继指针
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
// 设计双向节点,不构建双向链表结构
typedef struct Node
{
struct Node* prev; // 前趋
TYPE data;
struct Node* next; // 后继
}Node;
// 创建节点
Node* create_node(TYPE data)
{
Node* node = malloc(sizeof(Node));
node->prev = node;
node->data =data;
node->next =node;
return node;
}
// 添加的依赖函数,在p节点与n节点中间插入新节点
void _add_list(Node* p,Node* n,TYPE data)
{
Node* node = create_node(data);
node->next = n;
node->prev = p;
p->next = node;
n->prev = node;
}
// 头添加
void add_head_list(Node* head,TYPE data)
{
_add_list(head,head->next,data); //设计的是带头节点的双链表
}
// 尾添加
void add_tail_list(Node* head,TYPE data)
{
_add_list(head->prev,head,data);
}
// 删除节点node
void _del_list(Node* node)
{
Node* p = node->prev;
Node* n = node->next;
p->next = n;
n->prev = p;
free(node);
}
// 按位置删除
bool del_index_list(Node* head,size_t index)
{
Node* n = head->next;
for(int i=0; i<index; i++)
{
n = n->next;
if(head == n) return false;
}
_del_list(n);
return true;
}
// 按值删除
bool del_value_list(Node* head,TYPE data)
{
for(Node* n=head->next; head != n; n=n->next)
{
if(n->data == data)
{
_del_list(n);
return true;
}
}
return false;
}
// 遍历
void show_list(Node* head)
{
for(Node* n = head->next; head != n; n=n->next)
{
printf("%d ",n->data);
}
printf("\n");
}
int main(int argc,const char* argv[])
{
Node* head = create_node(0); //带头节点的双链表
for(int i=0; i<10; i++)
{
add_head_list(head,i);
}
show_list(head);
del_index_list(head,2);
del_value_list(head,9);
show_list(head);
}
封装双向链表
双向链表数据项:
头节点
节点数量
双向链表的特点:
1、从任意节点都可以遍历整个链表
2、已知节点的位置,可以选择从前到后或者从后到前进行遍历查找,以此提高链表的访问效率
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
// 设计双向链表节点
typedef struct Node
{
struct Node* prev;
TYPE data;
struct Node* next;
}Node;
// 创建节点
Node* create_node(TYPE data)
{
Node* node = malloc(sizeof(Node));
node->prev = node;
node->data = data;
node->next = node;
return node;
}
// 设计双向链表结构
typedef struct DoubleList
{
Node* head; //带头节点
size_t size;
}DoubleList;
// 创建链表
DoubleList* create_list(void)
{
DoubleList* list = malloc(sizeof(DoubleList));
list->head = create_node(0);
list->size = 0;
return list;
}
// 在前驱q和后继n之间添加一个节点
// 下划线开头表示该函数是依赖函数
void _add_list(Node* p,Node* n,TYPE data)
{
Node* node = create_node(data);
node->prev = p;
node->next = n;
p->next = node;
n->prev = node;
}
// 删除当前节点
void _del_list(Node* node)
{
/*
Node* p = node->prev;
Node* n = node->next;
p->next = n;
n->prev = p;
free(node);
*/
node->prev->next = node->next;
node->next->prev = node->prev;
free(node);
}
// 访问指定位置的节点
Node* _index_list(DoubleList* list,size_t index)
{
if(list->size <= index) return NULL;
if(index < list->size/2)
{
// 从前往后
Node* n = list->head->next;
while(index--) n = n->next;
//printf("%d\n",n->data);
return n;
}
else
{
// 从后往前
Node* n =list->head->prev;
while(++index < list->size) n = n->prev;
//printf("%d\n",n->data);
return n;
}
}
// 访问指定数值的节点
Node* _value_list(DoubleList* list,TYPE data)
{
for(Node* n=list->head->next; list->head!=n; n=n->next)
{
if(n->data == data) return n;
}
return NULL;
}
// 头添加
void add_head_list(DoubleList* list,TYPE data)
{
_add_list(list->head,list->head->next,data);
list->size++;
}
// 尾添加
void add_tail_list(DoubleList* list,TYPE data)
{
_add_list(list->head->prev,list->head,data);
list->size++;
}
// 插入
bool insert_list(DoubleList* list,size_t index,TYPE data)
{
Node* node = _index_list(list,index);
if(NULL == node) return false;
_add_list(node->prev,node,data);
list->size++;
return true;
}
// 按位置修改
bool modify_index_list(DoubleList* list,size_t index,TYPE data)
{
Node* node = _index_list(list,index);
if(NULL == node) return false;
node->data = data;
return true;
}
// 按值修改 更新
int modify_value_list(DoubleList* list,TYPE old,TYPE new)
{
int cnt = 0; //修改次数
for(Node* n=list->head->next; n!=list->head; n=n->next)
{
if(n->data == old)
{
n->data = new;
cnt++;
}
}
return cnt;
}
// 访问
bool access_list(DoubleList* list,size_t index,TYPE* data)
{
Node* node = _index_list(list,index);
if(NULL == node) return false;
*data = node->data;
return true;
}
// 查询
int query_list(DoubleList* list,TYPE data)
{
Node* n = list->head->next;
for(int i=0; i<list->size; i++)
{
if(n->data == data) return i;
n = n->next;
}
return -1;
}
// 按位置删除
bool del_index_list(DoubleList* list,size_t index)
{
Node* node = _index_list(list,index);
if(NULL == node) return false;
_del_list(node);
list->size--;
return true;
}
// 按值删除
bool del_value_list(DoubleList* list,TYPE data)
{
Node* node = _value_list(list,data);
if(NULL == node) return false;
_del_list(node);
list->size--;
return true;
}
// 遍历
void show_list(DoubleList* list)
{
for(Node* n=list->head->next; list->head!=n; n=n->next)
{
printf("%d ",n->data);
}
printf("\n");
}
// 清空
void clean_list(DoubleList* list)//清空之后还能向结构体中添加新的节点
{
Node* node = list->head->next;
while(node != list->head)
{
Node* temp = node;
node = node->next;
free(temp);
}
list->head->next = list->head;
list->head->prev = list->head;
list->size = 0;
}
// 销毁
void destory_list(DoubleList* list)//这个结构体就无法使用了
{
clean_list(list);
free(list->head);
free(list);
}
int main(int argc,const char* argv[])
{
DoubleList* list = create_list();
for(int i=0; i<10; i++)
{
add_tail_list(list,i);
}
show_list(list);
insert_list(list,0,100);
insert_list(list,3,100);
insert_list(list,6,100);
show_list(list);
modify_value_list(list,100,999);
show_list(list);
TYPE num = 0;
access_list(list,12,&num);
printf("num=%d\n",num);
printf("index=%d\n",query_list(list,1));
del_index_list(list,12);
del_value_list(list,8);
show_list(list);
clean_list(list);
// destory_list(list);
printf("--------\n");
for(int i=0; i<10; i++)
{
add_tail_list(list,i);
}
show_list(list);
}
5、Linux内核链表
链表的节点无法包含万物,那就让万物来包含链表的节点
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//Linux内核链表
// 遍历
#define list_for_each(n,head) \
for(n=head->next; n!=head; n=n->next)
// 计算结构体成员所处的结构中的地址编号
// type 结构类型 mem结构成员名
#define offset(type,mem) ((int)(&((type*)0)->mem))
// 计算某结构成员所处的结构变量的首地址
// node要计算的node type结构类型 mem结构成员名
#define node_to_obj(node,type,mem) \
(type*)((void*)node-offset(type,mem)) //void为一个字节
//考点就是以上两个宏定义
// 第二种遍历方式
#define list_for_each_entry(obj,head,mem) \
for(obj=node_to_obj(head->next,typeof(*obj),mem); \
&obj->mem != head;\
obj=node_to_obj(obj->mem.next,typeof(*obj),mem))
// 设计链表节点
typedef struct Node
{
struct Node* prev;
struct Node* next;
}Node;
// 创建链表
Node* create_list(void)
{
Node* head = malloc(sizeof(Node));
head->prev = head;
head->next = head;
return head;
}
void _add_list(Node* prev,Node* next,Node* node)
{
next->prev = node;
node->next = next;
node->prev = prev;
prev->next = node;
}
// 头添加
void add_head_list(Node* head,Node* node)
{
_add_list(head,head->next,node);
}
// 尾添加
void add_tail_list(Node* head,Node* node)
{
_add_list(head->prev,head,node);
}
// 判断链表是否为空
bool empty_list(Node* head)
{
return head->next == head;
}
// 删除节点
void _del_list(Node* node)
{
node->next->prev = node->prev;
node->prev->next = node->next;
node->next = NULL;
node->prev = NULL; //不可以free 节点
}
// 头删除
Node* del_head_list(Node* head)
{
if(empty_list(head)) return NULL;
Node* node = head->next;
_del_list(node);
return node;
}
// 尾删除
Node* del_tail_list(Node* head)
{
if(empty_list(head)) return NULL;
Node* node = head->prev;
_del_list(node);
return node;
}
// 按位置删除
Node* del_index_list(Node* head,size_t index)
{
Node* node = head->next;
for(int i=0; i<index; i++)
{
node = node->next;
if(node == head) return NULL;
}
_del_list(node);
return node;
}
/*----------以上都是通用的代码库,下面才是用户使用代码库的内容-------*/
typedef struct Student
{
char age;
int id;
float score;
Node node; //node是个节点,可以通过这个节点访问到下一个节点即next
char sex;
char name[20];
}Student;
int main(int argc,const char* argv[])
{
Student* stu = malloc(sizeof(Student));
printf("---%p %p---\n",stu,&stu->node);
printf("---%p %p---\n",(Student*)0,&((Student*)0)->node);
Node* head = create_list();
for(int i=0; i<10; i++)
{
Student* stu = malloc(sizeof(Student));
sprintf(stu->name,"hehe");
stu->sex = i%2?'w':'m';
stu->age = 20+i;
stu->id = 1001+i;
stu->score = rand()%100;
add_tail_list(head,&stu->node);
printf("%s %c %hhd %d %g\n",
stu->name,stu->sex,stu->age,stu->id,stu->score);
}
printf("====================\n");
del_index_list(head,3);
del_tail_list(head);
del_tail_list(head);
// 测试第二种遍历方式
Student* stu1 = NULL;
list_for_each_entry(stu1,head,node)
{
printf("%s %c %hhd %d %g\n",
stu1->name,stu1->sex,stu1->age,stu1->id,stu1->score);
}
/*
Node* n = NULL;
list_for_each(n,head)
{
// Student* stu = (Student*)n;
Student* stu = node_to_obj(n,Student,node);
printf("%s %c %hhd %d %g\n",
stu->name,stu->sex,stu->age,stu->id,stu->score);
}
*/
}
6、通用链表
节点:
void* ptr;
指针域
运算:常规功能+回调函数
关于链表常考的笔试面试题:
1、单链表逆序 ,在原链表的基础上调整
2、找出链表中的倒数第n个节点
3、判断链表是否有环
4、找出环形链表的入口
5、合并两个有序链表,合并后依然保持有序
6、判断两个链表是否是Y型链表
通用链表头文件
#include <stdbool.h>
//类型重定义函数指针
typedef int (*Compar)(const void*,const void*); //Compar 相当于引用了 int (const void*,const vooid*)
// 链表节点
typedef struct Node
{
void* ptr; //数据域,void*可以转换成任何类型的指针,指针可以指向任何东西,比TYPE更全面
struct Node* prev;
struct Node* next;
}Node;
// 通用链表结构
typedef struct List
{
Node* head;
size_t size;
}List;
// 创建链表
List* create_list(void);
// 添加
void add_list(List* list,void* ptr);
// 插入
bool insert_list(List* list,size_t index,void* ptr);
// 删除 位置
bool del_index_list(List* list,size_t index);
// 删除 值
bool del_value_list(List* list,void* ptr,Compar cmp);
// 查询
void* qurey_list(List* list,void* ptr,Compar cmp);
// 访问
void* visit_list(List* list,size_t index);
// 排序
// 清空
void clean_list(List* list);
// 销毁
void destory_list(List* list);
// 遍历
void show_list(List* list,void (*show)(void* ));
#endif//LIST_H
list.c
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include "list.h"
// 创建节点
static Node* create_node(void* ptr)
{
Node* node = malloc(sizeof(Node));
node->ptr = ptr;
node->prev = node;
node->next = node;
return node;
}
// 两个节点之间插入一个新节点
static void _add_list(Node* prev,Node* next,void* ptr)
{
Node* node = create_node(ptr);
prev->next = node;
next->prev = node;
node->prev = prev;
node->next = next;
}
//删除节点
static void _del_list(Node* node)
{
node->prev->next = node->next;
node->next->prev = node->prev;
free(node->ptr);
free(node);
}
//根据位置访问节点
static Node* _visit_list(List* list,size_t index)
{
if(index >= list->size) return NULL;
if(index < list->size/2)
{
Node* node = list->head->next;
while(index--) node = node->next;
return node;
}
else
{
Node* node = list->head->prev;
while(++index < list->size) node = node->prev;
return node;
}
}
// 创建链表
List* create_list(void)
{
List* list = malloc(sizeof(List));
list->head = create_node(0);
list->size = 0;
return list;
}
// 添加 尾添加
void add_list(List* list,void* ptr)
{
_add_list(list->head->prev,list->head,ptr);
list->size++;
}
// 插入
bool insert_list(List* list,size_t index,void* ptr)
{
Node* node = _visit_list(List* list, size_t index);
if (NULL == node) return false;
_add_list(node->prev,node,ptr);
return true;
}
// 删除 位置
bool del_index_list(List* list,size_t index);
{
Node* node = _visit_list(list,index);
if(NULL == node) return false;
_del_list(node);
list->size--;
return true;
}
// 删除 值
bool del_value_list(List* list,void* ptr,Compar cmp)//Compar是调用者需要提供的函数
{
for(Node* n = list->head->next;n!=list->head;n=n->next)
{
if(0 == cmp(ptr,n->ptr))
{
_del_list(n);
list->size--;
return true;
}
}
return false;
}
// 查询
void* qurey_list(List* list,void* ptr,Compar cmp)
{
for(Node* n = list->head->next;n!=list->head;n=n->next)
{
if(0 == cmp(ptr,n->ptr))
return n->ptr;
}
return NULL;
}
// 访问
void* visit_list(List* list,size_t index)
{
Node* node = _visit_list(list,index);
if(NULL == node) return NULL;
return node->ptr;
}
// 排序
void sort_list(List* list,Compar cmp)
{
for(Node* n = list->head->next;n->next!=list->head;n=n->next)
{
for(Node* m = n->next;m!=list->head;m=m->next)
{
if(-1 == cmp(N->ptr,m->ptr))
{
void* tmp = n->ptr;
n->ptr = m->ptr;
m->ptr = tmp;
}
}
}
}
// 清空
void clean_list(List* list)
{
Node* node = list->head->next;
while(list->size)
{
Node* temp = node;
node = node->next;
free(temp->ptr);
free(temp);
}
list->head->next = list->head;
list->head->prev = list->head;
}
// 销毁
void destory_list(List* list)
{
clean_list(list);
free(list->head->ptr);
free(list->head);
free(list);
}
// 遍历
void show_list(List* list,void (*show)(void* ))
{
for(Node* n=list->head->next; n!=list->head; n=n->next)
{
show(n->ptr);
}
}
text.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "list.h"
// 此文件才是调用者写的代码
typedef struct Student
{
char name[20];
char sex;
int id;
}Student;
// 打印结构体的回调函数由调用者提供
void show_stu(void* ptr)
{
Student* stu = ptr;
printf("姓名:%s 性别:%c 学号:%d\n",stu->name,stu->sex,stu->id);
}
//结构体比较函数也是由调用者提供的回调函数
int stucmp(const void* s1.const void* s2)
{
const Student* stu1 = s1;
const Student* stu2 = s2;
if(stu1->id > stu2->id)
return 1;
if(stu1->id < stu2->id)
return -1;
return 0;
}
int main(int argc,const char* argv[])
{
List* list = create_list();
for(int i=0; i<10; i++)
{
Student* stu = malloc(sizeof(Student));
strcpy(stu->name,"hehe");
stu->sex = i%2?'w':'m';
stu->id = 1001+i;
add_list(list,stu);
}
show_list(list,show_stu);
printf("----------------\n");
Student stu = {"xixi",'w',1001);
Student* stup;
//del_value_list(list,&stu,stucmp);
//insert_list(list,10,&stu);
//del_value_list(list,0);
show_stu(qurey_list(list,&stu,stucmp));
sort_list(list,stucmp);
//show_list(list,show_stu);
}