双链表中每个节点包含指向当前和之后节点的指针,插入节点到双链表中需要考虑四种情况:

1、插入到链表头部

2、插入到链表尾部

3、插入到空链表中

4、插入到链表内部

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0 //指针fwd指向前一个节点,bwd指向后一个节点
typedef struct NODE {
struct NODE *fwd;
struct NODE *bwd;
int value;
} Node; //插入value到双链表中,rootPtr为指向节点的指针
int doubleLinklistInsert(Node *rootPtr, int value)
{
Node *this;
Node *next;
Node *newNode; for(this = rootPtr; (next = this -> fwd) != NULL; this = next){
//如果节点值已经存在
if(next -> value == value){
return FALSE;
}
//找到插入位置
if(next -> value > value){
break;
}
} //分配内存
newNode = (Node *)malloc(sizeof(Node));
if(newNode == NULL){
return FALSE;
} newNode -> value = value; /*插入节点一共四种情况
1.插入到头部
2.插入到尾部
3.插入到中间
4.插入到空链表中
*/
//如果插入到头部部或者内部
if(next != NULL){
//插入到内部
if(this != rootPtr){
newNode -> fwd = next;
newNode -> bwd = this;
this -> fwd = newNode;
next -> bwd = newNode;
}else{
//插入到头部,newNode的bwd为NULL
newNode -> fwd = next;
newNode -> bwd = NULL;
rootPtr -> fwd = newNode;
next -> bwd = newNode;
}
}else{
//插入到尾部或者空表中
if(this != rootPtr){
//插入到尾部,修改根指针的bwd和fwd
newNode -> fwd = NULL;
newNode -> bwd = this;
this -> fwd = newNode;
rootPtr -> bwd = newNode;
}else{
//插入到空链表中
newNode -> fwd = NULL;
newNode -> bwd = NULL;
this -> fwd = newNode;
this -> bwd = newNode;
}
} return TRUE;
} int main()
{
Node third;
Node second;
Node first; third = (Node){NULL, &second, 4};
second = (Node){&third, &first, 2};
first = (Node){&second, NULL, 1}; Node root = {&first, &third, -1};
Node *rootPtr = &root; doubleLinklistInsert(rootPtr, 35);
doubleLinklistInsert(rootPtr, -10);
doubleLinklistInsert(rootPtr, 3); rootPtr = &root;
while((rootPtr = rootPtr -> fwd) != NULL){
printf("%d\t", rootPtr -> value);
}
return 0;
}

  运行:

优化:

1.语句提炼

对于下面的代码可以从if语句中,提取出共同的部分。

if(x = 3){
i = 1;
something;
}else{
i = 1;
something different;
}

将共同的i=1,提取出if语句

 i = 1;
if(x = 3){
something;
}else{
something different;
}

代码提炼:提取共同项

    if(next != NULL){
newNode -> fwd = next;
next -> bwd = newNode;
//插入到内部
if(this != rootPtr){
newNode -> bwd = this;
this -> fwd = newNode;
}else{
//插入到头部,newNode的bwd为NULL
newNode -> bwd = NULL;
rootPtr -> fwd = newNode;
}
}else{
//插入到尾部或者空表中
newNode -> fwd = NULL;
this -> fwd = newNode;
if(this != rootPtr){
//插入到尾部,修改根指针的bwd和fwd
newNode -> bwd = this;
rootPtr -> bwd = newNode;
}else{
//插入到空链表中
newNode -> bwd = NULL;
this -> bwd = newNode;
}
}

对于下面的情况

if(field!= NULL){
pointer = field;
}else{
pointer=NULL;
}
//第二种情况field等于NULL,所以上面条件语句可以写成
pointer =field;

要找出看起来虽然不一样,但逻辑上是相同的代码,进行简化,这里第二个else中this此时是等于rootPtr的,所以相同, this转换成rootPtr,然后提取出。提取之后两个条件语句中都有:

        if(this != rootPtr){
//插入到尾部,修改根指针的bwd和fwd
newNode -> bwd = this;
this -> fwd = newNode;
}else{
//插入到空链表中
newNode -> bwd = NULL;
rootPtr -> fwd = newNode;
}

提取出来:

    if(this != rootPtr){
//插入到尾部,修改根指针的bwd和fwd
newNode -> bwd = this;
this -> fwd = newNode;
}else{
//插入到空链表中
newNode -> bwd = NULL;
rootPtr -> fwd = newNode;
} if(next != NULL){
newNode -> fwd = next;
next -> bwd = newNode;
//插入到内部
}else{
//插入到尾部或者空表中
newNode -> fwd = NULL;
rootPtr -> bwd = newNode;
}

  下面的第二个else中的next是NULL,所以NULL可以转换一下,继续提取出来:

    newNode -> fwd = next;
if(next != NULL){
next -> bwd = newNode;
}else{
rootPtr -> bwd = newNode;
} if(this != rootPtr){
newNode -> bwd = this;
this -> fwd = newNode;
}else{
newNode -> bwd = NULL;
rootPtr -> fwd = newNode;
}

提取出来,最终简化版:

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0 //指针fwd指向前一个节点,bwd指向后一个节点
typedef struct NODE {
struct NODE *fwd;
struct NODE *bwd;
int value;
} Node; //插入value到双链表中,rootPtr为指向节点的指针
int doubleLinklistInsert(Node *rootPtr, int value)
{
Node *this;
Node *next;
Node *newNode; for(this = rootPtr; (next = this -> fwd) != NULL; this = next){
//如果节点值已经存在
if(next -> value == value){
return FALSE;
}
//找到插入位置
if(next -> value > value){
break;
}
} //分配内存
newNode = (Node *)malloc(sizeof(Node));
if(newNode == NULL){
return FALSE;
} newNode -> value = value; /*插入节点一共四种情况
1.插入到头部
2.插入到尾部
3.插入到中间
4.插入到空链表中
*/
//如果插入到头部部或者内部 newNode -> fwd = next;
if(next != NULL){
next -> bwd = newNode;
}else{
rootPtr -> bwd = newNode;
} if(this != rootPtr){
newNode -> bwd = this;
this -> fwd = newNode;
}else{
newNode -> bwd = NULL;
rootPtr -> fwd = newNode;
} return TRUE;
} int main()
{
Node third;
Node second;
Node first; third = (Node){NULL, &second, 4};
second = (Node){&third, &first, 2};
first = (Node){&second, NULL, 1}; Node root = {&first, &third, -1};
Node *rootPtr = &root; doubleLinklistInsert(rootPtr, 35);
doubleLinklistInsert(rootPtr, -10);
doubleLinklistInsert(rootPtr, 3); rootPtr = &root;
while((rootPtr = rootPtr -> fwd) != NULL){
printf("%d\t", rootPtr -> value);
}
return 0;
}

  运行:

通过提炼语句,消除if语句中的重复语句。

C和指针 第十二章 使用结构和指针 双链表和语句提炼的更多相关文章

  1. C和指针 第十二章 使用结构和指针

    链表是一种常用的数据结构,每个节点通过链或者指针链接在一起,程序通过间接指针访问链表中的节点. typedef struct Node { //指向下一个节点的指针 struct Node *next ...

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

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

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

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

  4. 《Linux命令行与shell脚本编程大全》第十二章 使用结构化命令

    许多程序要就对shell脚本中的命令施加一些逻辑控制流程. 结构化命令允许你改变程序执行的顺序.不一定是依次进行的 12.1 使用if-then语句 如下格式: if command then     ...

  5. C和指针 (pointers on C)——第十二章:利用结构和指针

    第十二章 利用结构和指针 这章就是链表.先单链表,后双向链表. 总结: 单链表是一种使用指针来存储值的数据结构.链表中的每一个节点包括一个字段,用于指向链表的下一个节点. 有一个独立的根指针指向链表的 ...

  6. perl5 第十二章 Perl5中的引用/指针

    第十二章 Perl5中的引用/指针 by flamephoenix 一.引用简介二.使用引用三.使用反斜线(\)操作符四.引用和数组五.多维数组六.子程序的引用  子程序模板七.数组与子程序八.文件句 ...

  7. [CSAPP笔记][第十二章并发编程]

    第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...

  8. C primer plus 第五版十二章习题

    看完C prime plus(第五版)第十二章,随带完成了后面的习题. 1.不使用全局变量,重写程序清单12.4的程序. 先贴出12.4的程序,方便对照: /* global.c --- 使用外部变量 ...

  9. 《OpenCL异构并行编程实战》补充笔记散点,第五至十二章

    ▶ 第五章,OpenCL 的并发与执行模型 ● 内存对象与上下文相关而不是与设备相关.设备在不同设备之间的移动如下,如果 kernel 在第二个设备上运行,那么在第一个设备上产生的任何数据结果在第二个 ...

随机推荐

  1. 科学计算器(JAVA实现)

    前记: 大二学 Java 的时候写的,现在贴上来,只为留念. 再翻代码,自己看着都头疼.一重重的 if 嵌套,当时写得费劲,现在看着更费劲. 代码思想: 代码的大致思想是这样: 首先定义一个算式字符串 ...

  2. 嵌入式Linux驱动学习之路(二十三)NAND FLASH驱动程序

    NAND FLASH是一个存储芯片. 在芯片上的DATA0-DATA7上既能传输数据也能传输地址. 当ALE为高电平时传输的是地址. 当CLE为高电平时传输的是命令. 当ALE和CLE都为低电平时传输 ...

  3. 嵌入式Linux驱动学习之路(十八)LCD驱动

    驱动代码: /************************************************************************* > File Name: lcd ...

  4. android第一行代码-3.activity之间的调用跟数据传递

    前面两节所有应用都是同一个activity中的,是时候讲activity之间交互的操作了,此后会涉及到intent这个概念,这也算一个新的里程碑开始. 主要内容包括intent的使用,以及activi ...

  5. Hotaru's problem

    Hotaru's problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  6. jquery中attr()与prop()区别

    我们知道jquery中获取元素属性有两种常见的方法,一个是attr()方法,这个是用的比较多的,也是我们第一个想到的.另外一个就是prop()方法了,这个方法之前很少用到,它是jquery1.6之后新 ...

  7. js阿拉伯数字转中文大写

    function DX(n) { if (!/^(0|[1-9]\d*)(\.\d+)?$/.test(n)) return "数据非法"; var unit = "千百 ...

  8. C#软件设计——小话设计模式原则之:单一职责原则SRP

    前言:上篇C#软件设计——小话设计模式原则之:依赖倒置原则DIP简单介绍了下依赖倒置的由来以及使用,中间插了两篇WebApi的文章,这篇还是回归正题,继续来写写设计模式另一个重要的原则:单一职责原则. ...

  9. Chrome开发者工具详解

    http://www.cnblogs.com/LibraThinker/p/5948448.html http://www.cnblogs.com/LibraThinker/p/5981346.htm ...

  10. Poisson泊松分布

    PMF 若随机变量\(K\)的概率质量函数PMF为 \[ P(K = k) = e^ {-\lambda} \frac {\lambda^k}{k!} \] 则称:\(K \sim Poisson(\ ...