创建一个链表的节点
/*
time:2020年9月9日20:12:24
obective:创建一个链表
author:wujunuw
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//创建节点
struct node
{
int data; //有效数据
struct node *PNext; //指向下一节点的指针
};
int main(void)
{
//第一步:申请堆内存
struct node *p=(struct node *)malloc(sizeof(struct node));
//第一步申请后需要验证一下是否申请成功
if(NULL==p)
{
printf("malloc error.\n");
return -1;
}
//第二步:清理申请到的堆内存
// 方法一:memset(*p,0,sizeof(struct node)).
// 方法二:bzero(*p,sizeof(struct node))
bzero(p,sizeof(struct node));
//第三步:填充节点
p->data=1; //将数据1填充到有效数据段
p->PNext=NULL; //暂时将其节点指针指向NULL,将来是要指向下一个节点的
return 0;
}
创建并访问一个单链表
/*
time:2020年9月9日21:54:03
obective:只用PHeader头指针访问链表的各节点
author:wujunuw
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct node
{
int data; //有效数据
struct node *PNext; //指向下一节点的指针
};
//创建多个节点
int main(void)
{
//创建一个头指针
struct node *pHeader=NULL;
/***************************************************************************************************************************/
//创建第一个节点1
//第一步:申请堆内存
struct node *p1=(struct node *)malloc(sizeof(struct node));
//第二步申请后需要验证一下是否申请成功
if(NULL==p1)
{
printf("malloc error.\n");
return -1;
}
//第三步:清理申请到的堆内存
// 方法一:memset(*p1,0,sizeof(struct node)).
// 方法二:bzero(p1,sizeof(struct node))
bzero(p1,sizeof(struct node));
//第四步:填充节点
p1->data=1; //将数据1填充到有效数据段
p1->PNext=NULL; //指向下一个节点2的首地址
pHeader=p1; //将节点1的首地址和头指针相关联
/**********************************************************************************************************************/
//创建第二个节点2
//第一步:申请堆内存
struct node *p2=(struct node *)malloc(sizeof(struct node));
//第二步堆内存申请后需要验证一下是否申请成功
if(NULL==p2)
{
printf("malloc error.\n");
return -1;
}
//第三步:清理申请到的堆内存
// 方法一:memset(*p1,0,sizeof(struct node)).
// 方法二:bzero(p1,sizeof(struct node))
bzero(p2,sizeof(struct node));
//第四步:填充节点
p2->data=2; //将数据1填充到有效数据段
p2->PNext=NULL; //指向下一个节点3的首地址
p1->PNext=p2; //将节点2的首地址和前一个节点的指针相关联
/***************************************************************************************************************************************/
//创建第三个节点3
//第一步:申请堆内存
struct node *p3=(struct node *)malloc(sizeof(struct node));
//第二步堆内存申请后需要验证一下是否申请成功
if(NULL==p3)
{
printf("malloc error.\n");
return -1;
}
//第三步:清理申请到的堆内存
// 方法一:memset(*p1,0,sizeof(struct node)).
// 方法二:bzero(p1,sizeof(struct node))
bzero(p3,sizeof(struct node));
//第四步:填充节点
p3->data=3; //将数据1填充到有效数据段
p2->PNext=p3; //将节点3的首地址和前一个节点的指针相关联
//至此上面的程序已经创建好了一个完整的链表
//下面我们对链表中的各个节点进行访问
//访问第一个节点的data
printf("node1_data=%d\n",pHeader->data); //用指针访问每个节点的data
printf("p1->data=%d\n",p1->data); //用每个节点的指针访问该节点的data
//访问第二个节点的data
printf("node2_data=%d\n",pHeader->PNext->data);
printf("p2->data=%d\n",p2->data);
//访问第三个节点的data
printf("node3_data=%d\n",pHeader->PNext->PNext->data);
printf("p3->data=%d\n",p3->data);
return 0;
}
运行结果:
node1_data=1
p1->data=1
node2_data=2
p2->data=2
node3_data=3
p3->data=3
插入新节点
方法有两种:在尾节点后插入或者在头节点后插入
(1)在尾节点后插入
/*
time:2020年9月11日14:35:16
obective:在链表的最后一个节点插入一个新节点
author:wujunuw
*/
#include<stdio.h>
#include<stdlib.h> //malloc的头文件
#include<string.h> //memset或bzero头文件
//创建节点模型
struct node
{
int data;
struct node *pNext;
};
//对创建节点的代码进行封装
//返回值:指针,指针指向我们本函数新创建的一个节点的首地址
//函数功能:创建节点
struct node* create_node(int data)
{
struct node *p=(struct node *)malloc(sizeof(struct node)); //指针p指向节点的首地址
if(NULL==p) //if语句的作用是为了检查堆内存的申请是否成功;
{
printf("malloc error.\n");
return 0; // return NULL;
}
bzero(p,sizeof(struct node)); //清理堆内存
p->data=data;
p->pNext=NULL;
return p;
}
//尾节点插入函数
void insert_tailnode(struct node *pHeader,struct node *new)
{
struct node *p=pHeader;
while(NULL!=p->pNext)
{
p=p->pNext; //当遍历的节点pNext指针没有指向NULL,则继续遍历下一个节点
}
p->pNext=new;
}
int main(void)
{
//创建一个头指针
//struct node *pHeader=NULL; //不能这样写。因为先这样写会导致内存分配错误Segmentation fault。
//一开始就定义头指针指向NULL,那么在函数insert_tailnode(struct node *pHeader,struct node *new)中,一开始
//就会对第一个节点p指针并指向头指针,这样就默认了已经创建了一个节点(因为只有节点才有指针p)。
//而实际上我们并没有创建节点,所以在语句struct node *p=pHeader;进行链接的时根本找不到节点指针p,所以显示内存分配错误。
struct node *pHeader=create_node(111);
//定义头指针pHeader并指向第一个节点,返回值是第一个节点的首地址
insert_tailnode(pHeader,create_node(222));
//第一个节点的pNext指针指向第二个节点create_node(222)的首地址,同时返回第二个节点的首地址
insert_tailnode(pHeader,create_node(333));
//第二个节点的pNext指针指向第三个节点create_node(333)的首地址,同时返回第三个节点的首地址
insert_tailnode(pHeader,create_node(444));
//第三个节点的pNext指针指向第四个节点create_node(333)的首地址,同时返回第四个节点的首地址
//依次往下书写,可以创建更多的节点
//遍历输出
//第一个节点data访问
printf("node1_data1=%d\n",pHeader->data);
//第二个节点data访问
printf("node1_data2=%d\n",pHeader->pNext->data);
//第三个节点data访问
printf("node1_data3=%d\n",pHeader->pNext->pNext->data);
//第四个节点data访问
printf("node1_data4=%d\n",pHeader->pNext->pNext->pNext->data);
return 0;
}
(2)在头节点后插入
/*
time:时间
obective:创建头节点
author:wujunuw
*/
#include<stdio.h>
#include<stdlib.h> //malloc的头文件
#include<string.h> //memset或bzero头文件
//创建节点模型
struct node
{
int data;
struct node *pNext;
};
//对创建节点的代码进行封装
//返回值:指针,指针指向我们本函数新创建的一个节点的首地址
//函数功能:创建节点
struct node* create_node(int data)
{
struct node *p=(struct node *)malloc(sizeof(struct node)); //指针p指向节点的首地址
if(NULL==p) //if语句的作用是为了检查堆内存的申请是否成功;
{
printf("malloc error.\n");
return 0; // return NULL;
}
bzero(p,sizeof(struct node)); //清理堆内存
p->data=data;
p->pNext=NULL;
return p;
}
//函数功能:在节点的后面创建新节点
void insert_tailnode(struct node *pHeader,struct node *new)
{
struct node *p=pHeader;
int cnt=0; //此处语句改为int cnt;会导致野指针现象
while(NULL!=p->pNext)
{
p=p->pNext; //当遍历的节点pNext指针没有指向NULL,则继续遍历下一个节点
cnt++;
}
p->pNext=new;
pHeader->data=cnt+1;
}
//在头节点后第一个节点前插入新节点
void insert_headnode(struct node *pHeader,struct node *new)
{ /*
//第一步将头节点的pNext指向新节点的首地址
pHeader->pNext=new;
//第二步将新节点的pNext指向原来的第一个节点首地址
new->pNext=pHeader->pNext;
//第三步计算节点数
pHeader->data+=1;
如果将其写成上面的顺序,那么运行时就会出错。为什么会这样?
原因:如果首先执行pHeader->pNext=new; 语句,此时不会出现问题。
但是再执行new->pNext=pHeader->pNext; 语句,则会出现问题。因为在执行上面pHeader->pNext=new;就将之前的第一个节点首地址和头节点断掉了关系。
如果再去执行 new->pNext=pHeader->pNext; 语句,则是将头节点中的Header->pNext赋给新节点。这样就会导致出错
*/
// 第1步: 新节点的next指向原来的第一个节点
new->pNext = pHeader->pNext;
// 第2步: 头节点的next指向新节点的地址
pHeader->pNext = new;
// 第3步: 头节点中的计数要加1
pHeader->data += 1;
//小结:当pHeader->pNext在左值代表的是节点中pNext存储的内容;
//当pHeader->pNext在右值代表的是下一个节点的首地址
}
int main(void)
{
struct node *pHeader=create_node(0); //定义一个头指针并将头节点的首地址赋给头指针
insert_headnode(pHeader,create_node(111));
insert_headnode(pHeader,create_node(222));
insert_headnode(pHeader,create_node(333));
printf("header_data(node_nunmber)=%d\n",pHeader->data);
printf("new=%d\n",pHeader->pNext->data);
printf("node1_data2=%d\n",pHeader->pNext->pNext->data);
printf("node1_data3=%d\n",pHeader->pNext->pNext->pNext->data);
return 0;
}
遍历:前面我们其实就是“手动”将每一个节点的data遍历了一遍,下面我们不用我们将“手动”的遍历方式封装成函数traverse(struct node *pHeader)
/*
time:2020年9月16日21:56:15
objective:遍历
author:wujunwu
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct node
{
int data;
struct node *pNext;
};
struct node *create_node(int data); //创建节点,返回值是新节点的首地址,形参是data
void insert_tailnode(struct node *pHeader,struct node *new); //在末尾插入新节点
void insert_headnode(struct node *pHeader,struct node *new); //在中间插入新节点
void traverse(struct node *pHeader); //遍历函数
int main (void)
{
struct node *pHeader=create_node(0); //头指针赋给第一个节点
insert_headnode(pHeader,create_node(111));
insert_headnode(pHeader,create_node(222));
insert_headnode(pHeader,create_node(333));
printf("head node data:%d\n",pHeader->data);
traverse(pHeader);
return 0;
}
struct node * create_node(int data)
{
struct node *p= (struct node *)malloc(sizeof(struct node));
if(NULL==p)
{
printf("malloc error\n");
return 0;
}
p->data=data;
p->pNext=NULL;
return p;
}
void insert_tailnode(struct node *pHeader,struct node *new)
{
int cnt=0;
struct node *p=pHeader;
while(NULL!=p->pNext)
{
p=p->pNext;
cnt++;
}
p->pNext=new;
pHeader->data=cnt+1;
}
void insert_headnode(struct node *pHeader,struct node *new)
{
new->pNext=pHeader->pNext;
pHeader->pNext=new;
pHeader->data +=1;
}
void traverse(struct node *pHeader) //形参在调用之前系统不给分配内存
{
//struct node *pHeader; 这样不行
struct node *p=pHeader;
printf("..................start traversr...................\n");
while(NULL!=p->pNext)
{
p=p->pNext; //指针指向下一个节点首地址
printf("p->data:%d\n",p->data);
}
printf("..................end traversr...................\n");
}
逆序:将单链表中的每一个节点顺序反过来
/*
time:2020年9月16日21:56:15
objective:遍历
author:wujunwu
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct node
{
int data;
struct node *pNext;
};
struct node *create_node(int data); //创建节点,返回值是新节点的首地址,形参是data
void insert_tailnode(struct node *pHeader,struct node *new); //在末尾插入新节点
void insert_headnode(struct node *pHeader,struct node *new); //在中间插入新节点
void traverse(struct node *pHeader); //遍历函数
void invert(struct node *pHeader); //逆序
int main (void)
{
struct node *pHeader=create_node(0); //头指针赋给第一个节点
insert_headnode(pHeader,create_node(111));
insert_headnode(pHeader,create_node(222));
insert_headnode(pHeader,create_node(333));
printf("head node data:%d\n",pHeader->data);
traverse(pHeader);
invert(pHeader);
traverse(pHeader);
return 0;
}
struct node * create_node(int data)
{
struct node *p= (struct node *)malloc(sizeof(struct node));
if(NULL==p)
{
printf("malloc error\n");
return 0;
}
p->data=data;
p->pNext=NULL;
return p;
}
void insert_tailnode(struct node *pHeader,struct node *new)
{
int cnt=0;
struct node *p=pHeader;
while(NULL!=p->pNext)
{
p=p->pNext;
cnt++;
}
p->pNext=new;
pHeader->data=cnt+1;
}
void insert_headnode(struct node *pHeader,struct node *new)
{
new->pNext=pHeader->pNext;
pHeader->pNext=new;
pHeader->data +=1;
}
void traverse(struct node *pHeader) //形参在调用之前系统不给分配内存
{
//struct node *pHeader; 这样不行
struct node *p=pHeader;
printf("..................start traversr...................\n");
while(NULL!=p->pNext)
{
p=p->pNext; //指针指向下一个节点首地址
printf("p->data:%d\n",p->data);
}
printf("..................end traversr...................\n");
}
//逆序
//思路:将链表遍历一遍,再将每次遍历找到的尾节点都头插入得到
void invert(struct node *pHeader)
{
struct node *p=pHeader->pNext; //定义指针
struct node *pback;
//当链表只有一个头节点或者只有一个有效节点时,就没必要逆序了。
if((NULL==p)||(NULL==pHeader->pNext))
return;
//链表为多个有效节点(2个及2个以上)
while(NULL!=p->pNext)
{
pback=p->pNext; //保存下一个节点地址
if(p==pHeader->pNext)
{
p->pNext=NULL;
}
else
{
p->pNext=pHeader->pNext; //将第一个有效节点的首地址赋给当前遍历的节点的pNext
}
pHeader->pNext = p; //将当前遍历节点的首地址赋给头节点
p = pback; //将下一个节点赋给指针P,继续遍历下一个节点
}
insert_headnode(pHeader, p); //因为遍历到了最后一个节点时候,p->pNext 不满足while(NULL!=p->pNext) ,所以就不能将最后一个节点可进行逆序
}
提示:该程序中注释太多,在实际编程过程中注释不应该太多,但也不应该太少,保证代码的可读性就好!!!