1、双链表

1.1 双向链表的声明

在一个双链表中,每个节点都包含两个指针--指向前一个节点的指针和指向后一个节点的指针.

声明

typedef struct NODE {
struct NODE *fwd;
struct NODE *bwd;
int value;
} Node;

根节点的fwd字段指向链表的第1个节点,根节点的bwd字段指向链表的最后一个节点.如果链表为空,这两个字段都为NULL.链表的第1个节点的bwd 字段和最后一个节点的 fwd 字段都为 NULL. 在一个有序的链表中,各个节点将根据value 字段的值以升序排列.

3.2 双向链表的插入

将一个新值插入到有序表中

函数原型

dll_insert( Node *rootp, int value );

dll_insert 接受两个参数,一个指向根节点的指针和一个整型值.

插入节点时,可能会遇到4中情况:

1、新值可能必须插入到链表的中间位置

2、新值可能必须插入到链表的起始位置

3、新值可能必须插入到链表的结束位置

4、新值可能必须既插入到链表的起始位置,又插入到链表的结束位置


/**
向双向链表中插入数据 @param rootp 根节点
@param value 带插入值
@return 0 已经有值, -1 不能插入, 1 插入成功
*/
int dll_insert( Dnode *rootp, int value) {
Dnode *this;
Dnode *next;
Dnode *newnode; //查看 value 是否已经存在于链表中, 如果是就返回.否则,为新值创建一个新节点(“newnode”将指向它)
//this 将指向应该在新节点之前的那个节点
//next 将指向应该在新节点之后的那个节点
for( this = rootp; (next = this->fwd) != NULL; this = next){
if (next->value == value) {
return 0;
}
if (next->value > value) {
break;
}
} newnode = (Dnode *)malloc( sizeof( Dnode) );
if ( newnode == NULL) {
return -1;
}
newnode->value = value; //把新值添加到链表中
if ( next != NULL) {
//情况 1,2 并非位于链表尾部.
if ( this != rootp ) {//情况1 并非位于链表起始位置
newnode->fwd = next;
this->fwd = newnode;
newnode->bwd = this;
next->bwd = newnode;
}else {// 情况2 位于起始位置
newnode->fwd = next;
this->fwd = newnode;
newnode->bwd = NULL;
next->bwd = newnode;
}
}else {
//情况3,4 位于链表的尾部
if (this != rootp) {// 情况3 并非位于链表的起始位置
newnode->fwd = NULL;
this->fwd = newnode;
newnode->bwd = this;
rootp->bwd = newnode;
}else {// 情况4 位于链表起始位置
newnode->fwd = NULL;
this->fwd = newnode;
newnode->bwd = NULL;
rootp->bwd = newnode;
}
} return 1;
}
//添加节点的4种情况可以提炼
if ( next != NULL) {
//情况1 或 2 并非位于链表的尾部
newnode->fwd = next;
if (this != rootp ) {//情况1 并非位于链表的起始位置
this->fwd = newnode;
newnode->bwd = this;
}else {//情况2 位于链表的起始位置
rootp->fwd = newnode;
newnode->bwd = NULL;
}
next->bwd = newnode;
}else {
//情况3或4 位于链表尾部
newnode->fwd = NULL;
if (this != rootp) {//情况3 并不位于链表起始位置
this->fwd = newnode;
newnode->bwd = this;
}else {//情况4 位于链表起始位置
rootp->fwd = newnode;
newnode->bwd = NULL;
}
rootp->bwd = newnode;
}

C和C指针小记(十八)-使用结构和指针-双向链表的更多相关文章

  1. C和C指针小记(十五)-结构和联合

    1.结构 1.1 结构声明 在声明结构时,必须列出它包含的所有成员.这个列表包括每个成员的类型和名称. struct tag {member-list} variable-list; 例如 //A s ...

  2. 结构体与typedef的使用,还有结构体指针的使用(二层结构体指针)

    该类容摘抄自以下链接,为学习之后的记录,不是鄙人原创. 学习链接:https://blog.csdn.net/a2013126370/article/details/78230890 typedef ...

  3. Leetcode 2. Add Two Numbers(指针和new的使用)结构体指针

    ---恢复内容开始--- You are given two non-empty linked lists representing two non-negative integers. The di ...

  4. C和C指针小记(十六)-动态内存分配

    动态内存分配 1.1 为什么使用动态内存分配 直接声明数组的方式的缺点: 1) 声明数组必须指定长度限制.无法处理超过声明长度的数组. 2) 如果声明更大的常量来弥补第一个缺点,会造成更多的内存浪费. ...

  5. C和C指针小记(十四)-字符串、字符和字节

    1.字符串 C语言没有字符串数据类型,因为字符串以字符串常量的形式出现或存储于字符数组中. 字符串常量和适用于那些程序不会对他们进行修改的字符串. 所有其他字符串都必须存储于字符串数组或动态分配的内存 ...

  6. C和C指针小记(十)-函数

    1.函数的定义 函数的定义就是函数体的实现. 语法: 类型 函数名(形式参数) 代码块 函数返回类型和函数名分开写是代码风格的问题,现代语言如swift返回值在函数名和参数表的后面,这样使得某些工程工 ...

  7. C和C指针小记(十二)-函数的可变参数表

    1.可变参数表是通过宏实现的 宏定义于stdarg.h头文件,它是标准库的一部分.这个头文件声明了一个类型var_list和三个宏--va_start.va_arg.va_end. 我们可以声明一个类 ...

  8. C和指针 第十二章 结构体 习题

    12.3 重新编写12.7,使用头和尾指针分别以一个单独的指针传递给函数,而不是作为一个节点的一部分 #include <stdio.h> #include <stdlib.h> ...

  9. C和指针 第十二章 结构体 整体赋值 error: expected expression

    定义结构体后整体赋值时发生错误 typedef struct NODE { struct NODE *fwd; struct NODE *bwd; int value; } Node; //声明变量 ...

随机推荐

  1. py下windows用户安装lxml

    windows用户在安装lxml可能会因为缺少C语言库报错可以选择到Unofficial Windows Binaries for Python Extension Packages下载whl文件 例 ...

  2. 网页出现400 Bad Request Request Header Or Cookie Too Large错误的解决方法

    在开发项目过程中,突然遇到400 Bad Request Request Header Or Cookie Too Large的报错,我也是第一次出现这样的错误,感觉还是挺新奇的. 分析下出现错误的原 ...

  3. curl 模拟表单post文件

    网上查询出来的几乎都是错误的,正确的应该是: $data = array( 'pic'=>new CURLFile($path) // 如果无效可以这样 // 'pic'=>curl_fi ...

  4. 【原创 Hadoop&Spark 动手实践 5】Spark 基础入门,集群搭建以及Spark Shell

    Spark 基础入门,集群搭建以及Spark Shell 主要借助Spark基础的PPT,再加上实际的动手操作来加强概念的理解和实践. Spark 安装部署 理论已经了解的差不多了,接下来是实际动手实 ...

  5. prometheus杂碎

    一个监控及告警的系统,内含一个TSDB(时序数据库).在我而言是一个数采程序 重要成员分三块 exploter:实际是外部接口,让各个程序实现这个接口,供普罗米修斯定时从此接口中取数 alert:告警 ...

  6. C++ 函数模板重载

    函数模板可以像普通函数那样重载. C++ 编译器会从不同的候选中匹配一个并进行调用. 即使不涉及到模板,这种匹配的规则也很复杂,现在还有加上模板一起匹配. 先来个小例子: #include <i ...

  7. Ubuntu 16.04设置开机启动脚本的方法

    需求:公司卡片机容量太小,只有100G,由于使用的人比较的多,开机使用后有时候就会出现磁盘空间占满数据写不进去的情况,影响工作进度,而且每次使用完都得关掉卡片机,所以就有必要写个清理磁盘的脚本,当卡片 ...

  8. ubuntu下core file文件生成及调试

    1.简介:corefile 是Linux下程序崩溃时生成的文件,可以用来分析程序崩溃的原因,因为它内部包含了程序崩溃时的堆栈信息. 2.corefile的设置 默认情况下,程序崩溃是不会生成coref ...

  9. Scala学习笔记(七):Rational、隐式转换、偏函数、闭包、重复参数及柯里化

    class Rational(n: Int, d: Int) { require(d != 0) private val g: Int = gcd(n, d) val number: Int = n ...

  10. 自定义GridView实现分割线解析

    前两天在些项目的时候碰到常用的GridView要实现一些分割线,之前就是用本方法利用listView和Item的背景颜色的不同线显示分割线.这是最low的一种做法.于是我就简单的写了一个自定义的 Gr ...