双向链表是比较常见的,主要是在链表的基础上添加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. vue 重要的东西

  2. reactjs 视频教程

    近期玩了一下react,感觉挺不错的,搜了一下没有看到什么视频教程,于是自己便录制了几个入门视频.希望能够帮到大家.已经上传土豆了,能够点击以下的链接查看. http://www.tudou.com/ ...

  3. 设计模式之外观模式(Facade)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  4. PC_excel完毕一列英文小写变大写

    原创作品,出自 "深蓝的blog" 博客.欢迎转载,转载时请务必注明出处.否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong ...

  5. react 项目实战(十)引入AntDesign组件库

    本篇带你使用 AntDesign 组件库为我们的系统换上产品级的UI! 安装组件库 在项目目录下执行:npm i antd@3.3.0 -S 或 yarn add antd 安装组件包 执行:npm ...

  6. 【OI】对拍

    对拍的方法是先用生成器生成几组随机数据,然后用暴力算法和当前算法对比结果来确保正确性. 数据生成器: 引入cstdlib与ctime两个库,然后通过srand初始化随机数: srand(time(0) ...

  7. Delphi中WebBrowser控件打开部分网站报"Invalid floating point operation”解决

    Delphi中WebBrowser控件打开部分网站报"Invalid floating point operation”解决 EmbeddedWBWebBrowserDelphi  最近用E ...

  8. Android合并两个APP的详细做法(掌握)

    有时候因公司需求,要求合并两个APP 使用里面的功能. 平台:Studio 小白鼠:二维码扫描 和自己项目 详细步骤: /**  * 1.将解压后的android/assets目录复制到项目中的mai ...

  9. windows console Kill PID 端口查看

    开始--运行--cmd 进入命令提示符 输入netstat -ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选&qu ...

  10. MATLAB——matlab特殊符号表【转载】

    链接来源: matlab特殊符号表 http://blog.sina.com.cn/s/blog_4a09187801014xg9.html Character Sequence Symbol Cha ...