链表
链表用于解决合理利用存储空间的问题
malloc在没有连续内存空间的时候分配会失败
-
解决方案:
- 不要一次性开辟一块连续的存储空间, 每次少开辟一点
最后利用指针将所有开辟的小的存储空间链接在一起, 组成一个整体
- 不要一次性开辟一块连续的存储空间, 每次少开辟一点
静态链表
typedef struct node{
int data; // 专门用于存储数据的
struct node *next; // 专门用于实现链接的
} Node;
int main()
{
// 1.定义三个结构体
Node a;
Node b;
Node c;
// 2.让三个结构体保存数据
a.data = 1;
b.data = 3;
c.data = 5;
// 3.将三个结构体链接在一起
a.next = &b;
b.next = &c;
// 如果指针没有值, 那么就可以赋值为NULL, 明确告诉系统该指针没有值
// 如果一个指针没有值,也没有赋值为NULL, 那么这个指针就是一个野指针
// 注意: 一定不要操作没有值的指针和野指针
c.next = NULL;
// 4.定义链表的头指针
Node *head = &a;
return 0;
}
空链表
- 只有一个头指针和头结点,并且头结点没有下一个结点且没有数据
#include <studio.h>
Node * createNode();
typedef struct node{
int data;
struct node *next;
} Node;
int main()
{
Node *head = createNode();
return 0;
}
Node *createNode()
{
//1.创建一个头结点
Node *head= (Node *)malloc(sizeof(Node));
//1.1可能分配失败
if(head == NULL)
{
exit(-1);
}
//2.下一个节点赋值为空
head -> next = NULL;
return head;
}
-
内存分配图解:
动态链表尾差法
- 规则:
1.把新结点的下一个结点指向头结点的下一个结点
2.把头结点的下一个结点指向新结点
代码如下:
#include <stdio.h>
typedef struct node
{
int data;
struct node *next;
} Node;
void printList(Node * head);
Node *createList();
int main()
{
//0.创建头结点和头指针
Node *head = createList();
//1.打印数据
printList(head);
return 0;
}
void printList(Node * head){
//1.接收头指针,头指针指向头结点
//2.头结点是没有数据的,我们可以将头指针直接指向头结点的下一个结点
//输出数据
Node *cur = head;
while(cur->next != NULL){
cur = cur -> next;
printf("%d\n",cur -> data);
}
/**
* @brief createList 尾差法创建动态链表
* @return 返回头指针
*/
Node *createList(){
//1.创建头指针和头结点
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
exit(-1);
}
head -> next = NULL;
//1.用一个变量表示循环条件
//2.提示用户输入要保存的数据
int num = 0;
printf("请输入想要保存的数据,输入-1结束\n");
scanf("%d",&num);
while(-1 != num)
{
Node *node = (Node*)malloc(sizeof(Node));
//保存数据
node -> data = num;
//1.把新结点的下一个结点指向头结点的下一个结点
node -> next = head -> next;
//2.把头结点的下一个结点指向新结点
head -> next = node;
printf("请输入想要保存的数据,输入-1结束\n");
scanf("%d",&num);
}
return head;
}
-
图解
动态链表头插法
- 规则:
1.定义变量记录最后一个结点
2.让新结点成为上一个结点的下一个结点
3.把新结点作为下一个结点的上一个结点
#include <stdio.h>
typedef struct node
{
int data;
struct node *next;
} Node;
Node *createList();
void printList(Node *head);
int main()
{
//动态链表头插法
Node *head = createList();
printList(head);
return 0;
}
void printList(Node *head){
Node *cur = head;
while(cur->next != NULL)//首先判断不是空链表
{
cur = cur -> next;
printf("%d\n",cur -> data);
}
}
Node *createList(){
//1.创建头结点和头指针
Node *head = (Node*)malloc(sizeof(Node));
//分配失败
if(head == NULL){
exit(-1);
}
head->next = NULL;
//定义变量控制循环
int num = -1;
printf("请输入你想要保存的数据:\n");
scanf("%d",&num);
//1.定义变量记录最后一个结点
Node *cur = head;//一定要放在最外面,否则会引起值覆盖
while(-1 != num)
{
Node *node = (Node*)malloc(sizeof(Node));
node->data = num;
node->next = NULL;
//2.让新结点的上一个结点的下一个结点指向新结点
cur->next = node;
//3.把新结点作为下一个结点的上一个结点
cur = node;
printf("请输入你想要保存的数据:\n");
scanf("%d",&num);
}
return head;
}
-
图解
封装
- 我们可以封装一个创建空链表的函数
/**
* @brief createEmpty 创建空链表
* @return 链表的头指针
*/
Node *createEmpty(){
// 1.定义头指针
Node *head = NULL;
// 2.创建一个空节点, 并且赋值给头指针
head = (Node *)malloc(sizeof(Node));
head->next = NULL;
// 3.返回头指针
return head;
}
- 我们可以将插入数据封装成一个函数
void insertNode(Node *head, int num){
// 1.创建一个新的节点
Node *node = (Node *)malloc(sizeof(Node));
// 2.将数据保存到新节点中
node->data = num;
// 3.进行插入
// 3.1让新节点的下一个节点 等于 头节点的下一个节点
node->next = head->next;
// 3.2让头结点的下一个节点 等于 新节点
head->next = node;
}
- 我们可以封装一个销毁链表的函数
// 封装一个专门用于销毁链表的函数
/**
* @brief destroyList 销毁链表
* @param head 链表的头指针
*/
void destroyList(Node *head){
Node *cur = NULL;
while(head != NULL){
cur = head->next;
free(head);
head = cur;
}
}
- 我们可以封装一个计算链表长度的函数
// 封装一个专门用于计算链表长度的函数
int listLength(Node *head){
// 1.定义变量记录节点的个数
int count = 0;
// 注意点: 头结点不要
Node *cur = head->next;
// 2.遍历统计节点个数
while(cur != NULL){
count++;
cur = cur->next;
}
return count;
}
- 我们可以封装一个查找指定结点的函数
// 封装一个专门用于查找指定节点的函数
/**
* @brief findNode 查找指定节点
* @param head 链表的头指针
* @param key 需要查找的key
* @return 符合要求的节点, 如果找不到返回NULL
*/
Node *findNode(Node* head, int key){
// 注意点: 头结点不需要查找
head = head->next;
while(head != NULL){
// 判断当前节点保存的值是否是要查找的值
if(head->data == key){
return head;
}else{
head = head->next;
}
}
return NULL;
}
- 我们可以封装一个删除指定结点的函数
//封装一个专门用于删除指定节点的函数
/**
* @brief deleteNode 删除指定节点
* @param head 链表的头指针
* @param node 需要删除的节点
*/
void deleteNode(Node *head, Node *node){
// 1.找到需要删除节点的上一个节点
while(head->next != node){
head = head->next;
}
// 2.将删除节点上一个节点的next改为 , 删除节点的下一个节点
head->next = node->next;
// 3.删除对应节点
free(node);
}