单链表基础(包含头尾插法)
**一 .单链表的概念 **
1. 单链表的构成
单链表是由若干个结构体构成的,每个结构体都包含数据域和指针域,有前驱结构体的结构体的指针域里都存着下一个结构体的地址,第一个结构体(也叫头节点)没有前驱结构体。
就像下图一样:
2.单链表的操作
先命名一个head结构体,再用malloc函数为它申请一块空间,再利用循环命名所需要的结构体个数并利用malloc 函数申请空间,依次让结构体的指针域指向下一个结构体的所在位置。就像下面的代码:
typedef struct Node{
int num;
struct Node* next;
}node;
node* head,*p; //定义头节点
head=(node*)malloc(sizeof(node));
for(i=0;i<5;i++)
{
p=(node*)malloc(sizeof(node)); //循环创建子结构体
}
3.单链表与数组的区别
数组是一块连续分配的空间,每个数据都是紧密排列的(也就是一个挨一个),而单链表则是任意空间,你根本不清楚每个结构体的具体位置,它们是依靠结构体里的指针域来连接的,就像下图一样
二. 创建链表
首先,一个单链表需要一个头节点(里面的数据域为NULL),我就以要创建一个简单的学生管理来举例,里面数据域包含学生的学号,名字。请看以下代码:
(这个为尾插法,也是比较常用的)
#include<stdio.h>
#include<stdlib.h>
typedef struct Student{
int no;
char name[10];
struct Student* next;
}student;
int mian()
{
student *head,*p,*tail; //tail为尾指针
head=(student*)malloc(sizeof(student));
head->next=NULL;
tail=head;
int no,n;
char name[10];
printf(" 输入学生的个数:");
scanf("%d",&n);
for(i=0;i<n;i++)
{
p=(student*)malloc(sizeof(student));
printf("输入%d个学生的学号:",i+1);
scanf("%d",&no);
p->no=no;
printf("输入%d个学生的 名字:",i+1);
scanf("%s",name);
strcpy(p->name,name)
tail->next=p;
tail=p;
}
以下为头插法
#include<stdio.h>
#include<stdlib.h>
typedef struct Student{
int no;
char name[10];
struct Student* next;
}student;
int mian()
{
student *head,*p;
head=(student*)malloc(sizeof(student));
head->next=NULL;
tail=head;
int no,n;
char name[10];
printf(" 输入学生的个数:");
scanf("%d",&n);
for(i=0;i<n;i++)
{
p=(student*)malloc(sizeof(student));
printf("输入%d个学生的学号:",i+1);
scanf("%d",&no);
p->no=no;
printf("输入%d个学生的 名字:",i+1);
scanf("%s",name);
strcpy(p->name,name);
p->next=head->next;
head->next=p;
}
****** 头插法和尾插法的区别
头插法插入的数据为倒置的,往往与自己想要的顺序相反,
而尾插法与自己输入的顺序相同,所以一般都是选举尾插法。
三.链表的插入
首先,要往链表中插入一个结构体(假设为student p),就要先知道想要插入的位置,然后定义一个student q, 先让student q指到要插入位置的前一个位置
再让student p的指针域指向student q指针域指向的地方,student q的指针域指向p,这样文字描述可能太混乱了,看下面的代码(以下代码为一个子函数,返回的是一个student型的值,并且在主函数里是由head来接受这个值的),假设在主函数里链表已经创建好了,并且在主函数里还输入好了这个学生的信息,有studentp保存着,要插入的位置设为cnt
student* insertlist (student* head,student*p,int cnt)
{
student *q=head;
int count=0;
while(count<cnt-1&&q!=NULL)
{
q=q->next;
count++;
}
p->next=q->next;
q->next=p;
return head;
}
四.链表的删除
- 链表的删除需要两个student* 类型的变量,并且一定是一前一后的关系(先让前面那个变量在头节点那个位置,后面那个变量在头节点下一个位置,并且两个变量一起移动,这样就永远都是一前一后了),在前面的变量是为了方便把链表重新连接起来,而后面的变量是用来指定到要删除的位置的,要实现删除,首先要知道删除的是哪个位置,在学生管理系统中我就用学号作为代表( 在主函数中设为No),同样的,链表的删除也用一个子函数来写,并且返回值也是一个student* 型的值,也同样用head 来连接
student* deletelist(student* head,int No)
{
student *p,*q;
p=head;
q=head->next;
while(q)
{
if(q->next==No)
{
p->next=q->next;
free(q);
}
else
{
p=q;
q=q->next;
}
}
return head;
}
五.链表的遍历
链表的遍历就是把输入的内容全部输出一遍,同样的,也是设为子函数的形式。遍历是从head节点开始遍历的并且不需要返回值,显然这需要用循环来遍历, 并且我们这遍历出来的值是学号(如果想输出其他的值只需要改变输出的变量即可)。先设一个与head变量类型相同的变量,让它指向 head->next指向的地方,并在没一轮循环之后让这个变量指向它指针域指向的地方,也就是它的下一个节点,当这个变量为空时,说明已经遍历完成了,所以我们这里用while循环比较合适
void putlist(student *head)
{
student* q=head->next;
while(q)
{
printf("%d",q->no);
q=q->next;
}
}