双向链表是比较常见的,主要是在链表的基础上添加prev指针,闲话少说直接上代码吧(这个也是网上一个大神的思路,真心不错,条理清楚,逻辑缜密)

主要也是为了学习,贴上我所调试成功的代码(Linux环境下)

双向链表代码:

 #include <stdio.h>
#include <stdlib.h> typedef struct NodeTag Node;
typedef struct LinkedListTag LinkedList; struct NodeTag
{
Node* prev;
Node* next;
void* data;
}; struct LinkedListTag
{
Node* head;
Node* tail;
Node* cur;
int size;
}; // 创建一个链表 成功返回该链表的指针 否则返回NULL
LinkedList* Create( void )
{
// 创建一个新节点
LinkedList* list = (LinkedList*)malloc(sizeof(LinkedList));
// 创建不成功,返回NULL
if(!list) return NULL; list->head = NULL;
list->tail = NULL;
list->cur = NULL;
list->size = ; // 初始化成功后,返回list
return list;
} // 将元素添加到链表的末尾,成功返回链表的size,失败返回-1
int AddBack(LinkedList* list, void* data)
{
// 创建一个新节点,创建不成功的话,返回-1
Node* node = (Node *)malloc(sizeof(Node));
if(!node) return -; // 为节点的数据域赋值
node->data = data;
// 如果为链表的末尾
if(list->tail)
{
list->tail->next = node; // 把新节点赋给链表末尾的下一个
node->prev = list->tail; // 新节点的前一个等于之前的末节点
node->next = NULL; // 新节点为末节点,把它下一个指向NULL
}
else // 如果不为末尾,其实就是空链表
{
node->next = NULL; // 新节点的下一个为NULL
node->prev = NULL; // 新节点的前一个为NULL
list->head = node; // 链表的头为新节点node
}
list->tail = node; // 链表的末尾指向node return ++list->size; // 返回链表的size
} // 将元素添加到链表前端,成功返回非0,否则返回0
int AddFront(LinkedList* list, void* data)
{
Node *node = (Node*)malloc(sizeof(Node));
if(!node) return ; node->data = data;
if(list->head)
{
list->head->prev = node;
node->next = list->head;
node->prev = NULL;
}
else
{
node->next = NULL;
node->prev = NULL;
list->tail = node;
}
list->head = node; return ++list->size;
} // 将元素从末端移除并返回该元素,如果链表为空则返回NULL
void* RemoveBack(LinkedList* list)
{
Node* temp;
void* data; if(!list->size) return NULL; temp = list->tail;
data = list->tail->data; if(list->head == list->tail)
{
list->head = NULL;
list->tail = NULL;
list->cur = NULL;
}
else
{
list->tail = list->tail->prev;
list->tail->next = NULL;
}
--list->size;
free(temp);
return data;
} // 将元素从前端移除并返回该元素,如果链表为空则返回NULL
void* RemoveFront(LinkedList* list)
{
Node* temp;
void* data; if(!list->size) return NULL; temp = list->head;
data = list->head->data; if(list->head == list->tail)
{
list->head = NULL;
list->tail = NULL;
list->cur = NULL;
}
else
{
list->head = list->head->next;
list->head->prev = NULL;
}
--list->size;
free(temp);
return data;
} /* 如果当前链表为空则返回非0,否则返回0 */
int IsEmpty(LinkedList* list)
{
return list->size == ;
} /* 获得链表的大小(元素总个数) */
int Size(LinkedList* list)
{
return list->size;
} /* 将当前位置移动到链表的开始 */
void Begin(LinkedList* list)
{
list->cur = list->head;
} /* 将当前位置移动到链表的最后 */
void End(LinkedList* list)
{
list->cur = list->tail;
}
/* 将当前位置向后移动一个位置 */
void MoveNext(LinkedList* list)
{
list->cur = list->cur->next;
} /* 将当前位置向后移动一个位置 */
void MovePrev(LinkedList* list)
{
list->cur = list->cur->prev;
} /* 清空链表中所有元素 */
void Clear(LinkedList* list)
{
while(RemoveBack(list));
} /* 销毁一个链表 */
void Destroy(LinkedList* list)
{
Clear(list);
free(list);
} /* 如果当前位置之后还有元素则返回非0,否则返回0 */
int HasNext(LinkedList* list)
{
if (!list->cur) return ;
if (list->cur == list->tail) return ;
return list->cur->next != NULL;
} /* 如果当前位置之前还有元素则返回非0,否则返回0 */
int HasPrev(LinkedList* list)
{
if (!list->cur) return ;
if (list->cur == list->head) return ;
return list->cur->prev != NULL;
} /* 返回当前位置的元素 */
void* Current(LinkedList* list)
{
return list->cur->data;
} // 正向打印链表
void Traverse(LinkedList* list)
{
for( Begin(list); HasNext(list); MoveNext(list) )
printf("%d ", *(int*)Current(list));
putchar('\n');
} // 反向打印链表
void RTraverse(LinkedList* list)
{
for (End(list); HasPrev(list); MovePrev(list))
printf("%d ", *(int*)Current(list));
putchar('\n');
} int main()
{
int i;
LinkedList* list = Create(); int array1[];
int array2[]; for(i=; i<; i++)
{
array1[i] = i+;
array2[i] = i++;
AddBack(list, &array1[i]);
} printf("链表大小(SIZE): %d\n", Size(list)); printf("正向打印链表:\n");
Traverse(list);
printf("反向打印链表:\n");
RTraverse(list); printf("添加array2[0]数\n");
AddBack(list, &array2[]); printf("链表大小(SIZE): %d\n", Size(list));
printf("正向打印链表:\n");
Traverse(list);
printf("反向打印链表:\n");
RTraverse(list); printf("调用AddFront函数,添加array2[0]数\n");
AddFront(list, &array2[]); printf("链表大小(SIZE): %d\n", Size(list));
printf("正向打印链表:\n");
Traverse(list);
printf("反向打印链表:\n");
RTraverse(list); printf("从末尾移除的元素是: %d\n", *(int *)RemoveBack(list));
printf("链表大小(SIZE): %d\n", Size(list));
printf("正向打印链表:\n");
Traverse(list);
printf("反向打印链表:\n");
RTraverse(list); printf("从开头移除的元素是: %d\n", *(int *)RemoveFront(list));
printf("链表大小(SIZE): %d\n", Size(list));
printf("正向打印链表:\n");
Traverse(list);
printf("反向打印链表:\n");
RTraverse(list); printf("清空链表,Clear(list)后\n");
Clear(list);
printf("链表大小(SIZE): %d\n", Size(list)); for(i=; i<; i++)
{
AddFront(list, &array2[i]);
}
printf("正向打印链表:\n");
Traverse(list);
printf("反向打印链表:\n");
RTraverse(list); Destroy(list);
printf("销毁链表Destroy(list)\n"); return ;
}

以下是执行结果:

链表大小(SIZE):
正向打印链表: 反向打印链表: 添加array2[]数
链表大小(SIZE):
正向打印链表: 反向打印链表: 调用AddFront函数,添加array2[]数
链表大小(SIZE):
正向打印链表: 反向打印链表: 从末尾移除的元素是:
链表大小(SIZE):
正向打印链表: 反向打印链表: 从开头移除的元素是:
链表大小(SIZE):
正向打印链表: 反向打印链表: 清空链表,Clear(list)后
链表大小(SIZE):
正向打印链表: 反向打印链表: 销毁链表Destroy(list)

在世界上,努力坚持的绝对不是自己一个人,好好努力会成功的。

[算法天天练] - C语言实现双向链表(一)的更多相关文章

  1. [算法天天练] - C语言实现约瑟夫环(2)

    Linux下 #include <stdlib.h>#include <stdio.h> int main(){ int n,m,i,s = 0; printf("E ...

  2. C语言实现双向链表

    目前我们所学到的链表,无论是动态链表还是静态链表,表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为单向链表(或单链表). 虽然使用单链表能 100% 解决逻辑关系为 & ...

  3. 用C语言把双向链表中的两个结点交换位置,考虑各种边界问题。

    用C语言把双向链表中的两个结点交换位置,考虑各种边界问题. [参考] http://blog.csdn.net/silangquan/article/details/18051675

  4. 关于中值滤波算法,以及C语言实现(转)

    源:关于中值滤波算法,以及C语言实现 1.什么是中值滤波? 中值滤波是对一个滑动窗口内的诸像素灰度值排序,用其中值代替窗口中心象素的原来灰度值,它是一种非线性的图像平滑法,它对脉冲干扰级椒盐噪声的抑制 ...

  5. 基于BP神经网络的简单字符识别算法自小结(C语言版)

    本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前面的闲话: 自我感觉自己应该不是一个非常 ...

  6. FFT算法理解与c语言的实现

    完整内容迁移至 http://www.face2ai.com/DIP-2-3-FFT算法理解与c语言的实现/ http://www.tony4ai.com/DIP-2-3-FFT算法理解与c语言的实现 ...

  7. 算法小练#1 - Dany Yang

    开始记录每周做过的算法题,这是第一周,新的开始 1021. 删除最外层的括号 题目要求如下: 有效括号字符串为空 ("")."(" + A + ")& ...

  8. C++语言实现双向链表

    这篇文章是关于利用C++模板的方式实现的双向链表以及双向链表的基本操作,在之前的博文C语言实现双向链表中,已经给大家分析了双向链表的结构,并以图示的方式给大家解释了双向链表的基本操作.本篇文章利用C+ ...

  9. 【操作系统】银行家算法实现(C语言)

    [操作系统]银行家算法实现(C语言) 注意:本人编码水平很菜.算是自己的一个总结.可能会有我还没有发现的bug.如果有人发现后可以指出,不胜感激. 1.银行家算法: 我们可以把操作系统看作是银行家,操 ...

随机推荐

  1. springboot整体介绍

    1.springboot:快速开发,强大的运维能力.(监控,服务发现,并打) 2.微服务,将一个大系统分解成很多独立的小服务,这些服务能随时发布. 3.2004年第一版spring 1.0,rod j ...

  2. Utuntu下Xshell使用+vi使用

    记得上次Utuntu下Xshell环境没有搭建成功,这个又试试! 按照以前的方法测试Utuntu的ip;在本机安装Xshell 然后出现xshell连接虚拟机时,提示SSH服务器拒绝了密码 修改方法: ...

  3. Solid Edge性质管理者 如何获取装配体的BOM表 物料清单

    工具-性质管理者 在里面可以输入每个文件的文件号,版本号,作者等信息   右击点击显示性质,你还可以添加或删除文件属性   把需要的属性更改好了之后,可以输出装配体的所有零件信息到Excel中,点击工 ...

  4. node安装-Win+Linux+Mac osx

    node下载地址,除了Mac osx或Win平台,仅有Linux平台命令安装. Win.Mac 点击即可下载(注:Mac有dmg和pkg安装格式). Linux分为Redhot和Deepin系列,安装 ...

  5. JS 获取form表单的所有数据

    在HTML中用js获取通过GET.POST方法(就是在网址后加?a=b&c=d之类)传过来的表单值. 针对大家常用的获取表单方式,很多时候都是在重复的写一些代码,今天给大家贴出来的代码可以作为 ...

  6. JavaScript 获得代码行号和脚本文件名

    如果你使用的是 V8 引擎,Chrome 和 Node.js 所用的,那么你可以利用 JavaScriptStackTraceApi 来获得行号信息,有两个 API: Error.captureSta ...

  7. Linux地址ping不通情况怎么办?

    查看原文:http://www.ibloger.net/article/325.html Linux地址ping不通情况怎么办? 问题:今天写了一个微信支付的项目.有一个class中使用了httpPo ...

  8. Android框架之高速开发框架xUtil

    做Android开发我们通常是从原生态的開始,就是调用默认那些Android代码来开发我们的应用,可是到了一定程度,我们就想着怎么来高速开发我们的应用.这个时候我们就要着手来研究框架了. 以下介绍一个 ...

  9. 第二次PHP面试题

    昨天下午翘班去参加了人生中第二次PHP面试.是一家相对第一家更加专业的互联网公司.效果不如第一家理想,笔试题有点难,而且偏高理论,面试时面试官也还不错,一起探讨,可是他的问题我还是很多都不知道,果然是 ...

  10. .NET下为百度文本编辑器UEditor增加图片删除功能

    [摘要:比来写了个项目,用到了UEditor,但是UE并出有文件删除功效 然后网上找若何增加 找半天只能找到一个1.2.X的 以是便摹仿PHP的 改成了.NET的 PHP本文 第一步 (增加背景删除地 ...