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. C++ 无锁数据结构

    https://www.zhihu.com/question/52629893/answer/131731126

  2. SharePoint online 获取文件版本记录

    endpoint: _api/web/GetFileByServerRelativeUrl('/allDoc/xxx.pdf')/Versions 问题: 第一次使用,无论在本地还是o365上,都只返 ...

  3. SharePonit online 列表表单定制

    1)在O365管理中心,确保启用了站点脚本定制,否则,网站不允许将页面切换到编辑模式. 2)Ribbon上,列表->表单web部件->编辑窗体 如果没有Ribbon,则到列表高级设置,启用 ...

  4. mongodb连接配置实践

    之前百度,google了很多,发现并没有介绍mongodb生产环境如何配置的文章, 当时想参考下都不行, 所以写篇文章,大家可以一块讨论下. 1. MongoClientOptions中的连接池配置: ...

  5. c#异常重试机制

    有时候我们碰到程序异常了,想让程序继续重新执行,进行重试,这时候就需要有一个合适的方法来进行操作: 自己写代码控制太麻烦了,也容易出错.这时候当然是站在巨人的肩膀上, https://github.c ...

  6. Linux内核同步

    Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...

  7. spring 中单例 bean 初始化之后和销毁之前执行指定动作 postconstruct 和 preDestroy

    1 生命周期方法, 在指定bean 创建完成后执行初始化动作或销毁之前做一些善后动作.有 3 种方法 1)实现接口 InitializingBean 然后实现 afterPropertiesSet 方 ...

  8. iScroll的使用

    CDN: <script src="//ossweb-img.qq.com/images/js/iscroll_library/iscroll-5.2.0.js">&l ...

  9. Python生成随机字符串

    利用Python生成随机域名等随机字符串. #!/usr/bin/env python# -*- coding: utf-8 -*- from random import randrange, cho ...

  10. C++ 函数模板重载

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