线性表的链式表示
一:基本概念及定义
在顺序表中,由于逻辑上相邻的元素,其物理位置也是相邻的,因此可以随机存取顺序表的任何一元素,但是插入与删除需要移动大量元素,存储分配必须事先进行分配,事先分配的存储单元大小可能不适合问题的需要。链式的线性表可以解决上述顺序表的缺点。
定义:线性表的链式存储是采用一组任意的存储单元存放线性表的元素,这些存储单元可以是连续的,也可以是不连续的。为了表示每个元素与其直接后继元素的逻辑关系,除了需要存储元素的信息外,还需要存储其后继元素的地址信息。这两部分组成的存储结构,称为结点。结点包括两个域:数据域和指针域。其中数据域存放数据元素的信息,指针域存放元素后继的存储地址。如下图所示。
链式的存取必须从头指针head开始,头指针指向链式的第一个结点,不过有时为了操作方便,会在第一个结点之前增加一个头结点(不过不是必要的),头结点的数据域可以存放线性表的长度等。最后一个结点的指针域为“NULL”。
单链表的存储结构用C语言来描述如下:
typedef struct Node
{
DataType data;
Struct Node *next;
}ListNode,*LinkList;
其中,ListNode是链表的结点类型,LinkList是指向链表结点的指针类型。
LinkList L;则定义一个链表,L指向该链表的第一个结点,对于不带头结点的空链表来说,L= NULL;
二:单链表的基本运算
(1):单链表的初始化操作
//将单链表初始化为空,动态生成一个头结点,并将头结点的指针域置为NULL
void InitList(LinkList *head)
{
if((*head=(LinkList)malloc(sizeof(ListNode)))==NULL) //为头结点分配一个存储空间
exit(-1);
(*head)->next = NULL; //将单链表的头结点指针域为空
}
(2):判断单链表是否为空
//判断单链表是否为空,是的时候返回1,否则返回0
int ListEmpty(LinkList head)
{
if(head->next == NULL) //判断单链表头结点的指针域是否为空
return 1; //单链表为空的时候,返回,否则返回0
else
return 0;
}
(3):按序号查找操作
/查找单链表中第i个结点,查找成功返回该结点的指针,否则返回NULL表示失败
ListNode *Get(LinkList head,int i)
{
ListNode *p;
int j;
if(ListEmpty(head)) //判断是否为空
return NULL;
if(i < 1)
return NULL; //判断i是否合法
j=0;
p = head;
while(p->next != NULL && j<i)
{
p = p->next;
j++;
}
if(j == i)
return p; //找到第i个结点,返回指正p
else
return NULL; //没有找到第i个结点,返回NULL
}
//查找线性表中元素值为e的元素,查找成功将对应元素的结点指针返回,否则返回NULL表示失败。
ListNode *LocateElem(LinkList head,DataType e)
{
ListNode *p;
p = head->next; //指针p指向第一个结点
while(p)
{
if(p->data == e) //找到与e相等的元素,返回该序号
{
return p;
break;
}
else
p = p->next;
}
if(p == NULL) //如果没有找到与e相等的元素,返回NULL
return NULL;
}
(5):定位操作
//查找线性表中元素值为e的元素,查找成功将对应元素的序号返回,否则返回0表示失败。
int LocatePos(LinkList head,DataType e)
{
ListNode *p;
int i;
if(ListEmpty(head)) //在查找第i个元素之前,判断链表是否为空
return 0;
p = head->next; //指针p指向第一个结点
i = 1;
while(p)
{
if(p->data == e) //找到与e相等的元素,返回该序号
return i;
else
{
p = p->next;
i++;
}
}
if(!p) //如果没有找到与e相等的元素,返回0
return 0;
}
(6):插入操作( 仅限在中间结点插入)
//第i个位置插入一个结点,结点的元素值为e。插入成功返回1,失败返回0
int InsertList(LinkList head, int i, DataType e)
{
ListNode *p,*pre; //定义指向第i个元素的前驱结点指针pre,指针p指向新生成的结点
int j;
pre = head; //指针p指向头结点
j = 0;
while(pre->next != NULL && j<i-1) //找到第i-1个结点,即第i个结点的前驱结点
{
pre = pre->next;
j++;
}
if(j!= i-1) //如果没找到,说明插入位置错误
{
printf("插入位置错");
return 0;
}
//新生成一个结点,并将e赋值给该结点的数据域
if((p = (ListNode*)malloc(sizeof(ListNode))) == NULL)
exit(-1);
p->data = e;
p->next = pre->next; //插入新结点
pre->next = p;
return 1;
}
(7):删除操作(仅限在中间结点删除) 其余两种插入和删除见网址:http://blog.youkuaiyun.com/xubin341719/article/details/7091979
//删除单链表中的第i个位置的结点。删除成功返回1,失败返回0
int DeleteList(LinkList head,int i,DataType *e)
{
ListNode *p,*pre;
int j;
pre = head;
j =0;
while(pre->next != NULL && pre->next->next != NULL && j<i-1) //判断是否找到前驱结点
{
pre = pre->next;
j++;
}
if(j!=i-1) //如果没找到要删除的结点位置,说明删除位置错误
{
printf("删除位置错误");
return 0;
}
else
p=pre->next;
*e=p->data;
//将前驱结点的指针域指向要删除结点的下一个结点,也就是将p指向的结点与单链表断开
pre->next=p->next;
free(p); //释放p指向的结点
return 1;
}
(8):求表长操作
//计算链表的长度
int ListLength(LinkList head)
{
ListNode *p;
int count;
count = 0;
p=head;
while(p->next !=NULL)
{
p=p->next;
count++;
}
return count;
}
(9):摧毁链表操作
//清空单链表
void DestroyList(LinkList head)
{
ListNode *p,*q;
p=head;
while(p!=NULL)
{
q=p;
p=p->next;
free(q);
}
}