补充知识:
结构体中嵌套结构体:
1.结构体中可以嵌套另一个结构体类型的数据
⒉结构体中可以嵌套指向自身的指针和指向另一个结构体的指针
3.结构体中不能嵌套自己类型的数据(无穷递归,确定不了数据类型的内存大小,分配不了内存)
链表:
链表是一种物理存储单元上非连续的存储结构,由一系列(链表中每一个元素称为结点)组成,结点可以在运行时动态生成,结点与结点之间通过指针链接。每个结点包括两部分: 一部分是存储数据元素的数据域,另一部分是存储下一个结点地址的指针域
1). 链表是一种常用的数据结构,它通过指针将一系列数据结点,连接成一个数据链。相对于数组,链表具有更好的动态性
2).数据域用来存储数据,指针域用来建立与下一个结点的联系
3).建立链表时无需预先知道数据总量,可以随机的分配空间,可以高效的在链表中的任意位置实时插入或删除数据
带头单链表:
静态带头单链表
缺点:固定结点个数
结点的生命周期
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef struct Teacher
{
int data;
Teacher *next;
}Teacher;
#include "Lead_Single_link_list.h"
int main()
{
Teacher *head,t1,t2,t3,t4,t5;
head = NULL;
t1.data = 1;
t2.data = 2;
t3.data = 3;
t4.data = 4;
t5.data = 5;
head = &t1;
t1.next = &t2;
t2.next = &t3;
t3.next = &t4;
t4.next = &t5;
t5.next = NULL;
while (head != NULL)
{
printf("%d ", head->data);
head = head->next; //指针下移
}
system("pause");
return 0;
}
动态带头单链表
1. 不停的malloc新节点
2. 新节点入链表
3. 让新节点变成当前节点
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef struct Node
{
int data;
Node *next;
}SList;
typedef struct BuyNode
{
int data;
Node *next;
}BuyNode;
SList* SList_Create(); //创建链表
void SList_Print(SList*head); //打印链表
void SList_NodeInsert(SList*head, int x, int y); //插入 在x前插入y
void SList_NodeDel(SList*head, int y); //删除 y
void SList_Destory(SList*head); //销毁链表
#include "Lead_Single_link_list.h"
SList* SList_Create()
{
SList*head = (SList*)malloc(sizeof(BuyNode));
assert(head != NULL);
//头结点初始化
head->data = 0;
head->next = NULL;
SList*pCur = NULL; //指向当前结点
SList*pM = NULL; //指向新购买的结点
pCur = head;
int data = 0;
printf("请输入结点的数据!\n");
scanf("%d", &data);
while (data != -1)
{
pM = (SList*)malloc(sizeof(BuyNode));
assert(pM != NULL);
//购买的结点初始化
pM->data = data;
pM->next = NULL;
//入链表
pCur->next = pM;
//新结点成为当前结点
pCur = pM;
printf("继续添加结点?\n");
scanf("%d", &data);
}
return head;
}
void SList_Print(SList *head)
{
assert(head != NULL);
SList*tmp = head->next;
while ( tmp!= NULL)
{
printf("%d ", tmp->data);
tmp = tmp->next;
}
printf("\n");
}
void SList_NodeInsert(SList *head, int x, int y)
{
assert(head != NULL);
SList *pPre = head; //前驱指针
SList *pCur = head->next; //当前指针
SList *pM = NULL; //新结点指针
pM = (SList*)malloc(sizeof(SList));
assert(pM != NULL);
//新节点初始化
pM->data = y;
pM->next = NULL;
//寻找新结点插入链表的位置
while (pCur != NULL)
{
if (pCur->data == x)
{
break;
}
pPre = pCur;
pCur = pCur->next;
}
//新节点插入链表
//如果没找到插入位置,结点则会添加到链表的末尾
pM->next = pPre->next; //新节点先链接后继
pPre->next = pM; //新节点后链接前驱
}
void SList_NodeDel(SList*head, int y)
{
assert(head != NULL);
SList *pPer = head;
SList *pCur = head->next;
while (pCur != NULL)
{
if (pCur->data == y)
{
break;
}
pPer = pCur;
pCur = pCur->next;
}
if (pCur == NULL)
{
printf("没有找到结点值为: %d 的结点\n", y);
return;
}
else
{
pPer->next = pCur->next;
free(pCur);
pCur = NULL;
}
}
void SList_Destory(SList*head)
{
SList*tmp = NULL;
while (head != NULL)
{
tmp = head->next;
free(head);
head = tmp;
}
}
int main()
{
SList*head = SList_Create();
SList_Print(head);
SList_NodeInsert(head, 12, 11);
SList_Print(head);
SList_NodeDel(head, 11);
SList_Print(head);
SList_Destory(head);
system("pause");
return 0;
}
借助三个辅助指针来实现
图解:
pM指针对新购买的结点进行初始化,初始化完成后将结点的地址给pCur指向的当前结点的next指针和pCur的本身,使得*next和pCur都能指向新节点,新节点完成入链表和成为当前结点的两个动作
插入新节点
未找到则会插入链表末尾
插入链表的三种情况
删除某个结点:
单链表逆置
void SList_Inversion(SList*head)
{
//链表为空,只有一个结点,只有两个结点都不要逆置
assert(head != NULL || head->next != NULL || head->next->next != NULL);
//初始化
SList*p = head->next;
SList*q = p->next;
SList*t = NULL;
//一个结点一个结点的逆置
while (q != NULL)
{
t = q->next; //缓存后面的结点
q->next = p; //逆置
p = q; //让p移动到下一个结点
q = t; //让q移动到下一个结点
}
head->next->next = NULL;
head->next = p;
}