数据结构专升本第二周.docx
《数据结构专升本第二周.docx》由会员分享,可在线阅读,更多相关《数据结构专升本第二周.docx(11页珍藏版)》请在冰豆网上搜索。
数据结构专升本第二周
2.3线性表的链式存储结构
一、单链表的基本概念
1、链表(LinkedList):
用链接存储方式存储的线性表。
特点:
用一组任意的存储单元存储线性表的数据元素(这组存储单元可以连续,也可以不连续)。
2、一个链表结点由两个域构成:
①存储数据元素信息的域称为数据域;
②存储直接后继存储位置的域称为指针域。
指针域中存储的信息称为指针(pointer)或链(link)。
3、若链表中每个结点只包含一个指针域,则称此链表为单链表(SingleLinked)。
指向第一个结点的指针被称为头指针(head)。
链表的最后一个结点被称为尾结点(rear),其指针域为空(NULL)。
为了方便操作,在单链表第一个结点前附加一个同结构结点,称为头结点(front)或表头(ListHead)。
4、单链表是一个非随机存取的存储结构。
二、单链表的基本运算
线性表的单链表存储结构
typedefcharDataType;
typedefstructnode/*结点类型定义*/
{DataTypedata;/*结点的数据域*/
structnode*next;/*结点的指针域*/
}ListNode;
typedefListNode*LinkList;
1、建立
头插法建立单链表
LinkListCreateListF(void)
{charch;
LinkListhead;/*头指针*/
ListNode*s;/*工作指针*/
head=NULL;/*链表开始为空*/
ch=getchar();
while(ch!
='\n')
{s=(ListNode*)malloc(sizeof(ListNode));/*生成新结点*/
s->data=ch;
s->next=head;
head=s;
ch=getchar();
}
returnhead;/*返回头指针*/
}
尾插法建立单链表
LinkListCreateListR(void)
{charch;
LinkListhead;/*头指针*/
ListNode*s,*r;/*工作指针*/
head=NULL;/*链表开始为空*/
r=NULL;/*链表尾指针开始为空*/
while((ch=getchar())!
='\n')
{s=(ListNode*)malloc(sizeof(ListNode));/*生成新结点*/
s->data=ch;
if(head==NULL)
head=s;/*新结点插入空表*/
elser->next=s;
r=s;
}
if(r!
=NULL)
r->next=NULL;/*对于非空表,将尾结点指针域置空*/
returnhead;/*返回头指针*/
}
在链表开始之前附加一个头结点的优点:
1)由于开始结点的位置被存放在头结点的指针域中,所以链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理;
2)无论链表是否为空,其头指针是指向头结点的非空指针,因此空表和非空表处理一致。
LinkListCreateListR1(void)
{charch;
LinkListhead=(ListNode*)malloc(sizeof(ListNode));
ListNode*s,*r;
r=head;/*尾指针初值指向头结点*/
while((ch=getchar())!
='\n')
{s=(ListNode*)malloc(sizeof(ListNode));/*生成新结点*/
s->data=ch;
r->next=s;
r=s;
}
r->next=NULL;
returnhead;/*返回头指针*/
}
以上三个算法的时间复杂度均为O(n)
2、查找
LinkListGetNode(LinkListhead,inti)
{/*在带头结点的单链表head中查找第i个结点*/
intj;
ListNode*p;
p=head;j=0;/*从头结点开始扫描*/
while(p->next&&j
{p=p->next;
j++;
}
if(i==j)
returnp;/*找到了第i个结点*/
else
returnNULL;/*找不到满足条件的结点*/
}
LinkListLocateNode(LinkListhead,DataTypekey)
{/*在带头结点的单链表head中查找其值为key的结点*/
ListNode*p=head->next;/*从开始结点比较*/
while(p&&p->data!
=key)/*直到p为NULL或p->data是key为止*/
p=p->next;/*扫描下一结点*/
returnp;
}
以上两个算法的时间复杂度均为O(n)
3、插入
voidInsertList(LinkListhead,DataTypex,inti)
{/*将值为x的新结点插入到带头结点的单链表head的第i个结点的位置上*/
ListNode*p,*s;
p=GetNode(head,i-1);/*寻找第i-1个结点*/
if(p==NULL)
{
printf("插入位置非法\n");
exit(0);
}
s=(ListNode*)malloc(sizeof(ListNode));/*生成新结点*/
s->data=x;
s->next=p->next;
p->next=s;
}
4、删除
/*单链表的删除*/
voidDeleteList(LinkListhead,inti)
{/*删除带头结点的单链表中的第i个结点*/
ListNode*p,*r;
p=GetNode(head,i-1);/*寻找第i-1个结点*/
if(p==NULL||p->next==NULL)
{
printf("删除位置非法\n");
exit(0);
}
r=p->next;
p->next=r->next;
free(r);/*释放第i个结点*/
}
以上算法的时间复杂度都为O(n)
2.3.2循环链表
循环链表(CircularLinkedList):
就是将单向链表中的最后一个结点的指针指向链表中第一个结点,使整个链表构成一个环形。
循环链表特点:
从链表中的任意一个结点出发都可以找到表中其他结点。
循环链表的运算和单链表基本一样,差别在于:
当需要从头到尾扫描整个链表时,是否到表尾的条件不同。
在单链表中判断某结点链域值是否为“空”(p->next!
=NULL||p->next),在循环链表中则判断某结点的链域值是否等于头指针(p->next!
=head)。
在用头指针表示的单循环链表中,找开始结点的时间是O
(1),找终端结点的的时间为O(n),实用中多采用尾指针表示单循环链表。
2.3.3双向链表
双(向)链表(DoubleLinkedList):
有两种不同方向链的链表称为双向循环链表。
双链表特点:
双链表的每一个结点除含数据域外,还有两个指针域,一个指针指向其前驱结点,另一个指针指向其后继结点。
可以给双链表加一表头结点。
双链表可以循环,也可以不循环。
优点:
这样的设计弥补了简单链表的单向性的缺陷(单链表的NEXT操作的时间复杂度是O
(1),PRIOR操作的时间复杂度是O(n))。
双链表和单链表相比,每一个结点增加了一个指针域,双向链表虽然多占用了空间,但它给数据运算带来了方便。
双链表的基本运算:
存储结构:
typedefstructDuLNode{
ElemTypedata;
structDuLNode*prior;
structDuLNode*next;
}DuLNode,*DuLinkList;
1、插入
voidDInsertBefore(DLinkListp,DataTypex)
{//将值为x的新结点插入到带头结点的双链表的*p之前
DListNode*s=(DListNode*)malloc(sizeof(DListNode));//生成新结点
s->data=x;
s->prior=p->prior;
s->next=p;
p->prior->next=s;
p->prior=s;
}
2、删除
//双链表的删除
voidDDeleteNode(DLinkListp)
{//删除带头结点双链表中的*p结点
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
}
2.4顺序表和链表的比较
一、基于空间的考虑
1、空间分配
顺序表是静态分配,链表是动态分配,若线性表的长度变化较大,难以估计其存储规模,采用链表为好。
2、存储密度
顺序表的存储密度为1,而链表小于1,节约存储空间,采用顺序表为好。
二、基于时间的考虑
若对线性表的操作主要是进行查找,采用顺序表为好;
若对线性表频繁进行插入和删除操作,采用链表为好。
习题:
1、带头结点:
voidInverseList(LinkList*head)
{ListNode*p,*t,*h;
h=(*head)->next;p=h->next;h->next=NULL;
while(p!
=NULL)
{t=p->next;p->next=h;h=p;p=t;}
(*head)->next=h;
不带头结点:
voidInverseList(LinkList*head)
{ListNode*p,*t,*h;
p=(*head)->next;h=*head;(*head)->next=NULL;
while(p!
=NULL)
{t=p->next;p->next=h;h=p;p=t;}
*head=h;
}
2、voidInsertSortList(LinkList*L,DataTypex)
{LinkNode*p,*s;
p=*L;
if(p==NULL||x>=p->data)
{s=(ListNode*)malloc(sizeof(ListNode));
s->data=x;s->next=p;*L=s;}
else
{while(p->next!
=NULL&&xnext->data)
p=p->next;
s=(ListNode*)malloc(sizeof(ListNode));
s->data=x;s->next=p->next;p->next=s;
}
}
3、带头结点
intListlength(LinkListL)
{inti=0;
ListNode*p;
for(p=L->next;p;p=p->next)
i++;
return(i);
}
4、LinkListConnect(LinkListL1,LinkListL2)
{LinkNode*p,*q;
p=L1;q=L2->next;
while(p->next)
p=p->next;
p->next=q;
free(L2);
returnL1;
}
5、voidMergeList(LinkListA,LinkListB,LinkList*C)
{LinkNode*qa,*qb,*qc;
qc=*C=A;
qa=A->next;qb=B->next;
while(qa&&qb)
{if(qa->datadata)
{qc->next=qa;qc=qc->next;qa=qa->next;}
else
{qc->next=qb;qc=qc->next;qb=qb->next;}
}
qc->next=qa?
qa:
qb;
free(B);
}