实现个算法,懒得手写链表,于是用C++的forward_list,没有next()方法感觉很不好使,比如一个对单向链表的最简单功能要求:

input:

1 2 5 3 4

output:

1->2->5->3->4

相当于仅仅实现了插入、遍历2个功能(当然遍历功能稍微修改就是销毁链表了)

用纯C写了份测试代码

/* 基本数据结构的定义以及函数的声明 */
typedef int ElemType; typedef struct Node
{
ElemType elem;
struct Node* next;
} Node, * NodePtr, **ForwardList; NodePtr createNode(ElemType x); void showList(ForwardList lst); void destroyList(ForwardList lst); // 创建元素为x的节点并插入到节点where后面
// 若where为NULL, 则插入到链表lst的首部作为首节点
// 返回新节点的指针
NodePtr insertAfterNode(NodePtr where, ElemType x,
ForwardList lst);
/* 链表相关函数的具体实现 */
NodePtr createNode(ElemType x)
{
NodePtr pNode = (NodePtr) malloc(sizeof(Node));
if (pNode == NULL) {
perror("malloc");
exit();
} pNode->elem = x;
pNode->next = NULL;
return pNode;
} NodePtr insertAfterNode(const NodePtr where,
ElemType x, ForwardList lst)
{
NodePtr pNode = createNode(x); if (where == NULL) {
*lst = pNode;
} else {
pNode->next = where->next;
where->next = pNode;
} return pNode;
} void showList(ForwardList lst)
{
printf("显示链表: ");
NodePtr curr = *lst;
while (curr->next != NULL) {
printf("%d->", curr->elem);
curr = curr->next;
}
printf("%d\n", curr->elem);
} void destroyList(ForwardList lst)
{
printf("销毁链表: ");
NodePtr curr = *lst;
while (curr != NULL) {
NodePtr next = curr->next;
printf("%d ", curr->elem);
free(curr);
curr = next;
}
printf("\n");
}
/* 测试代码 */
int main()
{
NodePtr head = NULL;
initListFromStdin(&head);
showList(&head);
destroyList(&head);
return ;
}

三个部分都是写在一份代码里(forward_list.c)的,测试结果如下

$ ls
data.in forward_list.c
$ cat data.in $ gcc forward_list.c -std=c99
$ ./a.out <data.in
显示链表: ->->->->
销毁链表:

由于是不需要考虑周全的C代码,所以很多C++的一些工程性的技巧不需考虑,比如模板、const,说起来之前没把C代码封装成函数的时候就曾经导致链表的头节点被修改,最后销毁链表时,遍历后头节点直接指向了最后一个节点,导致前4个节点都没被销毁。如果合理地使用const,在编译期就能检查出来。

嘛,其实这么一写下来,C++的forward_list版本也就写出来了,毕竟我的链表插入函数就是模仿forward_list的,但是写出来有点别扭。因为需要遍历到倒数第2个节点停止,最后代码如下

#include <cstdio>
#include <forward_list>
using namespace std; // 取得前向迭代器it的下一个迭代器
template <typename FwIter>
FwIter nextIter(FwIter it)
{
return ++it;
} int main()
{
forward_list<int> lst;
int x; for (auto it = lst.before_begin();
fscanf(stdin, "%d", &x) == 1;
)
{
it = lst.emplace_after(it, x);
} // 按照a0->a1->...->an的格式输出
auto it = lst.begin();
while (nextIter(it) != lst.end())
{
printf("%d->", *it++);
}
printf("%d\n", *it);
return 0;
}

既然C++不提供next()函数那就只有手写一个,因为迭代器传参数时拷贝了一份,所以nextIter()直接返回++it并不会对原迭代器进行修改,而是修改的原迭代器的拷贝。

注意一点就是,在顺序插入构建链表时需要记录链表最后一个节点,跟我的C代码实现风格一致(好吧其实我本来就是仿STL实现的)。

那么初始值就是before_begin()而不是begin(),因为空链表不存在begin(),确切的说空链表的初始节点为NULL。

测试代码,这里_M_node是glibc++的forward_list迭代器底层实现部分,并不是跨平台代码。迭代器相当于把节点地址进行了一层封装,而_M_node则是节点地址。

#include <forward_list>
#include <stdio.h> int main()
{
std::forward_list<int> lst;
printf("begin()地址: %p\n", lst.begin()._M_node);
printf("before_begin()地址: %p\n", lst.before_begin()._M_node);
return ;
}

结果如下:

$ g++ test.cc -std=c++
$ ./a.out
begin()地址: (nil)
before_begin()地址: 0x7fffb0896b60

C语言实现简单的单向链表(创建、插入、删除)及等效STL实现代码的更多相关文章

  1. [PHP] 数据结构-链表创建-插入-删除-查找的PHP实现

    链表获取元素1.声明结点p指向链表第一个结点,j初始化1开始2.j<i,p指向下一结点,因为此时p是指向的p的next,因此不需要等于3.如果到末尾了,p还为null,就是没有查找到 插入元素1 ...

  2. 单链表创建、删除、查找、插入之C语言实现

    本文将详细的介绍C语言单链表的创建.删除.查找.插入以及输出功能 一.创建 #include<stdio.h> #include<stdlib.h> typedef int E ...

  3. 链表的C++实现——创建-插入-删除-输出-清空

    注:学习了数据结构与算法分析后,对链表进行了C++实现,参考博文:http://www.cnblogs.com/tao560532/articles/2199280.html 环境:VS2013 // ...

  4. 单链表的插入删除操作(c++实现)

    下列代码实现的是单链表的按序插入.链表元素的删除.链表的输出 // mylink.h 代码 #ifndef MYLINK_H #define MYLINK_H #include<iostream ...

  5. c简单的单向链表

    ps:list链表  node节点 在链表中节点就是一个个的结构体 堆空间由于在申请内存时,地址是随机的,所以要用链表的方式将其连接起来,但是链表头的地址要知道. 每个节点包含两个部分:数据区和地址区 ...

  6. jQuery 节点操作(创建 插入 删除 复制 替换 包裹)

    一,创建元素节点: 第1个步骤可以使用jQuery的工厂函数$()来完成,格式如下: $(html); $(html)方法会根据传入的HTML标记字符串,创建一个DOM对象,并将这个DOM对象包装成一 ...

  7. (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作

    上午写了下单向循环链表的程序,今天下午我把双向链表的程序写完了.其实双向链表和单向链表也是有很多相似的地方的,听名字可以猜到,每个节点都包含两个指针,一个指针指向上一个节点,一个指针指向下一个节点.这 ...

  8. 复习下C 链表操作(单向链表)

    Object-C 作为C 的包装语言(运行时.消息机制).如果不熟悉C 的话实在玩得太肤浅. 随便深入oc 内部都会接触到C. runtime .GCD.Block.消息机制... 所有强大的功能无不 ...

  9. 原生JS实现单向链表

    1.前言 用JS实现一个简单的单向链表,并完成相关的功能 2.功能说明 push(value):从链表尾部添加一个新的节点 insertAfer(value,item):向链表中的item节点之后插入 ...

随机推荐

  1. Falsy Bouncer

    真假美猴王! 删除数组中的所有假值. 在JavaScript中,假值有false.null.0."".undefined 和 NaN. 这是一些对你有帮助的资源: Boolean ...

  2. WebApplication和WebSite的简单区别

    初步认识网站和网站应用程序区别 1. 网页头部文件 网站 <%@ Page Language="VB" AutoEventWireup="false" C ...

  3. Asp.net Core + EF Core + Bootstrap搭建的MVC后台通用管理系统模板(跨平台版本)

    Asp.net Core + EF Core + Bootstrap搭建的MVC后台通用管理系统模板(跨平台版本) 原创 2016年07月22日 10:33:51 23125 6月随着.NET COR ...

  4. confluence 部署与配置

    什么是confluence 部署步骤 安装 下载目标平台目标版本安装包.windows为例 若第一次默认安装 一直下一步. 若再次安装 下载并将mysql 连接控件放到 /xxx/xxx/conflu ...

  5. bzip2压缩 解压缩

    压缩/解压缩压缩/解压缩之后的文件名称 必须是bz2 首先是  -z   压缩文件-d 解压缩!

  6. Tencent tinker 出现pre-verified crash

    异常类型:app运行时异常 手机型号:sumsung N9008 手机系统版本:android4.4.2 tinker版本: 1.8.1 gradle版本::2.3.3 是否使用热更新SDK: Tin ...

  7. Django-RestfulFramework --- DRF

    >> (1)RESTful api 规范 和 DRF 的基本介绍 >> (2)DRF 的 认证系统实现 >> (3)DRF 的 权限系统实现 >> (4 ...

  8. GridView 控制默认分页页码间距 及字体大小

    GridView 控制默认分页页码间距 及字体大小 PagerCss TD A:hover { WIDTH: 20px; COLOR: black; padding-left: 4px; paddin ...

  9. 手机通过笔记本开的WIFI访问TOMCAT服务器站点示例

    我一直想用手机连上笔记本上的服务器TOMCAT,尝试了好久没连上,实验室一个妹子会这个技术,我也想学,自己摸索着学了几次,没成功,今晚想个办法试了一下,可以连接了,以后可以做手机网站开发了,这也是移动 ...

  10. Slice Header中的field_pic_flag的含义?

    编码模式指帧编码.场编码.帧场自适应编码.当这个句法元素取值为1时属于场编码:0为非场编码. 序列参数集中的句法元素frame_mbs_only_flag和mb_adaptive_frame_fiel ...