一、双向链表的概念

双向链表基于单链表。单链表是单向的,有一个头结点,一个尾结点,要访问任何结点,都必须知道头结点,不能逆着进行。而双链表添加了一个指针域,通过两个指针域,分别指向结点的前结点和后结点。这样的话,可以通过双链表的任何结点,访问到它的前结点和后结点。

在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点的地址,一般称为右链域;一个存储直接前驱结点地址,一般称之为左链域。

双向链表结构示意图

表头为空,表头的后继节点为"节点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();
2.2 .c
 #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语言双向链表讲解的更多相关文章

  1. C语言双向链表

    原文:C语言双向链表 今天写了点双向链表的各种操作,写插入的时候费了点时间,不过,现在看来还是值得耗费那点时间去写的,这种小东西应该能信手拈来才行啊. /*双向链表*/ #include <st ...

  2. 第二十四节:Java语言基础-讲解数组的综合应用

    数组的综合应用 // 打印数组 public static void printArray(int[] arr) { for(int x=0;x<arr.length;x++) { if(x!= ...

  3. 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)

    卷 backup 的文件夹 PATH 列表卷序列号为 00000025 D4A8:14B0J:.│  1.txt│  c语言经典案例效果图示.doc│  ├─1传智播客_尹成_C语言从菜鸟到高手_第一 ...

  4. 贪吃蛇游戏——C语言双向链表实现

    采用了双向链表结点来模拟蛇身结点: 通过C语言光标控制函数来打印地图.蛇身和食物: /************************** *************************** 贪吃 ...

  5. Go语言之讲解GOROOT、GOPATH、GOBIN

    Go是一门全新的静态类型开发语言,具有自动垃圾回收,丰富的内置类型,函数多返回值,错误处理,匿名函数,并发编程,反射等特性. go命令依赖一个重要的环境变量:$GOPATH GOPATH允许多个目录, ...

  6. (C/C++学习)18.C语言双向链表

    说明:数组提供了连续内存空间的访问和使用,而链表是对内存零碎空间的有效组织和使用.链表又分为单向链表和双向链表,单向链表仅提供了链表的单方向访问,相比之下,双向链表则显得十分方便. 一.单向链表的节点 ...

  7. c语言递归讲解分析

    C语言允许函数调用它自己,这种调用的过程称为"递归(recursion)" 举例说明,如下代码: #include <stdio.h> void up_and_down ...

  8. C语言递归函数讲解

    递归函数是什么? 是函数.................... 你可以把它理解成是for循环与死循环的结合的函数.简单的说:递归函数是有条件终止的死循环函数: 死循环函数这里是指在函数体中调用自身: ...

  9. C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻

    这是道哥的第014篇原创 目录 一.前言 二.变量与指针的本质 1. 内存地址 2. 32位与64位系统 3. 变量 4. 指针变量 5. 操作指针变量 5.1 指针变量自身的值 5.2 获取指针变量 ...

随机推荐

  1. selenium,控制滚动条

    今天写selenium用例的时候,遇见奇葩的问题,FF下是没有错误的,但是在chrome和ie下就会有问题,后来发现是 操作中点击一个按钮,在页面不可见,就会导致异常,解决方法如下: element ...

  2. Oracle数据库表空间查看和更改

    set linesize 200  --设置输出一行字符个数为200 1.查看表空间名称和大小(单位MB) SELECT t.tablespace_name, round(SUM(bytes / (1 ...

  3. Loadrunner参数化避免重复数据

    1.我们性能测试过程中经常遇到需要创建很多数据来运行测试场景,但是如果数据准备不够多,可能会造成数据不够用,导致场景运行失败,下面简单的例子: 2.我们对用户名分别使用VuserID和lteratio ...

  4. 在一个tomcat中配置多个tomcat服务器 111

    <Service name="Tomcat_1">     <Connector port="8888" protocol="HTT ...

  5. android api文档:intent阅读笔记

    intent是几大组件之间进行通信的组件.可以包含以下几个部分: component:指明了处理该intent的对象. Action类似于一个函数名,规定了其他部分的对应用法: The action ...

  6. [LeetCode]-011-Longest Common Prefix

    Write a function to find the longest common prefix string amongst an array of strings. []=>" ...

  7. Linux shell - 除法保留小数点

    我想实现 举例:1/3=0.33得到0.33, 尝试过bc 只能得到.33,没有0了, linux 下的shell脚本,1和3是变量$a和$b,并能指定小数点后的位数, 方法1: $> res= ...

  8. MySQL技术内幕 InnoDB存储引擎 之 InnoDB体系架构

    后台线程 1.Master Thread 2.IO Thread 3.Purge Thread 4.Page Cleaner Thread  内存 重做日志在以下三种情况下将重做日志缓存中的内容刷新到 ...

  9. Python学习之==>面向对象编程(一)

    一.面向对象与面向过程 面向对象与面向过程是两种不同的编程范式,范式指的是按照什么方式去编程.去实现一个功能.不同的编程范式本质上代表对各种不同类型的任务采取不同的解决问题的思路. 1.面向过程编程 ...

  10. delphi2010:按键 控制键 组合键的判断 响应

    procedure TForm7.FormShortCut(var Msg: TWMKey; var Handled: Boolean); var   aKey: TShortCut;   aShif ...