一、链表
链表就是表的离散存储,逻辑结构表结构,通过指针方式实现逻辑上的链接 。
链表的一个节点包括两个部分:数据域和指针域。
二、链表的定义
链表的创建需要定义其存储的数据以及指向下一个节点的指针。
#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;
}