链表的实现(C语言)
说明
1、该链表为较为原始的链表,如果能搞懂这个其他链表也能理解。
2、链表的难点为链表的创建、插入、删除、排序。
头文件函数的申明以及主函数
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef struct Node
{
int data;//数据域
struct Node *pNext;//指针域
}NODE,*PNODE;
PNODE create_list();//创建链表
void traverse_list(PNODE pHead);//遍历链表(输出链表的元素)
bool is_empty(PNODE pHead);//判断链表是否为空
int length_list(PNODE pHead);//计算链表的长度
bool delete_list(PNODE pHead,int,int*);//删除链表元素
bool insert_list(PNODE pHead,int,int);//插入元素
void sort_list(PNODE pHead);//排序
int main()
{
PNODE pHead=NULL;
int len;
int pVal;
pHead=create_list();//将create_list()函数创建的链表的首地址返回给pHead;
len=length_list(pHead);
printf("链表长度为:%d\n",len);
printf("链表元素为:");
traverse_list(pHead);
printf("插入数据后:");
insert_list(pHead,4,22);
traverse_list(pHead);
delete_list(pHead,3,&pVal);
printf("删除的值为:%d\n",pVal);
printf("删除后的链表为:");
traverse_list(pHead);
sort_list(pHead);
printf("排序后:");
traverse_list(pHead);
return 0;
}
ps:主函数是用来测试链表是否可用的
创建链表
输入的元素都需要分配一个节点储存该元素以及指向下一个节点的指针,因此每次都要动态分配一个类型为我们定义的结构体(定义了数据域和指针域)的内存。创建链表是用尾插法实现的,关键的是要有一个指针一直指向最后一个节点,我们定义了一个pTail。
PNODE create_list()
{
int i,len;
int val; //用于存放用户输入的值
PNODE pHead=(PNODE)(malloc(sizeof(NODE)));//分配的头结点只有指针域,没有数据域
if(pHead==NULL)
{
printf("内存分配失败!\n");
exit(-1);
}
//PNODE pTail(PNODE)(malloc(sizeof(NODR)));不需要为PTail分配内存
PNODE pTail=pHead;//创建一个指针使其一直指向最后一个节点,pTail一直指向的都是最后一个节点(尾插法)
pTail->pNext=NULL;//清空头结点
printf("请输入链表的节点个数:");
scanf("%d",&len);
for(i=0;i<len;i++)
{
printf("请输入第%d个节点的数据:",i+1);
scanf("%d",&val);
PNODE pNew=(PNODE)(malloc(sizeof(NODE)));//每输入一个数据,就为其分配一个内存存放那个数据和指向下一个节点的指针
if(pNew==NULL)
{
printf("内存分配失败!\n");
exit(-1);
}
pNew->data=val;
pTail->pNext=pNew;
pNew->pNext=NULL;
pTail=pNew;
}
return pHead;//返回头结点,也就是链表的首地址
}
输出链表、判断链表是否为空以及计算链表的长度
输出链表和计算链表的长度的关键就是要对链表进行遍历也就是p=p->pNext,通过结构体所定义的指针域。
void traverse_list(PNODE pHead)
{
PNODE p=pHead->pNext;
while(p!=NULL)
{
printf("%d\t",p->data);
p=p->pNext;
}
printf("\n");
}
bool is_empty(PNODE pHead)
{
if(pHead->pNext==NULL)
return true;
else
return false;
}
int length_list(PNODE pHead)
{
PNODE p=pHead->pNext;
int len=0;
while(p!=NULL)
{
len++;
p=p->pNext;
}
return len;
}
排序
链表排序广义上和数组的排序是一样的,我们可以先写出数组的排序,通过适当的修改,就变成了链表的排序。
void sort_list(PNODE pHead)//链表排序和数组排序在广义上是相同的,可以通过写出数组的排序,然后进行修改
{
int len=length_list(pHead);//得到链表的长度
int i,j,t;
PNODE p,q;
for(i=0,p=pHead->pNext;i<len-1;i++,p=p->pNext)//通过i和j来判断交换的趟数
{
for(j=i+1,q=p->pNext;j<len;j++,q=q->pNext)
{
if(p->data>q->data)
{
t=q->data; //t=a[j];
q->data=p->data; //a[j]=a[i];
p->data=t; //a[i]
}
}
}
}
插入和删除
插入和删除关键就是找到我们要插入元素的前面一个数,当然也可以是后面的,但因为该链表是单链表,找前面一个元素更好操作。我们是通过“拆链子”的方法,也就是改变指针的指向的方法。
bool insert_list(PNODE pHead,int pos,int val)
{
int i=0;
PNODE p=pHead;
while(p!=NULL&&i<pos-1)//遍历链表,是p指向的是pos前面一个节点
{
p=p->pNext;
i++;
}
if(i>pos-1 || p==NULL)//可以避免pos<1的时候以及pos>len+1
return false;
PNODE pNew=(PNODE)(malloc(sizeof(NODE)));
if(pNew==NULL)
{
printf("内存分配失败!\n");
}
pNew->data=val;
PNODE q=p->pNext;
p->pNext=pNew;
pNew->pNext=q;
return true;
}
bool delete_list(PNODE pHead,int pos,int *pVal)
{
int i=0;
PNODE p=pHead;
while(p->pNext!=NULL && i<pos-1)
{
p=p->pNext;
i++;
}
if(i>pos-1 || p->pNext==NULL)//可以避免pos<1的时候以及pos>len删除和插入不一样,删除不能在没有节点的地方删除
return false;
PNODE q=p->pNext;
*pVal=q->data;
p->pNext=q->pNext;
free(q);
q=NULL;
return true;
}
测试结果
通过测试可以发现链表的功能都能实现。链表的实现过程我觉得最难得就是排序、删除还有插入,这需要我们出数值的思想转换到链表的思想,尤其是对指针要有一定的理解。