前言:
我比较喜欢用“结点”而不是“节点”
定义链表的结点结构体:
//单链表
struct node
{
int num;
struct node * next;
};
typedef struct node Node;
typedef Node * Link;
//双链表
struct dbnode
{
int num;
struct node * next;
struct node * prior;
};
typedef struct dbnode dbNode;
typedef Node * dbLink;
链表中主要有以下这么几种操作:
void init(Link *head); //初始化链表
void display(Link head); //输出链表
void release(Link *head); //释放链表
void destroy(Link head); //清空链表
void create_node(Link *new_node); //结点分配空间
int is_malloc_ok(Link new_node); //结点分配成功
void insert_head(Link head,Link new_node); //头插入
void insert_tail(Link head,Link new_node); //尾插入
void insert_mid_head(Link head, Link new_node, int num); //在num前插入(没有num则不插入)
void insert_mid_tail(Link head,Link new_node,int num); //在num后插入(没有num则不插入)
void delete(Link head,int num); //删除
单链表、双链表、循环链表、等等
这些都是线性表的链式存储结构,我们在做题目做项目的时候,可以去考虑使用哪一种来解决问题,具体问题具体分析
/********************************
给结点分配空间
当分配不成功的时候重新分配
********************************
void create_node(Link *new_node)
{
do{
*new_node = (Link) malloc (sizeof(dblNode));
}while(*new_node == NULL);
}
/********************************
判断结点是否分配成功
给结点分配空间
当分配不成功的时候重新分配
*********************************/
#define MALLOC_MAX 10
int is_malloc_ok(dbLink new_node)
{
if(NULL == new_node)
{
return 0;
}
return 1;
}
void create_node(Link *new_node)
{
int count = MALLOC_MAX;
do
{
*new_node = (Link)malloc(sizeof(Node));
count--;
}while((!is_malloc_ok(*new_node)) && (count != 0));
}
/********************************
随机给结点赋值
*********************************/
#include <stdlib.h>
#include <time.h>
void random_link(dbLink head,int n)
{
int i;
dbLink new_node;
for(i = 0;i < n;i++)
{
create_node(&new_node);
new_node->num = rand() % 100;
//insert_head(head,new_node);
insert_tail(head,new_node);
}
}
单向链表
单向链表仅包含两个域(数据域和指针域)
数据域存储结点的数据
指针域指向表中的下一个结点,而最后一个节点则指向NULL
适用于简单操作,或者增加和删除操作比较多的具体应用中
单向链表只可向一个方向遍历。查找一个结点的时候需要从从第一个结点依次遍历,直到访问到需要的位置。(不一定似的从第一个结点,看程序员自己的想法)
带头结点的单链表比较常用
双向链表
双向链表包含(一个数据域和两个指针域)
头结点的prior指向NULL,尾结点的next指向NULL。
这样可以从任何一个结点访问其前驱结点、后继结点,以至整个链表。
适用于需要大批量数据查询的具体应用中
由于占用额外的空间以存储指针,若是用来实现简单操作,可能会浪费空间,因为单链表即可解决这些问题。
循环的双向链表比较常用
//带头结点的双链表
/***********************************************
头插
************************************************/
void insert_head(dbLink head,dbLink new_node)
{
new_node->next = head->next;
new_node->prior = head;
head->next = new_node;
if(new_node->next != NULL)
{
new_node->next->prior = new_node;
}
}
/***********************************************
尾插
************************************************/
void insert_tail(dbLink head,dbLink new_node)
{
dbLink p = head;
while(p->next != NULL)
{
p = p->next;
}
new_node->next = p->next;
new_node->prior = p;
p->next = new_node;
}
/***********************************************
删除
************************************************/
void delete(dbLink head,int num )
{
dbLink p = head->next;
while(p != NULL && p->num != num)
{
p = p->next;
}
if(p != NULL)
{
p->prior->next = p->next ;
if(p->next != NULL)
{
p->next->prior = p->prior;
}
free(p);
}
else
{
printf("找不到该结点,无法执行删除!\n");
}
}
循环链表
循环链表的结构体与单链表、双链表相同
唯一的不同点是:单链表的尾结点next指针域指向链表头;双链表的头结点prior指向尾结点,尾结点的next指向头
使用带头结点的循环单链表实现插入和删除操作时,算法实现较为方便
只要知道表中任何一个结点的地址,就可以遍历链表中任意结点
适用于需要进行大量增加、删除元素操作而对访问元素无要求的,及预先无法确定表的大小的具体问题
//循环双链表
/***********************************************
头插
************************************************/
void insert_head(dbLink head,dbLink new_node)
{
new_node->next = head->next;
new_node->prior = head;
head->next = new_node;
new_node->next->prior = new_node;
}
/***********************************************
尾插
************************************************/
void insert_tail(dbLink head,dbLink new_node)
{
//head->prior = new_node;
new_node->prior = head->prior;
//head->next = new_node;
head->prior->next = new_node;
head->prior = new_node;
new_node->next = head;
}
/***********************************************
删除
************************************************/
相关题目:(没来得及写)
- 题目:利用链表实现一个先入后出的栈结构,并提供栈操作的push和pop的接口
- 题目:使用双向链表来实现双向队列,并且让队列具有以下的操作:(1)判断队列是否为空(2)得到双向队列中元素的个数(3)向左端添加一个新元素(4)向右端添加一个新元素(5)从左端删除一个元素(6)从右端删除一个元素
- 排序链表(按大小插入)
- 排序链表(插入后排序)
- 反转单向和双向链表
- 反转部分单向链表
- 环形单链表的约瑟夫问题
- 程序功能:建立一个带有头结点的单向链表,并将存储在数组中的字符依次转储到链表的各个结点中。
- 编写程序STUDENT *Create(STUDENT studs[],int n)。STUDENT是一个结构类型,包含姓名、成绩和指针域。studs数组中存储了n个STUDENT记录。create函数的功能是根据studs数组建立一个链表,链表中结点按成绩降序排列,函数返回链表头指针。
- 打印两个有序链表的公共部分
- 在单链表和双链表中删除倒数第K个结点
- 删除链表的中间结点和a/b处的结点
- 判断一个链表是否为回文结构
- 将单向链表按某值划分成左边小、中间相等、右边大的形式
- 赋值含有随机指针结点的链表
- 两个单链表生成相加链表
- 两个单链表相交的一系列问题
- 将单链表的每K结点之间逆序
- 删除无序单链表中值重复出现的结点
- 将搜索二叉树转换成双向链表
- 单链表的选择排序
- 一种怪异的结点删除方式
- 向有序的环形单链表中插入新结点
- 合并两个有序的单链表
- 按照左右半区的方式重新组合单链表