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 获取指针变量 ...
随机推荐
- HDU 2795 Billboard (线段树单点更新 && 求区间最值位置)
题意 : 有一块 h * w 的公告板,现在往上面贴 n 张长恒为 1 宽为 wi 的公告,每次贴的地方都是尽量靠左靠上,问你每一张公告将被贴在1~h的哪一行?按照输入顺序给出. 分析 : 这道题说明 ...
- Windows10 + VS2015 环境下对gdal2.0.1进行64bit编译小结
这是官方给出的编译指导,但是在实践过程中有几点仍然需要特别注意. Tip 1:不要使用默认的"VS开发人员命令提示"工具,使用该工具会遭遇如下的错误: 正在创建库 gdal_i.l ...
- 解决vuex保存的数据刷新页面时清空
参考文章: vuex中store保存的数据,刷新页面会清空 主要解决代码: 1.更改store文件下index文件state的定义 const store = new Vuex.Store({ sta ...
- Codeforces Aim Tech Round4 (Div2) D
题目链接: 题意: 给你一个严格升序的单链表,但是是用数组来存放的.对于每一个位置来说,你可以知道这个位置的值和下一个的位置.你每一个可以询问一个位置,机器会告诉你这个位置的值,和下一个位置的指针.要 ...
- 【HTTP/FTP客户端库】
[HTTP/FTP客户端库]资料来源:http://curl.haxx.se/libcurl/competitors.html Free Software and Open Source projec ...
- 惠普DL360G6安装ESXi主机后设置多块网卡
需要先把服务器的网线连接到路由器 然后打开esxi设置网络的netwoork adapters 选中多块网卡,点确定保持 然后在到esxi客户端操作: 直接下一步 这里填上路由器分配的网段ip即可了
- EDM营销技巧之如何进行用户唤醒
一般来说,EDM营销中肯定有些用户就长期不关注我们的邮件.这个时候我们应该如何唤醒用户呢?本文给大家分享一下技巧. 首先,我们要注重电子邮件地址的价值.只有好的新的邮件地址库,才能引起用户足够的关注. ...
- 求旋转数组的最小数字(剑指offer)
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数 ...
- 使用js如何获取treeview控件的当前选中的节点
var selectedNodeID = theForm.elements["<%=treeView1.ClientID%>_SelectedNode"].value; ...
- 关于ajax请求controller返回中文乱码的解决方法!
问题描述:前台ajax请求 Spring框架的 controller 返回的本应该是一段中文字符串 ,结果返回了一串 ?????? 解决办法:在RequestMapping的属性中添加返回数据类型属性 ...