array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 初识单链表--创建、插入、遍历 - 爱码网

    前两天由于"q->next=p->next; p->next=q;"这两句代码的困扰,花了两天时间也算是搞清楚一点,记录下来与大家一起分享。该篇的核心内容在于:

  • 链表中每一个节点的"原生输出"与使用头指针访问的形式"遍历输出"
  • 头节点引入的目的、意义
  • 链表的遍历是如何一步一步演化成为我们所看到的样子的

一、创建链表

注意:

一个链表中可以没有头结点,但是绝对不可能没有头指针的。链表中第一个节点的存储位置叫头指针。这里的第一个节点可以是头结点(不存放有效数据)也可以是存储有效数据的第一个节点。

使用堆内存来创建一个链表节点的步骤:
1.申请堆内存,大小是一个节点的大小(检查申请的结果是否正确)
2.清除申请到的堆内存
3.填充新节点的data域和指针域

下面的代码是没有头结点的一个简单的链表。后面会提到带有头结点的,以及为什么要带有头结点?

/*

目的:实现只包含一个节点的链表的初始化,以及数据输出
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
int data;
struct Node *next;
};
int main()
{
struct Node *pHead = NULL;//定义头指针,为了安全起见,给pHead置NULL
struct Node *p = (struct Node*)malloc(sizeof(struct Node));
pHead = p;
if(NULL == p)
{
printf("malloc error.\n");
return -1;
}
memset(p,0,sizeof(struct Node));
p->data = 1;
p->next = NULL;

printf("p->data = %d\n",p->data);
printf("pHead->data = %d\n",p->data);
return 0;

}

输出结果:

初识单链表--创建、插入、遍历

从实验结果可以得知:p->data和pHead->data的结果是一致的,这个对链表的理解非常重要。先搁在这,请往下看。

/*
目的:"原原本本的使用每一个节点打印输出数据"与"使用头指针的方式打印出各个节点的数据"
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
int data;
struct Node *next;
};
int main()
{
struct Node *pHead = NULL;
struct Node *p = (struct Node*)malloc(sizeof(struct Node));
pHead = p;
if(NULL == p)
{
printf("malloc error.\n");
return -1;
}
memset(p,0,sizeof(struct Node));
p->data = 11;
p->next = NULL;
printf("p->data = %d\n",p->data);
printf("pHead->data = %d\n\n",pHead->data);

struct Node *p1 = (struct Node*)malloc(sizeof(struct Node));
if(NULL == p1)
{
printf("malloc error.\n");
return -1;
}
memset(p1,0,sizeof(struct Node));
p1->data = 22;
p1->next = NULL;

p->next = p1;
printf("p1->data = %d\n",p1->data);
printf("pHead->next->data = %d\n\n",pHead->next->data);

struct Node *p2 = (struct Node*)malloc(sizeof(struct Node));
if(NULL == p2)
{
printf("malloc error.\n");
return -1;
}
memset(p2,0,sizeof(struct Node));
p2->data = 33;
p2->next = NULL;

p1->next = p2;
printf("p2->data = %d\n",p2->data);
printf("pHead->next->next->data = %d\n",pHead->next->next->data);
return 0;
}

实验结果:

初识单链表--创建、插入、遍历

实验结果得出来的结论:p->data和pHead->data的结果仍然是一致的。

初识单链表--创建、插入、遍历

以上两组代码,其实就是在说明,上图的那一个意思(图画的比较丑),但是要表达的意思就在图里面了。

二、链表插入

1. 尾部插入(比较简单)

<1>在上面的链表的创建中,我们明显可以看到:我们每创建一个节点都写了相同的一段代码出来。在这里我们把它封装成为一个具体的函数。

<2>要想在链表的尾部插入一个节点,我们需要两个步骤:a.找到最后的那一个节点  b.把将要添加的新的节点与先前的最后一个节点连接起来即可。

初识单链表--创建、插入、遍历

/*
目的:一个头指针加上一个封装的节点,实现链表的尾部插入操作。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct  Node
{
int data;
struct Node *next;
};
struct Node *create_node(int data)
{
struct Node *p = (struct Node*)malloc(sizeof(struct Node));
if(NULL == p)
{
printf("malloc error\n");
return -1;
}
memset(p,0,sizeof(struct Node));
p->data = data;
p->next = NULL;
return p;
}
void insert_tail(struct Node *pHead,struct Node *pNew)
{
struct Node *p = pHead;
while(NULL != p->next)
{
p = p->next;
}
p->next = pNew;
}
int main()
{
struct Node *pHead = create_node(1);
insert_tail(pHead,create_node(2));
insert_tail(pHead,create_node(3));

insert_tail(pHead,create_node(4));

printf("pHead->data = %d\n",pHead->data);
printf("pHead->next->data = %d\n",pHead->next->data);
printf("pHead->next->next->data = %d\n",pHead->next->next->data);
printf("pHead->next->next->next->data = %d\n",pHead->next->next->next->data);
return 0;

}

实验结果:

初识单链表--创建、插入、遍历

加入自己亲自一步一步的看下来的话,会不会有一个疑问呢?先前struct Node *pHead = NULL;的,而现在却是:

struct Node *pHead = create_node(1);为什么呢?

我们可以试着写成struct Node *pHead = NULL这时候,会出现Segmentation fault (core dumped)段错误提示。

这里已经开始有一点头结点的味道出来了,接着往下看。

2. 头部插入

在进行头部插入的过程 ,很难想象没有头结点是什么样子的。

初识单链表--创建、插入、遍历

头结点的特点:
<1>紧跟在头指针的后面
<2>头节点的数据部分是空的(可以存放链表的节点数)
<3>指针部分指向第一个有效的节点。
<4>头结点的创建在创建头指针的时候与头指针一并的创建,并且和头指针关联起来。
/*
目的:头部插入
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *create_node(int data)
{
struct Node *p = (struct Node*)malloc(sizeof(struct Node));
if(NULL == p)
{
printf("malloc error\n");
return -1;
}
memset(p,0,sizeof(struct Node));
p->data = data;
p->next = NULL;
return p;
}
void insert_head(struct Node *pHead,struct Node *pNew)
{
pNew->next = pHead->next;
pHead->next = pNew;

}
void insert_tail(struct Node *pHead,struct Node *pNew)
{
struct Node *p = pHead;
while(NULL != p->next)
{
p = p->next;
}
p->next = pNew;
}
int main()
{
struct Node *pHead = create_node(-1);//头节点里面不存放数据也是白搁在那了,我们给它置-1
insert_head(pHead,create_node(1));
insert_head(pHead,create_node(2));

printf("head node = %d\n",pHead->data);
printf("the  first node = %d\n",pHead->next->data);
printf("the  second node = %d\n",pHead->next->next->data);
return 0;

}

实验结果:

初识单链表--创建、插入、遍历

三、链表遍历

前面的链表创建、插入可以到,我们在每一次输出一个节点数据的时候很麻烦的,在这里我们把它封装成为一个函数。

注意:

1. 在traverse_list_1()中我们使用"struct Node *p = pHead->next"的手法来跳过头结点里面的数据,从第一个有效数据开始。

但是,引出了打印不完全的问题,我们在while()循环的外面,额外的补充了一个printf()函数,显得格格不入。

2. 这才引出了traverse_list_2()的降临。仔细的品味while()循环中的那个判断条件是实现链表遍历的关键所在。

/*
代码实现的功能:实现链表创建、插入、遍历
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *create_node(int data)
{
struct Node *p = (struct Node*)malloc(sizeof(struct Node));
if(NULL == p)
{
printf("malloc error.\n");
return -1;
}
memset(p,0,sizeof(struct Node));
p->data = data;
p->next = NULL;
return p;
}
void insert_tail(struct Node *pHead,struct Node *pNew)
{
struct Node *p = pHead;
while(NULL != p->next)
{
p = p->next;
}
p->next = pNew;
}
void insert_head(struct Node *pHead,struct Node *pNew)
{
struct Node *p = pHead;
pNew->next = p->next;
p->next = pNew;
}
void traverse_list_1(struct Node *pHead)
{
struct Node *p = pHead->next;
while(NULL != p->next)
{
printf("data = %d\n",p->data);
p = p->next;
}
printf("data = %d\n",p->data);
}
void traverse_list_2(struct Node *pHead)
{
struct Node *p = pHead;
while(NULL != p->next)
{
p = p->next;
printf("data = %d\n",p->data);
}
}
int main()
{
struct Node *pHead = create_node(0);
insert_tail(pHead,create_node(1));
insert_tail(pHead,create_node(2));
insert_tail(pHead,create_node(3));

insert_head(pHead,create_node(123));

traverse_list_1(pHead);
printf("*********************\n");
traverse_list_2(pHead);
#if 0
printf("node 1 = %d\n",pHead->data);
printf("node 2 = %d\n",pHead->next->data);
printf("node 3 = %d\n",pHead->next->next->data);
printf("node 4 = %d\n",pHead->next->next->next->data);
printf("node 5 = %d\n",pHead->next->next->next->next->data);
#endif
return 0;
}

实验结果:

初识单链表--创建、插入、遍历

总结:

以上便是自己这两天的对链表的创建、插入、遍历操作的一些感悟、总结。

写的十分的仓促,加上自己水平有限,假如有什么错误的地方,欢迎大家指正、纠错!!

相关文章: