数据结构之单向链表

一、链表

        链表就是表的离散存储,逻辑结构表结构,通过指针方式实现逻辑上的链接 。

        链表的一个节点包括两个部分:数据域和指针域。

 

 二、链表的定义

        链表的创建需要定义其存储的数据以及指向下一个节点的指针。

#include <stdio.h>
#include <stdlib.h>
//链表
//1. 定义数据类型
typedef int Data_t;

//2. 定义节点类型
typedef struct node_t 
{
	Data_t data;	    //数据域:存储数据本身的
	struct node_t *next;//指针域:存储逻辑关系
}node_t;

三、链表的创建

        创建链表选择用动态申请的方式,需要存储多少数据就申请多少内存,避免内存的浪费。

//创建空链表
//返回值 失败返回 NULL 
//成功 返回 链表头节点地址
node_t * create_link_list(void)
{
	node_t *p = (node_t *)malloc(sizeof(node_t));
	if(p) p->next = NULL; //空链表 头节点next=NULL
	return p;
}

四、链表插入节点

        在插入新的节点就意味着要先断开原来的指向关系,重新建立新的指向关系。

        例如,现需要将节点q插入到链表中,第一步,需要进行入参检查,判断链表是否为空链表;

        第二步,申请q的内存空间,并将所要存的数据传给q;

        第三步,找到要插入的地方的前一个节点p;

        第四步,先将q的next指向p的下一个节点,再将p的next指向q。

        

 代码实现:

//链表插入节点
int insert_data(node_t *head, int index, Data_t data)
{
    //1.入参检查
	if(head == NULL) return -1;
	//2. 创建新节点空间
	node_t *q = (node_t *)malloc(sizeof(node_t));
	if(q == NULL) return -2;
	q->data = data;//存储data到q节点中
	//3. p指针 找到index的前一个节点地址	
	node_t *p = head;
	//移动p 指向index前一个节点位置  p 到最末尾仍然没有到index前一个位置
	for(int i=0;i<index && p != NULL ; i++) p = p->next;
	if( p == NULL  ) 
	{//p 到最末尾仍然没有到index前一个位置
		printf("%d 位置 没有在链表上!\n",index);
		return -3;
	}
	else 
	{//p找到index位置的前一个节点的地址 
		//4.将新节点插入到链表中
		q->next = p->next;
		p->next = q;
	}
	return 0;
}

五、链表中删除节点

        删除一个节点,需要将删除的节点的上一个节点的next指向删除的下一个节点。

        第一步,分别找到要删除的节点的上一个节点和下一个节点;

        第二步,用一个节点q将所要删除的节点先进行备份;

        第三步,将所要删除的节点的上一个节点的next指向节点q的next。

        第四步,释放节点,free(q)。

如下图所示:

代码实现:

/给定下标index删除该节点
int del_data(node_t *head, int index)
{
	if(head == NULL || head->next == NULL ) return -1;
	//1.找到index前一个节点地址p
	node_t *p = head;
	for( int i=0;i<index && p->next != NULL ; i++) p=p->next;
	if(p->next == NULL)
	{
		printf("%d 节点没有在链表上,删除失败!\n",index);
		return -2;
	}
	//2. 备份index节点地址 q
	node_t *q = p->next;
	//3. 链接index左右两边的节点
	p->next = q->next;
	//4. 释放节点q节点
	free(q);
	return 0;
}

五、两个链表的拼接

        若要连接和表1和表2,就需要先找到表1的最后一个节点,将表1的最后一个节点指向表2除了头节点之外的第一个节点,最后将表2的头节点的next置空。

 

代码实现:

//拼接两个链表
int connect_link_list(node_t *head1,node_t *head2)
{
	if(head1 == NULL || head2 == NULL) return 0;
	if(head2->next == NULL) return 0;
	node_t *p = head1;
	//找最后节点p
	while(p->next != NULL) p = p->next;
	//拼接2两个节点
	p->next = head2->next;
	//head2 设置为空表 
	head2->next = NULL;
	return 0;
}

六、翻转链表

        翻转链表就是将链表中的最后一个节点与第一个节点交换,倒数第二个与第二个交换,依次交换知道全部交换完为止。

代码实现:

//链表翻转
int link_list_Reverse(node_t* head)
{
	if(head ==NULL || head->next == NULL) return -1;
	//1. 头节点 与 数据节点分离
	node_t *p = head->next;
	node_t *q; // 临时中转指针
	head->next = NULL; //头 空链表
	//2. 依次从身体中取出一个节点 头插发到链表
	while(p != NULL) 
	{
		//insert_data(head, 0, p->data); // 存在内存泄露
		q = p;		//备份p到q
		p=p->next;	//移动p
		q->next = head->next; // 头部插入
		head->next = q;	 //
	}
	return 0;
}

七、遍历链表

//遍历链表
int show_link_list(node_t *head)
{
	if(head == NULL ) return -1;
	if(head->next == NULL) 
	{
		printf("\n");
		return 0;
	}
	node_t *p = head->next;
	for(  ; p != NULL  ; p=p->next )
	  printf("%d ", p->data);
	printf("\n");
}

八、调用

int main()
{
	//创建一个链表
	node_t *head = create_link_list();
	if(head == NULL) 
	{
		printf("创建失败!!\n");
		return -1;
	}
	else 
	  printf("创建成功 head=%p\n",head);

	node_t *head2 = create_link_list();
	if(head2 == NULL) 
	{
		printf("创建失败!!\n");
		return -1;
	}
	else 
	  printf("创建成功 head2=%p\n",head2);

	//插入数据到链表
	insert_data(head, 0, 10);
	insert_data(head, 1, 20);
	insert_data(head, 1, 30);
	insert_data(head, 1, 40);
	show_link_list(head);
	
	for(int i=1;i<10;i++) insert_data(head2,0,i);
	
	show_link_list(head2);
	connect_link_list(head,head2);
	show_link_list(head);
	show_link_list(head2);
	
	link_list_Reverse(head);
	show_link_list(head);
	
	del_data(head, 1);
	show_link_list(head);
	del_data(head, 2);
	show_link_list(head);
	del_data(head, 2);

	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值