C和C指针小记(十七)-使用结构和指针-链表
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字段的地址也是很方便的: ¤t->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 = ¤t->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指针小记(十七)-使用结构和指针-链表的更多相关文章
- C和C指针小记(十五)-结构和联合
1.结构 1.1 结构声明 在声明结构时,必须列出它包含的所有成员.这个列表包括每个成员的类型和名称. struct tag {member-list} variable-list; 例如 //A s ...
- 结构体指针,C语言结构体指针详解
结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过“结构体变量名.成员名”的方式引用结构体变量中的成员,除了这种方法之外还可以使用指针. 前面讲过,& ...
- C和C指针小记(六)-基本声明、指针声明、typedef 、常量、作用域、链接属性、存储类型、static
1.变量的声明 声明变量的基本形式: 说明符号(一个或者多个) 声明表达式列表 说明符 (specifier) 包含一些关键字,用于描述被声明的标识符的基本类型,它也可用户改变标识符的缺省存储类型和作 ...
- C语言--- 高级指针2(结构体指针,数组作为函数参数)
一.结构体指针 1. 什么是结构体指针?指向结构体变量的指针 结构体: typedef struct stu{ char name[ ...
- Leetcode 2. Add Two Numbers(指针和new的使用)结构体指针
---恢复内容开始--- You are given two non-empty linked lists representing two non-negative integers. The di ...
- C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用
类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...
- ctypes 操作 python 与 c++ dll 互传结构体指针
CMakeLists.txt # project(工程名) project(blog-3123958139-1) # add_library(链接库名称 SHARED 链接库代码) add_libra ...
- python 传递结构体指针到 c++ dll
CMakeLists.txt # project(工程名) project(xxx) # add_library(链接库名称 SHARED 链接库代码) add_library(xxx SHARED ...
- 【C语言入门教程】7.3 结构体指针的定义和引用
C 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...
随机推荐
- Spring MVC框架处理Web请求的基本流程
- Huginn及环境搭建
博客搬迁至https://blog.wangjiegulu.com RSS订阅:https://blog.wangjiegulu.com/feed.xml Huginn 及环境搭建 什么是 Hugin ...
- Object C函数指针@selector
其作用相当于函数指针,现在我看到的大多说用法都是在调用某些函数需要传递一个 函数指针 参数时,使用@selector.它会在当前类里面查找selector后面所跟的函数,返回一个SEL类型的值. S ...
- AICODER全栈实习报名
三期班开始报名 三期班定于6月17日开班,实习费用如下: 三个月模式实习费为12000元. 一个月模式实习费为4500元. AICODER提供后付费模式,报名参加AICODER的线下实习3个月模式的朋 ...
- Linux shell去除字符串中所有空格
Linux shell去除字符串中所有空格 echo $VAR | sed 's/ //g'
- Navicat 提示 Access violation at address ***(如004ECCF4) in module ‘navicat.exe’. Read of address ***(如00000048)
Navicat 提示 Access violation at address ***(如004ECCF4) in module ‘navicat.exe’. Read of address ***(如 ...
- python 文档
python 文档 https://docs.python.org/2/library/index.html
- 第四百一十六节,Tensorflow简介与安装
第四百一十六节,Tensorflow简介与安装 TensorFlow是什么 Tensorflow是一个Google开发的第二代机器学习系统,克服了第一代系统DistBelief仅能开发神经网络算法.难 ...
- Mysql 5.7 安装 解压缩版 忘记密码
[参考]Mysql 命令大全 [参考]MySQL 5.7.19 忘记密码 重置密码 配置文件my.ini示例 服务启动后停止 log配置 [参考]MySQL5.7.19 服务挂掉 自动关闭 mysql ...
- centos 7 rpm方式安装mysql
一.下载rpm 二.安装 1.用rz上传到centos上,目录为/home/upload 2.解压 tar -xvf mysql-5.7.25-1.el7.x86_64.rpm-bundle.tar ...