C语言系列(20)——结构体(03)

一、什么是链表

将内存中若干个地址空间,用指针连起来。

链表:

是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

二、单向链表

链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域
这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值
单向链表
信息域:存储。指针域:存储下个节点的首地址

  • 单向链表实现
  1. 链表节点结构体定义
typedef struct student
{
	char name[10];
	char sex;
	int age;
	//以上为信息域
	//以下为指针域
	struct student * pNext;//指向下一个节点的指针
}Student;
  1. 定义节点
//定义节点 第一个节点
Student *s1 = (Student*)malloc(sizeof(Student));
//节点赋值
strcpy_s(s1->name, sizeof(s1->name), "Binson");
s1->age = 10;
s1->sex = 'm';
//定义节点 第二个节点
Student *s2 = (Student*)malloc(sizeof(Student));
//节点赋值
strcpy_s(s2->name, sizeof(s2->name), "name2");
s2->age = 12;
s2->sex = 'f';
//定义节点 第三个节点
Student *s3 = (Student*)malloc(sizeof(Student));
//节点赋值
strcpy_s(s3->name, sizeof(s3->name), "name3");
s3->age = 20;
s3->sex = 'f';
  1. 链接节点
//将各节点连起来,形成链表
//定义一个头节点,头节点永远指向头。
Student* pHead = s1;
Student* pLast = NULL;//尾
Student* p = pHead;
//后面用各节点内部的指针,指向下一个节点。
s1->pNext = s2;// 第一个节点的指针域,指向第二个节点
s2->pNext = s3;// 第二个节点的指针域,指向第三个节点
s3->pNext = NULL;//最后一个节点的指针,指向NULL。

当然,后面还可以继续链接更多的节点。
注意: 永远不要丢失头指针(pHead),这样,我们就通过pHead,找到后面所有的节点。

  • 例子: 实现一个10个学生节点的链表,并输出其值。(基于上述代码基础上实现)
Student* pHead = NULL;//首
Student* pLast = NULL;//尾
int i = 0;
int size = 10;
for ( i = 0; i < size; i++)
{
	Student* p = (Student*)malloc(sizeof(Student));
	p->pNext = NULL;
	p->age = i + 18;//赋个值,打印的时候确保其唯一性
	if (i == 0)//第一个节点,用pHead指向
	{
	    pHead = p;
	    pLast = p;//只有一个节点,首尾为同一个
	}
	else
	{
		//不是第一个节点,就把其增加到后面
		pLast->pNext = p;
		pLast = p;//再把尾指针移动最后一个
	}
}
//通过 pHead 输出
Student* p = pHead;//不要改变pHead的指向,使用临时指针进行遍历
while (p != NULL)
{
	printf("%d\n", p->age);
	p = p->pNext;//依次往后移动指针,循环变量的改变
}
//释放内存空间,这一步很重要,不使用了就要释放,不然就造成了内存泄漏
p = pHead;
while (p != NULL)
{
	//需要先用个临时指针指向后一个,不然释放了之后,通过pNext找不到后一个了
	Student* tmp = p->pNext;         
	free(p);
	p = tmp;//依次往后移动指针
}
  • 例子: 插入一个新节点:输入其年龄,实现在一个10个学生节点的链表中(按年级有序链表),将这个新节点插入进链表中,使其仍然有序。
//单链表前增加
Student* newStu = (Student*)malloc(sizeof(Student));
printf("请输入年龄:");
scanf_s("%d", &newStu->age);
Student* p = pHead;
Student* perP = pHead;
while (p != NULL)
{
	if (p->age >= newStu->age )//表示要将newStu插入到p的前面
	{
	     //判断p是否是pHead
	     if (p == pHead)
	     {
	          newStu->pNext = p;
	          pHead = newStu;
	          break;
	     }
	     else
	     {
	          newStu->pNext = p;
	          perP->pNext = newStu;
	          break;
	     }
	}
	else if(p == pLast)//没找到比age大的,则增加到最后一个
	{
	     pLast->pNext = newStu;
	     newStu->pNext = NULL;
	     pLast = newStu;
	     break;
	}
	perP = p;//单向链表,在前面增加元素,需要一个前面的节点指针
	p = p->pNext;//依次往后移动指针
}
  • 例子: 实现在一个10个学生节点的链表中,删除与指定年龄一致的节点。
printf("输入年龄:");
scanf_s("%d", &age);
Student* p = pHead;
Student* perP = p;
while (p != NULL)
{
    if (age == p->age)
    {
         if (p == pHead)//删除头节点
         {
              pHead = p->pNext;
              free(p);//释放
              p = pHead;//重新指向头指针,再遍历删除
              continue;
         }
         else if (p == pLast)//删除尾节点
         {
              perP->pNext = NULL;
              pLast = perP;
              free(p);
              break;
         }
         else//删除中间节点
         {
              perP->pNext = p->pNext;
              free(p);
              p = perP;
         }
    }
	perP = p;
	p = p->pNext;//依次往后移动指针
}

以上基本实现,单链表的遍历、增、删

三、双向链表

双向链表: 在节点的结构体定义中,有两个指针,一个指向前一个节点、一个指向后一个节点
双向链表对比单向链表,双向链表可以通过当前节点找到上一个节点和下一个节点,而单向链表只能找到下一个节点。
双向链表

  • 双向链表结构体定义,如:
typedef struct student
{
     char name[10];
     char sex;
     int age;
     struct student * pPre;//指向上一个节点的指针
     struct student * pNext;//指向下一个节点的指针
}Student;
  • 例子: 实现一个10个学生节点的双向链表,并输出其值。
Student* pHead = NULL;//首
Student* pLast = NULL;//尾
int i = 0;
int size = 10;
for (i = 0; i < size; i++)
{
    Student* p = (Student*)malloc(sizeof(Student));
    p->pNext = NULL;
    p->pPre = NULL;
    p->age = i + 18;//随机赋个值,打印的时候确保其唯一性
    if (i == 0)//第一个节点,用pHead指向
    {
         pHead = p;
         pLast = p;//只有一个节点,首尾为同一个
    }
    else
    {
         //不是第一个节点,就把其增加到后面
         pLast->pNext = p;
         p->pPre = pLast;//把新增节点的前指针,指向前一个。
         pLast = p;//再把尾指针移动最后一个
    }
}
//通过 pHead 输出
Student* p = pHead;//不要改变pHead的指向
while (p != NULL)
{
    printf("%d\n", p->age);
    p = p->pNext;//依次往后移动指针
}
//释放内存空间
p = pHead;
while (p != NULL)
{
    //需要先用个临时指针指向后一个,不然释放了之后,通过pNext找不到后一个了
    Student* tmp = p->pNext;
    free(p);
    p = tmp;//依次往后移动指针
}

例子: 双向链表插入节点:输入其年龄,实现在一个10个学生节点的链表中(按年级有序链表),将这个新节点插入进链表中,使其仍然有序。

Student* newStu = (Student*)malloc(sizeof(Student));
printf("请输入年龄:");
scanf_s("%d", &newStu->age);
Student* p = pHead;
//这两句很关键,默认per和next指向空
newStu->pNext = NULL;
newStu->pPre = NULL;
while (p != NULL)
{
    if (p->age >= newStu->age)//表示要将newStu插入到p的前面
    {
         //判断p是否是pHead。插入到最前面
         if (p == pHead)
         {
              p->pPre = newStu;//头指针的per指向 新节点
              newStu->pNext = p;//新节点的next指向 原来的头
              pHead = newStu;//头指针指向new节点,重新把头指针指向第一个。
         }
         else
         {
              newStu->pPre = p->pPre;//新节点的pre指向前一个节点
              newStu->pNext = p;//新节点的next指向当前节点
              p->pPre->pNext = newStu;//前一个节点的next指向新节点
              p->pPre = newStu;//单前节点的pre指向新节点
         }
		 Break;
    }
    else if (p == pLast)//没找到比age大的,则增加到最后一个
    {
         pLast->pNext = newStu;
         newStu->pPre = pLast;
         pLast = newStu;
         break;
    }
    p = p->pNext;//依次往后移动指针
}
  • 例子: 双向链表节点删除:实现在一个10个学生节点的链表中,删除与指定年龄一致的节点。
int age;
printf("输入年龄:");
scanf_s("%d", &age);
Student* p = pHead;
Student* tmp = NULL;
while (p != NULL)
{
    if (age == p->age)
    {
         if (p == pHead)//删除头节点
         {
              pHead = p->pNext;
              pHead != NULL ? pHead->pPre = NULL : NULL;
              free(p);//释放
              p = pHead;//重新指向头指针,再遍历删除
              continue;
         }
         else if (p == pLast)
         {
              p->pPre->pNext = NULL;
              free(p);
              break;
         }
         else
         {
              p->pNext->pPre = p->pPre;//当前节点的下一个节点的pre指向当前节点的上一个节点
              p->pPre->pNext = p->pNext;//当前节点的上一个节点的next指向当前节点的下一个节点
              tmp = p;//临时指针指向p,用于释放
              p = p->pPre;//p指向上一个节点,继续下次循环
              free(p);
         }
    }
    p = p->pNext;//依次往后移动指针
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值