C语言双向链表讲解
一、双向链表的概念
双向链表基于单链表。单链表是单向的,有一个头结点,一个尾结点,要访问任何结点,都必须知道头结点,不能逆着进行。而双链表添加了一个指针域,通过两个指针域,分别指向结点的前结点和后结点。这样的话,可以通过双链表的任何结点,访问到它的前结点和后结点。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点的地址,一般称为右链域;一个存储直接前驱结点地址,一般称之为左链域。
双向链表结构示意图
表头为空,表头的后继节点为"节点10"(数据为10的节点);"节点10"的后继节点是"节点20"(数据为10的节点),"节点20"的前继节点是"节点10";"节点20"的后继节点是"节点30","节点30"的前继节点是"节点20";...;末尾节点的后继节点是表头。
双链表删除节点
删除"节点30"
删除之前:"节点20"的后继节点为"节点30","节点30" 的前继节点为"节点20"。"节点30"的后继节点为"节点40","节点40" 的前继节点为"节点30"。
删除之后:"节点20"的后继节点为"节点40","节点40" 的前继节点为"节点20"。
双链表添加节点
在"节点10"与"节点20"之间添加"节点15"
添加之前:"节点10"的后继节点为"节点20","节点20" 的前继节点为"节点10"。
添加之后:"节点10"的后继节点为"节点15","节点15" 的前继节点为"节点10"。"节点15"的后继节点为"节点20","节点20" 的前继节点为"节点15"。
二、C语言实现双向链表
2.1 头文件
#pragma once
//新建双向链表。成功返回链表头,否则,返回NULL
extern int create_dLink();
//撤销双向链表,成功返回0,否则返回-1
extern int destory_dLink();
//双向列表是否为空,为空返回1,否则返回0
extern int is_empty_dLink();
//双向链表的大小
extern int dLink_size();
//获取索引index位置的元素,成功返回节点指针,否则返回NULL
extern void* dLink_get(int index);
//获取双向链表中第一个元素,成功返回节点指针,否则返回NULL
extern void* dLink_getFirst();
//获取双向链表中最后一个元素,成功返回节点指针,否则返回NULL
extern void* dLink_getTail();
/*
链表中增加
*/
//在Index位置插值value,成功返回0,否则返回-1;
extern int dLink_insert(int index,void * pVal);
//在表头插入值
extern int dLink_insert_head(void *pVal);
//在表尾插入值
extern int dLink_insert_tail(void *pVal);
/*
链表中删除
*/
//在index处删除
extern int dLink_delete(int index);
//删除第一个节点
extern int dLink_delete_first();
//闪电湖第二个节点
extern int dLink_delete_tail();
#include<stdio.h>
#include "double_link.h"
#include<malloc.h> //双向链表节点
typedef struct My_node
{
struct My_node *prev;
struct My_node *pNext;
void * p;
}my_node;
//b表头不存放元素值
my_node *phead = NULL;
//节点的个数
int node_count = ;
//创建节点,成功返回节点指针,否则,返回NULL
my_node* create_node(void *pVal)
{
my_node *pnode = NULL;
pnode = (my_node*)malloc(sizeof(My_node));
if (!pnode)
{
printf("create pnode error\n");
return NULL;
}
//默认的,pnode的前一节点和后一节点都指向他自己
pnode->prev = pnode->pNext = pnode;
//节点的值为pVal
pnode->p = pVal;
return pnode;
} //新建双向链表 成功返回0 否则返回-1
int create_dLink()
{
phead = create_node(NULL);
if (!phead)
return -;
//设置节点的个数
node_count = ;
return ;
} int destory_dLink()
{
if (!phead)
{
printf("%s failed! dlink is null!\n", __func__);
return -;
}
My_node*pnode = phead->pNext;
my_node* ptmp = NULL;
if (pnode!=phead)
{
ptmp = pnode;
pnode = pnode->pNext;
free(pnode);
}
free(phead);
phead = NULL;
node_count = ;
return ;
} int is_empty_dLink()
{
return node_count==;
} int dLink_size()
{
return node_count;
}
//获取双向链表中第Index位置的节点
my_node* get_node(int index)
{
if (index< || index >= node_count)
{
printf("%s failed ! index out of bound\n", __func__);
return NULL;
}
//正向查找
if (index <= (node_count / ))
{
int i = ;
my_node *pnode = phead->pNext;
while ((i++)<index)
{
pnode = pnode->pNext;
}
return pnode;
}
//反向查找
int j = ;
int rindex = node_count - index - ;
my_node *rnode = phead->prev;
while ((j++)<rindex)
{
rnode = rnode->prev;
}
return rnode;
}
void * dLink_get(int index)
{
my_node *pindex = get_node(index);
if (!pindex)
{
printf("%s failed!\n", __func__);
return NULL;
}
return pindex->p;
} //获取第一个节点
void * dLink_getFirst()
{
return get_node() ;
}
//获取最后一个节点
void * dLink_getTail()
{
return get_node(node_count-);
}
//将值插入到index位置,成功返回0;否则 返回-1
int dLink_insert(int index, void * pVal)
{
//插入表头
if (index == )
return dLink_insert_head(pVal);
//获取要插入位置对应的节点
my_node* pindex = get_node(index);
if (!pindex)
return -;
//创建节点
my_node* pnode = create_node(pVal);
if (!pnode)
return -;
pnode->prev = pindex->prev;
pnode->pNext = pindex;
pindex->prev->pNext = pnode;
pindex->prev = pnode;
node_count++;
return ;
}
//数值插入表头
int dLink_insert_head(void * pVal)
{
my_node* pnode = create_node(pVal);
if (!pnode)
return -;
pnode->prev = phead;
pnode->pNext = phead->pNext; phead->pNext->prev = pnode;
phead->pNext = pnode;
node_count++;
return ;
} int dLink_insert_tail(void * pVal)
{
my_node* pnode = create_node(pVal);
if (!pnode)
return -;
pnode->pNext = phead;
pnode->prev = phead->prev;
phead->prev->pNext = pnode;
phead->prev = pnode;
return ;
} int dLink_delete(int index)
{
my_node* pindex = get_node(index);
if (!pindex)
{
printf("%s failed! the index in out of bound\n",__func__);
return -;
}
pindex->pNext->prev = pindex->prev;
pindex->prev->pNext = pindex->pNext;
free(pindex);
node_count--;
return ;
} int dLink_delete_first()
{
return dLink_delete();
} int dLink_delete_tail()
{
return dLink_delete(node_count-);
}
2.3 test测试代码
#include<stdio.h>
#include"double_link.h"
//1.双向链表操作数为int
void int_test()
{
int arr[] = {,,,,,,,,,};
printf("xxxxxxxxxxxxxxxxx\n");
create_dLink(); //创建链表
dLink_insert(, &arr[]); //双向链表表头插入
dLink_insert(, &arr[]); //双向链表表头插入
dLink_insert(, &arr[]); //双向链表表头插入
dLink_insert(, &arr[]); //双向链表表头插入
dLink_insert(, &arr[]); //双向链表表头插入
dLink_insert(, &arr[]); //双向链表表头插入
printf("is_empty_dLink()=%d\n",is_empty_dLink()); //双向链表是否为空
printf("dLink_size()=%d\n", dLink_size()); //双向链表的大小
//遍历双向链表
int i ;
int * p ;
int sz = dLink_size();
for ( i = ; i < sz; i++)
{
p = (int*)dLink_get(i);
printf("dLink_get(%d)=%d\n",i,*p);
}
destory_dLink();
} //2.操作数为字符串
void string_test()
{
char* str[] = {"one","two","three","four","five"};
create_dLink(); //创建链表
dLink_insert(, str[]); //双向链表表头插入
dLink_insert(, str[]); //双向链表表头插入
dLink_insert(, str[]); //双向链表表头插入
printf("is_empty_dLink()=%d\n", is_empty_dLink()); //双向链表是否为空
printf("dLink_size()=%d\n", dLink_size()); //双向链表的大小
//遍历双向链表
int i ;
char * p ;
int sz = dLink_size();
for (i = ; i < sz; i++)
{
p = (char*)dLink_get(i);
printf("dLink_get(%d)=%s\n", i, p);
}
destory_dLink();
}
//3.双向链表为结构体
typedef struct MyStruct
{
int id;
char name[];
} stu;
stu arr_stu[] =
{
{,"lii"},
{ ,"mike" },
{ ,"lucky" },
{ ,"eric" },
};
#define arr_stu_size ((sizeof(arr_stu))/(sizeof(arr_stu[0])))
void stuc_test()
{
create_dLink(); //创建链表
dLink_insert(, &arr_stu[]); //双向链表表头插入
dLink_insert(, &arr_stu[]); //双向链表表头插入
dLink_insert(, &arr_stu[]); //双向链表表头插入
printf("is_empty_dLink()=%d\n", is_empty_dLink()); //双向链表是否为空
printf("dLink_size()=%d\n", dLink_size()); //双向链表的大小
//遍历双向链表
int i ;
stu * p ;
int sz = dLink_size();
for (i = ; i < sz; i++)
{
p = (stu*)dLink_get(i);
printf("dLink_get(%d)=[%d,%s]\n", i, p->id,p->name);
}
destory_dLink();
}
int main()
{
int_test();
string_test();
stuc_test(); return ;
}
2.34结果显示
C语言双向链表讲解的更多相关文章
- C语言双向链表
原文:C语言双向链表 今天写了点双向链表的各种操作,写插入的时候费了点时间,不过,现在看来还是值得耗费那点时间去写的,这种小东西应该能信手拈来才行啊. /*双向链表*/ #include <st ...
- 第二十四节:Java语言基础-讲解数组的综合应用
数组的综合应用 // 打印数组 public static void printArray(int[] arr) { for(int x=0;x<arr.length;x++) { if(x!= ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)
卷 backup 的文件夹 PATH 列表卷序列号为 00000025 D4A8:14B0J:.│ 1.txt│ c语言经典案例效果图示.doc│ ├─1传智播客_尹成_C语言从菜鸟到高手_第一 ...
- 贪吃蛇游戏——C语言双向链表实现
采用了双向链表结点来模拟蛇身结点: 通过C语言光标控制函数来打印地图.蛇身和食物: /************************** *************************** 贪吃 ...
- Go语言之讲解GOROOT、GOPATH、GOBIN
Go是一门全新的静态类型开发语言,具有自动垃圾回收,丰富的内置类型,函数多返回值,错误处理,匿名函数,并发编程,反射等特性. go命令依赖一个重要的环境变量:$GOPATH GOPATH允许多个目录, ...
- (C/C++学习)18.C语言双向链表
说明:数组提供了连续内存空间的访问和使用,而链表是对内存零碎空间的有效组织和使用.链表又分为单向链表和双向链表,单向链表仅提供了链表的单方向访问,相比之下,双向链表则显得十分方便. 一.单向链表的节点 ...
- c语言递归讲解分析
C语言允许函数调用它自己,这种调用的过程称为"递归(recursion)" 举例说明,如下代码: #include <stdio.h> void up_and_down ...
- C语言递归函数讲解
递归函数是什么? 是函数.................... 你可以把它理解成是for循环与死循环的结合的函数.简单的说:递归函数是有条件终止的死循环函数: 死循环函数这里是指在函数体中调用自身: ...
- C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻
这是道哥的第014篇原创 目录 一.前言 二.变量与指针的本质 1. 内存地址 2. 32位与64位系统 3. 变量 4. 指针变量 5. 操作指针变量 5.1 指针变量自身的值 5.2 获取指针变量 ...
随机推荐
- c++中的类(构造函数,析构函数的执行顺序)
类对象的初始化顺序 新对象的生成经历初始化阶段(初始化列表显式或者隐式的完成<这部分有点像java里面的初始化块>)——> 构造函数体赋值两个阶段 1,类对象初始化的顺序(对于没有父 ...
- File类 ReadAllBytes() ReadAllLines() ReadAllText()
用 File 类里的 ReadAllLines方法读取文件string[] contents = File.ReadAllLines(@"F:\Users\zhuotu003\Desktop ...
- JSON.stringify方法报错:Converting circular structure to JSON
别以为JSON.parse(JSON.stringify(data))做深拷贝无敌,对于以下这种情况,当你需要保留父级对象,即 对象存在循环引用,就会报错. var a = [ { "id& ...
- leetcode-mid-backtracking -46. Permutations-NO
mycode 没有通过,其实只需要把temp.append改为temp+[nums[i]]即可 def permute(nums): def dfs(res,nums,temp): print(num ...
- acl权限设置
acl权限分配 1.setfacl设置文件或目录的权限 setfacl -m u:user:rw text setfacl -m user:rwx /mnt 2.getfacl查看文件或目录的权限 g ...
- Mybaits二级缓存的使用与配置
什么是延迟加载 resultMap中的association和collection标签具有延迟加载的功能. 延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息.使用关联信息时再去加载关联信 ...
- 【洛谷T89353 【BIO】RGB三角形】
题目链接 这个题我一开始显然直接暴力 然后30分(但是应用数据分治的我通过复杂度判断并且其余输出0的能力硬生生的拿下了60分) 主要还是讲正解 这里有一个结论 这样一个图,红点的值可以通过两个黄点来判 ...
- PADS LAYOUT的一般流程
1.概述 本文档的目的在于说明使用PADS的印制板设计软件PowerPCB进行印制板设计的流程和一些注意事项,为一个工作组的设计人员提供设计规 范,方便设计人员之间进行交流和相互检查. 2.设计 ...
- 原生JS去重
方式一: function deleteRepetionChar(arr){ //先判断输入进来的是数组对象还是字符串 if( typeof arr == "object"){ v ...
- Windows操作路由表
route print route add 172.17.0.0 mask 255.255.0.0 192.168.99.100 route delete 172.17.0.0 mask 255.25 ...