过去对于链表仅限于理论上的了解,但是对于其具体的实现没有深入学习,最近参加笔试遇到了挺多次,所以好好系统的复习下。
一、基本定义
链表是一种常见的数据结构,在内存中的存储是不连续的,常用malloc等函数动态随机分配空间,用指针相连。
链表用结构体来定义,指针部分定义为指向本结构体类型的指针类型。
每个元素包含两个部分:数据部分和指针部分。数据部分用来存放元素所包含的数据,指针部分用来指向下一个元素。最后一个元素的指针指向null,表示指向的地址为空。
二、单链表
每个结点只有一个指针指向下一个结点,末尾节点指针为空。
单链表的建立及遍历:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
/*单向链表*/
struct Student/*建立学生信息结构体模型*/
{
char cName[20];/*学生姓名*/
int iNumber;/*学生学号*/
struct student *next;/*指向本结构体类型的指针类型*/
};
int iCount;/*全局变量表示链表长度*/
struct Student *Create();/*创建链表函数声明*/
void print(struct Student *);/*遍历输出链表函数声明*/
int main()
{
int insert_n=2;/*定义并初始化要插入的结点号*/
int delete_n=2;/*定义并初始化要删除的结点号*/
struct Student *pHead;/*声明一个指向学生信息结构体的指针作pHead为头结点传递*/
pHead=Create();/*创建链表,返回链表的头指针给pHead*/
print(pHead);/*将指针pHead传入输出函数遍历输出*/
return 0;
}
struct Student *Create()
{
struct Student *pHead=NULL;/*初始化链表,头指针为空*/
struct Student *pEnd,*pNew;
iCount=0;/*初始化链表长度*/
pEnd=pNew=(struct Student *)malloc(sizeof(struct Student));/*动态开辟一个学生信息结构体类型大小的空间,使得pEnd和pNew同时指向该结构体空间*/
scanf("%s",pNew->cName);/*从输入流获取第一个学生姓名*/
scanf("%d",&pNew->iNumber);/*从输入流获取第一个学生学号*/
while(pNew->iNumber!=0)/*设定循环结束条件——学号不为0时*/
{
iCount++;/*链表长度+1,即学生信息个数+1*/
if(iCount==1)/*如果链表长度刚刚加为1,执行*/
{
pNew->next=pHead;/*使指针指向为空*/
pEnd=pNew;/*跟踪新加入的结点*/
pHead=pNew;/*头结点指向首结点*/
}
else/*如果链表已经建立,长度大于等于2时,执行*/
{
pNew->next=NULL;/*新结点的指针为空*/
pEnd->next=pNew;/*原来的结点指向新结点*/
pEnd=pNew;/*pEnd指向新结点*/
}
pNew=(struct Student *)malloc(sizeof(struct Student));/*再次分配结点的内存空间*/
scanf("%s",pNew->cName);/*从输入流获取第一个学生姓名*/
scanf("%d",&pNew->iNumber);/*从输入流获取第一个学生学号*/
}
free(pNew);/*释放结点空间*/
return pHead;/*返回创建出的头指针*/
}
void print(struct Student *pHead)
{
struct Student *pTemp;/*定义指向一个学生信息结构体类型的临时指针*/
int iIndex=1;/*定义并出事哈变量iIndex,用来标识第几个学生(信息)*/
printf("总共%d个学生(信息):\n",iCount);
pTemp=pHead;/*指针得到首结点的地址*/
while(pTemp!=NULL)/*当临时指针不指向NULL时*/
{
printf("第%d个学生信息:\n",iIndex);
printf("姓名:%s",pTemp->cName); /*输出姓名*/
printf("学号:%d",pTemp->iNumber);/*输出学号*/
pTemp=pTemp->next;/*移动临时指针到下一个结点*/
iIndex++;/*进行自加运算*/
}
}
主函数增加:
int main()
{
int insert_n=2;/*定义并初始化要插入的结点号*/
int delete_n=2;/*定义并初始化要删除的结点号*/
struct Student *pHead;/*声明一个指向学生信息结构体的指针作pHead为头结点传递*/
pHead=Create();/*创建链表,返回链表的头指针给pHead*/
pHead=Insert(pHead,insert_n);/*将指针pHead和要插入的结点数传递给插入函数*/
print(pHead);/*将指针pHead传入输出函数遍历输出*/
Delete(pHead,delete_n);/*将指针pHead和要删除的结点数传递给删除函数*/
print(pHead);/*将指针pHead传入输出函数遍历输出*/
return 0;
}
插入:
struct Student *Insert(struct Student *pHead,int number)
{
struct Student *p=pHead,*pNew;/*定义pNew指向新分配的空间*/
while(p&&p->iNumber!=number)
p=p->next;/*使临时结点跟踪到要插入的位置(该实例必须存在学号为number的信息,插入其后,否则出错)*/
printf("姓名和学号:\n");
/*分配内存空间,返回该内存空间的地址*/
pNew=(struct Student *)malloc(sizeof(struct Student));
scanf("%s",pNew->cName);
scanf("%d",&pNew->iNumber);
pNew->next=p->next;/*新结点指针指向原来的结点*/
p->next=pNew;/*头指针指向新结点*/
iCount++;/*增加链表结点数量*/
return pHead;/*返回头指针*/
}
删除:
void Delete(struct Student *pHead,int number)
{
int i;
struct Student *pTemp;/*临时指针*/
struct Student *pPre;/*表示要删除结点前的结点*/
pTemp=pHead;/*得到链表的头结点*/
pPre=pTemp;
for(i=0;i<number;i++)
{/*通过for循环使得Temp指向要删除的结点*/
pPre=pTemp;
pTemp=pTemp->next;
}
pPre->next=pTemp->next;/*连接删除结点两边的结点*/
free(pTemp);/*释放要删除结点的内存空间*/
iCount--;/*减少链表中的结点个数*/
}
三、双向链表
双向链表含有一个数据域和两个链域。链域分为存储直接后继结点地址的右链域,和存储直接前驱结点地址的左链域。
建立与遍历:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define N 10
typedef struct Node
{
char name[20];
struct Node *llink,*rlink;
}STUD;
STUD *creat(int);void print(STUD *);
int main()
{
int number;
char studname[20];
STUD *head,*searchpoint;
number=N;
head=creat(number);
print(head);
printf("请输入你要查找的人的姓名:");
scanf("%s",studname);
searchpoint=search(head,studname);
printf("你所要查找的人的姓名是:%s",*&searchpoint->name);
return 0;
}
STUD *creat(int n)
{
STUD *p,*h,*s;
int i;
if((h=(STUD *)malloc(sizeof(STUD)))==NULL)
{
printf("不能分配内存空间");
exit(0);
}
h->name[0]='\0';
h->llink=NULL;
h->rlink=NULL;
p=h;
for(i=0;i<n;i++)
{
if((s=(STUD *)malloc(sizeof(STUD)))==NULL)
{
printf("不能分配内存空间");
exit(0);
}
p->rlink=s;
printf("请输入第%d个人的姓名",i+1);
scanf("%s",s->name);
s->llink=p;
s->rlink=NULL;
p=s;
}
h->llink=s;
p->rlink=h;
return(h);
}
void print(STUD *h)
{
int n;
STUD *p;
p=h->rlink;
printf("数据信息为:\n");
while(p!=h)
{
printf("%s ",&*(p->name));
p=p->rlink;
}
printf("\n");
}
元素查找:
查找函数 STUD *search(STUD *,char *);
STUD *search(STUD *h,char *x)
{
STUD *p;
char *y;
p=h->rlink;
while(p!=h)
{
y=p->name;
if(strcmp(y,x)==0)
return§;
else
p=p->rlink;
}
printf(“没有查到该数据!”);
}
四、循环链表
循环链表的建立不需要专门的头节点,单链表的最后一个结点的指针指向NULL,而循环链表的最后一个结点的指针指向链表头结点。头尾相连形成了一个环形的数据链。
判断循环链表是否为尾结点时,只需要判断该节点的指针域是否指向链表的头结点。
五、合并链表
void merge(struct stud *La,struct stud *Lb)
{
struct stud *a,*b,*c;
c=La;
a=La->next;/* 合并前 */
b=Lb->next;
while(a!=NULL && b!=NULL)/* La和Lb都没处理完 */
{
if(a->num <= b->num)
{
c->next=a;
c=a;
a=a->next;
}
else
{
c->next=b;
c=b;
b=b->next;
}
}
if(a!=NULL)
c->next=a;/* 若La没有处理完 */
else
c->next=b;/* 若Lb没有处理完 */
free(Lb); /* 释放Lb的头结点 */
}
本文整理自:韩亦乐的博客