单链表的节点创建、访问、插入、遍历、逆序

创建一个链表的节点

/*
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)  ,所以就不能将最后一个节点可进行逆序
}


提示:该程序中注释太多,在实际编程过程中注释不应该太多,但也不应该太少,保证代码的可读性就好!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wu Junwu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值