双向链表是比较常见的,主要是在链表的基础上添加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. Spring 加载类路径外的资源文件

    原文:http://blog.csdn.net/gaofuqi/article/details/46417259 <bean id="propertyConfigurer" ...

  2. Thinking in Java---多线程仿真:银行出纳员仿真+饭店仿真+汽车装配工厂仿真

    多线程一个非常有意思的作用就是用于仿真,这篇博客就会结合几个仿真实例来综合运用一下前面所学的多线程并发知识. 一.银行出纳员仿真 问题描写叙述:银行会有非常多来办业务的顾客,他们会排队等待服务:对于银 ...

  3. Android学习笔记-保存数据的实现方法1

    Android开发中,有时候我们需要对信息进行保存,那么今天就来介绍一下,保存文件到内存,以及SD卡的一些操作,及方法,供参考. 第一种,保存数据到内存中: //java开发中的保存数据的方式 pub ...

  4. JavaScript全局属性/函数

    JavaScript 全局属性和方法可用于创建Javascript对象. JavaScript 全局属性 属性 描述 Infinity 代表正的无穷大的数值. NaN 指示某个值是不是数字值. und ...

  5. SpringMVC 运行过程

    加入jar包 在web.xml中配置DispatherServlet 加入SpringMVC配置文件 编写处理请求的处理器,并表示为处理器 编写视图 可见的运行流程: 实际的运行流程:

  6. Java中的经典算法之冒泡排序

    原理:比较两个相邻的元素,将值大的元素交换至右端. 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后.然后比较第2个数和第3个数,将 ...

  7. spring-boot-configuration-processor的作用

    spring默认使用yml中的配置,但有时候要用传统的xml或properties配置,就需要使用spring-boot-configuration-processor了 先引入pom依赖 <d ...

  8. 3-5 编程练习:jQuery实现简单的图片对应展示效果

    3-5 编程练习:jQuery实现简单的图片对应展示效果 通过这个章节的学习, 老师带领大家完成了一个基本的图片切换特效,接下来,我们也实现一个类似的效果,点击相应的按钮,切换对应的图片. 效果图 : ...

  9. Quartz定时调度jar包的执行Demo分享

    1.Quartz简介 ​ Quartz框架的核心是调度器.调度器负责管理Quartz应用运行时环境.调度器不是靠自己做所有的工作,而是依赖框架内一些非常重要的部件.Quartz不仅仅是线程和线程管理. ...

  10. 键盘按钮keyCode大全:获取按键对应的键值的方法

    没有大全,只有方法,授人与鱼不如授人于渔: 下面这行代码,大家可以打在控制台里,直接进行测试: document.body.onkeyup = function (e) { e = e || wind ...