1、链表

链表(linked list)即使是一些包含数据的独立数据结构的(Node)集合.

链表中的每个节点通过链或指针链接在一起.

程序通过指针访问链表中的节点.

节点通常是动态分配的,但也有由节点数组构建的链表(即使这样,程序也是通过指针来遍历链表).

1.1 单链表

单链表中,每个节点包含一个指向链表下一节点的指针.链表最后一个节点的指针字段的值为NULL.提示链表后面不再有其他节点.

根指针,根指针指向链表的第一个节点,根指针只是一个指针,不包含任何数据.

//但链表节点的结构
typedef struct NODE {
struct Node *link;
int value;
} Node;

单链表的一些性质

链表不一定是顺序存储的,它的节点可能分部于内存的各个地方.

单链表可以通过链表从开始遍历链表知道结束位置,但无法从相反方向进行遍历.

1.2 在单链表中插入

我们怎么才能把一个新节点插入到有序单链表中呢?加入要把12插入到单链表.(如果链表中的值为 5, 10, 15)

思路:从链表的起始位置开始,跟随指针直到找到第一个值大于7的节点,然后把这个新值插入到那个节点之前的位置.

有一个问题,当我们找到第一个大于12的节点时,指针可能指向了15,我们无法直到已经遍历过的指针了,我们也无法返回.解决这个问题的方法就是始终保存一个指向链表当前节点之前的那个节点指针.

//插入到一个有序的单链表,函数的参数是一个指向链表第一个节点的指针以及需要插入的值.
#include <stdlib.h>
#include <stdio.h>
#include "ssl_node.h" #define FALSE 0
#define TRUE 1 int ssl_insert( Node *current, int new_value ) {
Node *previous;
Node *new;
//寻找正确的插入位置,方法是按顺序访问链表,直到到达其值大于或等于新插入的值
while( current->value < new_value ){
previour = current;
current = current->link;
}
//为新节点分配内存,并把新值存储到新节点中,如果内存分配失败,函数返回False
new = (Node *)malloc( sizeof( Node ) );
if( new == NULL )
return FALSE;
new->value = new_value;
//把新节点插入到链表中,并返回TURE
new->link = current;
previous->link = new;
return TURE;
}
//调用
result = ssl_insert( root, 12 );

以上函数是存在问题的

插入20时,while 会越过链表的尾部.并对一个NULL指针执行间接访问操作. 我们没有对current 指针判空,在while语句中直接对current取value是不对的.对空指针间接引用是非法的.

所以 while( current != NULL & current->value < value ) 时继续循环

插入3时, 为了在链表的起始位置插入一个节点,函数必须修改指针.但是,函数不能反问变量root. 修正这个问题最容易的方法是把 root 声明为全局变量,这样插入函数就能修改它.不幸的是,这是最坏的一种方法.因为这样一来函数只对这个链表起作用了

稍好的解决方法是把一个指向root的指针作为参数传递给函数.然后使用间接访问. 函数不仅可以获得 root (指向链表第一个节点的指针,也就是根指针)的值,也可以向它存储一个新的指针值. 这个参数的类型是什么呢? root 是一个指向 Node 的指针, 所以参数的类型应该是Node **, 也就是一个指向Node 的指针的指针.

#include <stdlib.h>
#include <stdio.h>
#include "ssl_node.h" #define FALSE 0
#define TRUE 1
//插入到一个有序链表,函数的参数是一个指向链表根指针的指针,以及一个需要插入的新值
int ssl_insert( Node **rootp, int new_value) {
Node *current;//当前指针
Node *previous;//前一个指针
Node *new;//新节点指针 //得到一个指向第一个节点的指针
current = *rootp;
previous = NULL;//初始化previous //寻找正确的插入位置,方法是按序访问链表,直到达到一个其值大于或等于新值的节点
while ( current != NULL && current->value < new_value) {
previous = current;
current = current->link;
} //为新节点分配内存,并把新值存储到新的节点,如果内存分配失败.函数返回FALSE
new = (Node *)malloc( sizeof( Node ));
if (new == NULL) {
return FALSE;
}
new->value = new_value; //把新节点插入到链表中,并返回 TURE
new->link = current;
if ( previous == NULL) {//用于检测新值是否应该添加到链表的起始位置
*rootp = new;
}else {
previous->link = new;
} return TRUE;
}

终极优化

把一个节点插入到链表的起始位置必须作为特殊情况进行处理吗?毕竟我们此时插入新节点需要修改的指针是根指针,对于任何其他节点,对指针进行修改时实际修改的是前一个节点的link 字段. 这两个看上去不同的操作实际上是一样的.

消除特殊情况的关键在于:链表中的每个节点都有一个指向它的指针. 对于第一个节点,这个指针是根指针,对于其他节点,这个指针是前一个节点的link字段.重点在于每个节点都有一个指针指向它.至于指针是否位于某个节点之内是无关紧要的.

取得当前节点内部的link字段的地址也是很方便的: &current->link

#include <stdlib.h>
#include <stdio.h>
#include "ssl_node.h" #define FALSE 0
#define TRUE 1
/**
把头指针和节点内指针同样对待,就不用对插入第一个节点之前的情况特殊对待了 @param linkp 指向头指针的指针
@param new_value 新值
@return 插入结构 1成功,0失败
*/
int ssl_insert_1( register Node **linkp, int new_value){
register Node *current;
register Node *new; //寻找正确的插入位置,方法是按序访问链表,直道到达一个其值大于或等于新值的节点
while ( (current = *linkp) != NULL && current->value < new_value) {
linkp = &current->link;
} //为新节点分配内存,并把新值存储到新节点中,如果内存分配失败,函数返回FALSE
new = (Node *)malloc( sizeof( Node) );
if (new == NULL) {
return FALSE;
}
new->value = new_value; //在链表中插入新节点
new->link = current;
*linkp = new;
return TRUE
}

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

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

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

  2. 结构体指针,C语言结构体指针详解

    结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过“结构体变量名.成员名”的方式引用结构体变量中的成员,除了这种方法之外还可以使用指针. 前面讲过,& ...

  3. C和C指针小记(六)-基本声明、指针声明、typedef 、常量、作用域、链接属性、存储类型、static

    1.变量的声明 声明变量的基本形式: 说明符号(一个或者多个) 声明表达式列表 说明符 (specifier) 包含一些关键字,用于描述被声明的标识符的基本类型,它也可用户改变标识符的缺省存储类型和作 ...

  4. C语言--- 高级指针2(结构体指针,数组作为函数参数)

    一.结构体指针 1. 什么是结构体指针?指向结构体变量的指针     结构体:     typedef  struct stu{                          char name[ ...

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

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

  6. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  7. ctypes 操作 python 与 c++ dll 互传结构体指针

    CMakeLists.txt # project(工程名) project(blog-3123958139-1) # add_library(链接库名称 SHARED 链接库代码) add_libra ...

  8. python 传递结构体指针到 c++ dll

    CMakeLists.txt # project(工程名) project(xxx) # add_library(链接库名称 SHARED 链接库代码) add_library(xxx SHARED ...

  9. 【C语言入门教程】7.3 结构体指针的定义和引用

    C 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...

随机推荐

  1. 【C++】C++中assert和ENDEGU预处理语句

    assert 断言语句是C++中的一种预处理宏语句,它能在程序运行时根据否定条件中断程序. C++中的assert()函数可以实现断言功能,在使用assert函数之前应该先引入<cassert& ...

  2. Python之基础练习代码

    代码1: # -*- coding: utf-8 -*- def function(*numbers): sum = 0 for num in numbers: sum = sum + num*num ...

  3. iOS开发之资料收集

    github排名:https://github.com/trending, github搜索:https://github.com/search. 此文章转自github:https://github ...

  4. 那些好用的Chrome 插件

    1. json-viewer 推荐理由:一款Chrome浏览器查看JSON数据自动格式化显示的浏览器插件 https://github.com/tulios/json-viewer

  5. Atitit 关于共享经济之共享男女朋友的创业计划

    Atitit 关于共享经济之共享男女朋友的创业计划 1. 共享经济的历史与趋势 1 1.1. 共享经济三大特征=产能过剩+共享平台+人人参与. 1 1.2. 共享经济是个大趋势,使用权渐渐的取代所有权 ...

  6. comake2

    http://blog.csdn.net/lsjseu/article/details/23395565 comake允许用户通过编写COMAKE文件,来帮助用户管理编译依赖以及编译环境的开发工具: ...

  7. C# 耗时统计

    Stopwatch sw = new Stopwatch(); sw.Restart(); var a = redis.ListRange<UserModel>(key); sw.Stop ...

  8. java.lang.UnsatisfiedLinkError: No implementation found for long org.opencv.core.Mat.n_Mat()

    Android调试openCV4Android的时候出现以下错误 java.lang.UnsatisfiedLinkError: No implementation found for long or ...

  9. [原创]Modbus协议学习笔记

    一.参考资料 1.老罗传奇的2篇博文,写的不错,通俗易懂.链接地址为:http://www.cnblogs.com/luomingui/tag/Modbus/ 2.阿莫论坛精华资料:http://ww ...

  10. Beautiful Soup模块

    Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库,它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时 ...