一、链表的设计原理(一般建议使用堆空间存放链表)
-
设计链表的节点(数据域+指针域)
-
创建链表头结点(head)头结点相当于定义这一条链表的起点(钥匙)
初始化链表头结点(数据域 + 指针域) -
创建链表子节点(node)子节点插入到头结点后面(可以存储更多信息)
初始化链表子结点(数据域 + 指针域) -
插入节点(insert)将创建的节点放进链表里面,创立完整链表。
尾插法,将新节点放置链表末尾
头插法,将新节点放置头结点后一个
排序插法,可以按冲从小到大,也可以从大到小 -
剔除节点(delete)
将不需要节点删除,将链表不需要的节点从链表中拿出来,从此和链表无关。
原理是:使用链表头结点,遍历整个链表,将各节点中的数据与待删除数据作用是比对,可找到剔除节点的位置。将该节点从链表中删除。 -
修改节点内的数据
更改节点数据域中的内容 -
销毁链表
如果链表是使用堆空间存储,销毁链表时需使用free函数将每个节点的堆空间释放
二、链表代码设计 (双向循环链表)
- 设计链表的节点(数据域+指针域)
typedef struct double_clink_list
{
int data; //数据域
struct double_clink_list * next; //后继指针
struct double_clink_list * prev; //前驱指针
}double_cll,*double_cll_p; //重命名
- 创建链表头结点(head)
/**********************************************************************
**函数 :double_cll_p init_double_cll(void)
**函数说明 : 创建头结点
**形参说明 :无
**返回值 : 成功返回 新创建的头结点地址
失败返回 空地址
**********************************************************************/
double_cll_p init_double_cll(void) //创建头节点
{
double_cll_p head = calloc(1,sizeof(double_cll)); //申请堆空间存放头结点
if(head == NULL) //判断申请是否成功
{
printf("init_double_cll error\n");
return NULL; //失败直接返回
}
head->data = 0; //初始化数据域
head->next = head; //初始化后继指针
head->prev = head; //初始化前驱指针
return head; //返回头结点地址
}
- 创建链表子节点(node)
/**********************************************************************
**函数 :double_cll_p create_node_double_cll(int data)
**函数说明 : 创建子节点
**形参说明 : data 为存放在 子节点数据域的数据
**返回值 : 成功返回 新创建的子结点地址
失败返回 空地址
**********************************************************************/
double_cll_p create_node_double_cll(int data) //创建子节点
{
double_cll_p node = calloc(1,sizeof(double_cll)); //申请堆空间存放子节点
if(node == NULL) //判断申请是否成功
{
printf("create_node_double_cll error\n");
return NULL; //失败直接返回
}
node->data = data; //初始化数据域
node->next = node; //初始化后继指针
node->prev = node; //初始化前驱指针
return node; //返回子结点地址
}
- 插入节点(insert)
/**********************************************************************
**函数 :int tail_insert_double_cll(double_cll_p node,double_cll_p head)
**函数说明 : 尾插法,将新节点放置链表末尾
**形参说明 : node 为待插入子节点的地址 head为链表头节点的地址
**返回值 : 成功返回 0
失败返回 -1
**********************************************************************/
int tail_insert_double_cll(double_cll_p node,double_cll_p head)//尾插法,将新节点放置链表末尾
{
if(head == NULL || node == NULL) //判断头结点与子节点地址是否为空
{
printf("tail_insert_double_cll error\n");
return -1; //失败直接返回-1
}
//以下部分理解不了的自己画个图 或者 看我下面的图
node->prev = head->prev; //子节点前驱指针 指向 头结点前驱指针指向的子节点
node->next = head; //子节点后继指针 指向 头结点
head->prev = node; //头节点前驱指针 指向 子结点
node->prev->next = node; //头结点前驱指针 指向 的子节点的后继指针 指向 新子结点
return 0; //成功返回0
}
尾插图解
/**********************************************************************
**函数 :int head_insert_double_cll(double_cll_p node,double_cll_p head)
**函数说明 : 头插法,将新节点放置头结点后一个
**形参说明 : node 为待插入子节点的地址 head为链表头节点的地址
**返回值 : 成功返回 0
失败返回 -1
**********************************************************************/
int head_insert_double_cll(double_cll_p node,double_cll_p head)//头插法,将新节点放置头结点后一个
{
if(head == NULL|| node == NULL) //判断头结点与子节点地址是否为空
{
printf("head_insert_double_cll error\n");
return -1; //失败直接返回-1
}
//以下部分理解不了的自己画个图
node->next = head->next; //子节点后继指针 指向 头结点后继指针指向的子节点
head->next = node; //头节点后继驱 指针 指向子结点
node->prev = head; //子节点前驱指针 指向 头结点
node->next->prev = node; //头结点后继指针 指向 的子节点的前驱指针 指向 新子结点
return 0; //成功返回0
//或者
//将新节点插入到head->next的前面
//tail_insert_double_cll(node,head->next);
}
/**********************************************************************
**函数 :int sort_insert_double_cll(double_cll_p node,double_cll_p head)
**函数说明 : 排序插法,可以按从从小到大排序插入,也可以从大到小排序插入
**形参说明 : node 为待插入子节点的地址 head为链表头节点的地址
**返回值 : 成功返回 0
失败返回 -1
**********************************************************************/
int sort_insert_double_cll(double_cll_p node,double_cll_p head)//排序插法,可以按冲从小到大,也可以从大到小
{
if(head == NULL || node == NULL) //判断头结点与子节点地址是否为空
{
printf("sort_insert_double_cll error\n");
return -1; //失败直接返回-1
}
double_cll_p p = head; //定义一个指针变量存放头结点地址
for(;p->next != head;p = p->next) //遍历整个链表,如果指针p再次指向head地址,说明已经遍历完
{
if(p->next->data > node->data) //从小到大排序 插入
{
node->next = p->next;
p->next = node;
node->next->prev = node;
node->prev = p;
return 0; //插入成功返回0
}
// if(p->next->data < node->data) //从大到小排序插入
// {
// node->next = p->next;
// p->next = node;
// node->next->prev = node;
// node->prev = p;
// return 0; //插入成功返回0
// }
}
//如果链表只有头结点,那么子节点直接插入即可
node->next = p->next;
p->next = node;
node->next->prev = node;
node->prev = p;
return 0; //插入成功返回0
}
- 剔除节点(delete)
/**********************************************************************
**函数 :int del_double_cll(double_cll_p p,double_cll_p head)
**函数说明 : 删除指定地址的节点
**形参说明 : p 为待剔除子节点的地址 head为链表头节点的地址
**返回值 : 成功返回 0
失败返回 -1
**********************************************************************/
int del_double_cll(double_cll_p p,double_cll_p head)//删除指定地址的节点
{
if(p == NULL || head == NULL) //判断头结点与子节点地址是否为空
{
printf("del_double_cll error\n");
return -1; //失败直接返回-1
}
p->next->prev = p->prev; //将要删除的子节点断链
p->prev->next = p->next; //将要删除的子节点断链
p->next = NULL; //将要删除的子节点后继指针 指向 空
p->prev = NULL; //将要删除的子节点前驱指针 指向 空
free(p); //释放子节点的堆空间
return 0; //剔除成功返回0
}
- 查找对应数据节点并返回该节点地址
/**********************************************************************
**函数 :double_cll_p find_double_cll(int data,double_cll_p head)
**函数说明 : 查找对应数据节点并返回该节点地址
**形参说明 : data 为待查找数据 head为链表头节点的地址
**返回值 : 成功返回 待查找数据节点地址
失败返回 空地址
**********************************************************************/
double_cll_p find_double_cll(int data,double_cll_p head)//查找对应数据节点并返回该节点地址
{
if(head == NULL) //判断头结点地址是否为空
{
printf("find_double_cll error\n");
return NULL; //失败直接返回空地址
}
double_cll_p p = head->next; //定义一个指针变量 存放 头结点后继指针指向节点 的地址
for(;p != head; p = p->next) //遍历整个链表,如果指针p再次指向head地址,说明已经遍历完
{
if(p->data == data) //遍历的过程中,如果子节点数据 等于 待查找数据,则返回该子节点地址
return p; //返回子节点地址
}
return NULL; //找不到返回 空地址
}
- 销毁链表
/**********************************************************************
**函数 :int free_double_cll(double_cll_p head)
**函数说明 : 释放链表所有内存
**形参说明 : head为链表头节点的地址
**返回值 : 成功返回 0
失败返回 -1
**********************************************************************/
int free_double_cll(double_cll_p head)//释放链表所有内存
{
if(head == NULL) //判断头结点地址是否为空
{
printf("double_cll not exist\n");
return -1; //失败直接返回-1
}
double_cll_p p = head; //定义一个指针变量 存放 头结点 的地址
for(;p->next != head;) //遍历整个链表,如果指针p再次指向head地址,说明已经遍历完
{
p = p->next; //变量p指向下一个节点
free(p->prev); //释放前一个节点的 堆空间(断链)
}
free(p); //释放头节点 堆空间
return 0; //链表销毁成功返回 0
}
完整 C程序 和 头文件
//双向循环链
#include "double_cll.h"
int main(int argc,char *argv[])
{
double_cll_p head = init_double_cll();//创建头结点
int data;//输入数据
double_cll_p q = NULL;//存放需要删除节点的地址
while(1)
{
scanf("%d",&data);//从键盘读取一个数据
if(data>0)
{
if(NULL==find_double_cll(data,head))//禁止输入重复的数据
{
double_cll_p node = create_node_double_cll(data);//创建子节点
sort_insert_double_cll(node,head);//使用排序插入方式将子节点插入到链表
}
else
{
printf("data repetition \n");
}
show_double_cll(head); //使用链表正顺序输出
}
else if(data ==0)
break;
else
{
if((q=find_double_cll(-data,head))!=NULL)//查找是否存在需要删除的数据
{
del_double_cll(q,head);//删除指定节点地址的 整个节点
}
else
{
printf("data absence \n");
}
show_double_cll(head);//使用链表正顺序输出
}
}
free_double_cll(head);//释放内存
return 0;
}
double_cll_p init_double_cll(void)//创建头节点
{
double_cll_p head = calloc(1,sizeof(double_cll));
if(head == NULL)
{
printf("init_double_cll error\n");
return NULL;
}
head->data = 0;
head->next = head;
head->prev = head;
return head;
}
double_cll_p create_node_double_cll(int data)//创建子节点
{
double_cll_p node = calloc(1,sizeof(double_cll));
if(node == NULL)
{
printf("create_node_double_cll error\n");
return NULL;
}
node->data = data;
node->next = node;
node->prev = node;
return node;
}
int tail_insert_double_cll(double_cll_p node,double_cll_p head)//尾插法,将新节点放置链表末尾
{
if(head == NULL || node == NULL)
{
printf("tail_insert_double_cll error\n");
return -1;
}
node->prev = head->prev;
node->next = head;
head->prev = node;
node->prev->next = node;
return 0;
}
int head_insert_double_cll(double_cll_p node,double_cll_p head)//头插法,将新节点放置头结点后一个
{
if(head == NULL|| node == NULL)
{
printf("head_insert_double_cll error\n");
return -1;
}
node->next = head->next;
head->next = node;
node->prev = head;
node->next->prev = node;
return 0;
//将新节点插入到head->next的前面
//tail_insert_double_cll(node,head->next);
}
int sort_insert_double_cll(double_cll_p node,double_cll_p head)//排序插法,可以按冲从小到大,也可以从大到小
{
if(head == NULL || node == NULL)
{
printf("sort_insert_double_cll error\n");
return -1;
}
double_cll_p p = head;
for(;p->next != head;p = p->next)
{
if(p->next->data > node->data)//从小到大排序插入
{
node->next = p->next;
p->next = node;
node->next->prev = node;
node->prev = p;
return 0;
}
// if(p->next->data < node->data)//从大到小排序插入
// {
// node->next = p->next;
// p->next = node;
// node->next->prev = node;
// node->prev = p;
// return 0;
// }
}
node->next = p->next;
p->next = node;
node->next->prev = node;
node->prev = p;
return 0;
}
int show_double_cll(double_cll_p head)//按链表正顺序输出
{
if(head == NULL)
{
printf("show_double_cll error\n");
return -1;
}
double_cll_p p = head;
for(;p->next!=head;p = p->next)
{
printf("%d\t",p->next->data);
}
printf("\n");
return 0;
}
double_cll_p find_double_cll(int data,double_cll_p head)//查找对应数据节点并返回该节点地址
{
if(head == NULL)
{
printf("find_double_cll error\n");
return NULL;
}
double_cll_p p = head->next;
for(;p != head; p = p->next)
{
if(p->data == data)
return p;//重复返回1
}
return NULL;//没有重复
}
int free_double_cll(double_cll_p head)//释放链表所有内存
{
if(head == NULL)
{
printf("double_cll not exist\n");
return -1;
}
double_cll_p p = head;
for(;p->next != head;)
{
p = p->next;
free(p->prev);
}
free(p);
return 0;
}
int del_double_cll(double_cll_p p,double_cll_p head)//删除指定地址的节点
{
if(p == NULL || head == NULL)
{
printf("del_double_cll error\n");
return -1;
}
p->next->prev = p->prev;
p->prev->next = p->next;
p->next = NULL;
p->prev = NULL;
free(p);
return 0;
}
#ifndef __double_cll_h__
#define __double_cll_h__
#include<stdio.h>
#include <stdlib.h>
typedef struct double_clink_list
{
int data;
struct double_clink_list * next;
struct double_clink_list * prev;
}double_cll,*double_cll_p;
double_cll_p init_double_cll(void);//创建头节点
double_cll_p create_node_double_cll(int data);//创建子节点
int tail_insert_double_cll(double_cll_p node,double_cll_p head);//尾插法,将新节点放置链表末尾
int head_insert_double_cll(double_cll_p node,double_cll_p head);//头插法,将新节点放置头结点后一个
int sort_insert_double_cll(double_cll_p node,double_cll_p head);//排序插法,可以按冲从小到大,也可以从大到小
int show_double_cll(double_cll_p head);//按链表正顺序输出
double_cll_p find_double_cll(int data,double_cll_p head);//查找对应数据节点并返回该节点地址
int free_double_cll(double_cll_p head);//释放链表所有内存
int del_double_cll(double_cll_p p,double_cll_p head);//删除指定地址的节点
#endif
结束语:需要完整C程序的私信我发邮箱