什么是链表
链表是一种链式存取得数据结构,它与数组类似,但是单链表堆内存的运用更加方便,可以在中间添加或者删除一个或者多个元素,不需要向数组那样移动大量元素。链表分为很多种,单链表,双向链表,循环链表。
单链表的建立与初始化
单链表就是一种链式存储的结构,链表中的数据是以结点来表示的,相当于是一个个节点相互连接在一起。
结点:分为两个部分,数据域与指针域,用结构体的形式来表示。如下:
typedef struct Node //定义结点结构体
{
int date; //数据域
struct Node *next; //指针域
}node; //结点名
我们这里需要定义一个结构体指针,来指向这些结点的头结点,我们将这个指针称之为头指针。我们可以运用头指针来寻找到整个链表。我们需要用malloc函数来建立一个头结点,并对其进行初始化。我在结构体的那篇博客里详细介绍了这个函数,这里就不做多解释了。
头结点的建立与初始化如下:
void Init(node *&n)//*&n的意思是直接引用在main函数中定义的n,不用在函数内再定义
{
n = (node *)malloc(sizeof(node));//定义结构体指针变量,并为其分配一块内存
n ->next = NULL;//将指针域赋为空,防止发生内存泄漏
}
int main()
{
node *n ;//定义一个结构体指针变量
Init(n);//初始化
system ("pause");
return 0;
}
增加结点
我们在单链表中一般运用头插法与尾插法的方法来增加一个结点,来达到建立一个链表的目的。
头插法
头插法就是一直将新建立的结点插入到当前链表的头结点之后。
void front(node *&head) //头插法
{
node *n; //定义另一个指针变量
int m,i;
for(i=0;i<5;i++)
{
n = (node *)malloc(sizeof(node));//让其指向一块新建的内存
scanf("%d",&m); //输入一个数
n->date = m; //给他的数据域赋值
n->next = head->next; //让这个指针指向head所指向的下一个结点
head -> next = n; //连接头结点和新建节点
}
}
如下图所示,帮助理解
如上图所示,循环往复,就依次将结点插入到了链表中,由于是头插法,所以输出的结果也是倒叙输出的
尾插法
尾插法就是一直将新建结点插入到链表的表尾处,在开始时链表中的头结点就是为结点。此后每次插入的新节点就又成为链表的尾结点。
代码如下
void tail(node *&head) //尾插法
{
int m,i;
node *p1,*p2; //定义另一个指针变量
p1 = head; //让p1也指向头结点,代替头节点完成下面的程序
//要确保头指针head不能改变
for(i=0;i<5;i++)
{
p2 = (node *)malloc(sizeof(node));//让p2指向一块新建的内存
scanf("%d",&m); //输入一个数
p2->date = m; //给他的数据域赋值
p2->next = p1->next; //让p2指针指向p1所指向的下一个结点
p1 -> next = p2; //连接尾结点和新建节点
p1 = p2; //让p1也指向新建节点,此时新建节点就是链表中的尾结点
}
}
如下图所示,帮助理解
创立完成后可以运用循环语句让将结点中的数据输出出来,又称为链表的遍历,代码如下
void print(node *&head) //输出数据
{
node *n;
n = head->next; //跳过头结点输出后面的结点
while(n) //直到n指向空就停止
{
printf("%d ",n->date); //输出结点中的数据
n = n->next; //让n指向下一个结点
}
printf("\n");
}
然后再在main函数中调用这些子函数即可
int main()
{
node *head ; //定义头结点
Init(head); //初始化链表
front(head); //头插法
tail(head); //尾插法
print(head); //输出结点中的数据
system ("pause");
return 0;
}
比较头插法与尾插法可知
- 头插法较尾插法简单,但是输出的结果与输入的相反。
- 尾插法虽然复杂所运用的指针较多,但是输出的结果与输入的一致。
删除节点
建立完链表后,我们如果想要删除链表中的某一数据,就相当于是删除这个数据所在的结点。我们建立链表时一般不会为头结点赋值,所以不用考虑头结点的情况。
代码如下
void Delete(node *&head)
{
int m;
node *n1,*n2; //定义两个指针变量
n1 = n2 = head; //让他们同时指向头结点
printf("\n请输入想要删出的数据\n");
scanf("%d",&m);
while(n1->date != m) //通过while循环遍历整个链表
{
n2=n1; //让n2也指向n1所指向的结点
n1=n1->next; //让n1遍历整个链表
if(n1==NULL) //如果找到表尾还没找到就条纯循环
{
break;
}
}
if(n1==NULL) //如果n1等于空,就说明没找到该数据
{
printf("\n没有%d这个数据\n\n",m);
}
else
{
n2->next = n1->next; //此时n1在n2的前一个结点处,让n2指向
//n1所指向的下一个结点,就相当于将n1哪个结点跳过了
free(n1); //然后再释放掉n1所指向的那块内存
n1=NULL; //再让n1指向空,避免其成为悬空指针
}
}
如下图所示,帮助理解
修改结点数据
先遍历结点,然后找到该数据所在的结点,然后再修改结点的数据。
代码如下
void Alter(node *&head) //修改数据
{
int m,n;
node *p; //定义结点指针
p = head; //让p指向头结点
printf("\n请输入想要修改的数据\n");
scanf("%d",&m);
while(p->date != m) //通过while循环找到那个数据
{
p=p->next; //让p遍历整个链表
if(p==NULL) //如果找到表尾还没找到就条纯循环
{
break;
}
}
if(p==NULL) //如果p等于空,就说明没找到该数据
{
printf("\n没有%d这个数据\n\n",m);
}
else
{
printf("请输入新的数据\n");
scanf("%d",&n);
p->date = n; //替换该结点的数据
}
}
查找某一个数据
也是通过遍历,找到该数据,再输出出来
代码如下
void Find(node *&head) //查找数据
{
int m,n;
node *p; //定义结点指针
p = head; //让p指向头结点
printf("\n请输入想要查找的数据\n");
scanf("%d",&m);
while(p->date != m) //通过while循环找到那个数据
{
p=p->next; //让p遍历整个链表
if(p==NULL) //如果找到表尾还没找到就条纯循环
{
break;
}
}
if(p==NULL) //如果p等于空,就说明没找到该数据
{
printf("\n没有%d这个数据\n\n",m);
}
else
{
printf("%d\n",p->date); //输出该数据
}
}
至此,我们完成了对单链表的一些基本简单操作。