栈和队列都是动态集合,在这种结构中,可以用delete操作去掉元素是预先规定好的。
栈中:可以去掉的是最近插入的元素(先进后出)
队列中:可以去掉的是在集合中存在时间最长的那个(先进先出)
栈:可是使用一个数组S[1…n]来实现一个至多有n个元素的栈。数组S有属性top[S],指向最近插入的元素。S[1]是栈底元素,S[top]是栈顶元素。
top[S]=0时,栈空,若有弹出操作,则下溢。若top[S]超过n,则称栈上溢。
入栈,top先加一;栈,top后减一。
队列:用Q[1…n]来实现一个队列,其中至多包含n-1个元素,属性head[Q]指向队头,head数值加一,则出队。属性tail[Q]指向队尾,插入元素的时候,tail数值加一。
当head[Q]=tail[Q]+1时,队列为满,插入元素,上溢。head[Q]=tail[Q]=1时,队列为空,删除元素,下溢。
链表数据结构中,各对象按线性顺序排序。链表和数组不同,链表中的顺序是由对象的指针决定,数组中的线性序是由数组的下标决定的。
链表可以用来灵活的表示动态集合。
双链表L的每一个元素都是一个对象,每一个对象包含一个关键字和两个指针:next:指向后继,若为NIL则为没有后继是最后一个元素,为尾tail;prev指向前驱,若为NIL则为链表的第一个元素,为头head。属性head[L]指向表的第一个元素,若,head[L]=NIL,则链表为空。
单链表,双链表;已排序链表和未排序链表;环形和非环形
单链表没有前驱prev指针。已排序的链表中的元素的线性顺序对应表中各个关键字的线性顺序。
链表的搜索操作:
从头开始寻找,判断链表是否为空,判断数据值是否为所需的数据值,如果不是继续往后挪,指向后继,如果到队尾都没有,则查找失败。否则查找成功返回所在的位子的指针。
链表的插入操作,
下”的指针。下面用代码描述其过程:
pinsert->prior=p;
pinsert->next=p->next;
p->next->prior=pinsert;
p->next=pinsert;
3.双向链表的删除操作
理解了双向链表的插入操作后,删除操作便十分容易理解。下面用代码描述其过程:
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
双向链表的其他操作与单链表类似,在此不再赘述,完整的代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int status;
typedef int elemtype;
typedef struct node{
elemtype data;
struct node * next;
struct node * prior;
}node;
typedef struct node* dlinklist;
status visit(elemtype c){
printf("%d ",c);
}
/*双向链表初始化*/
status initdlinklist(dlinklist * head,dlinklist * tail){
(*head)=(dlinklist)malloc(sizeof(node));
(*tail)=(dlinklist)malloc(sizeof(node));
if(!(*head)||!(*tail))
return ERROR;
/*这一步很关键*/
(*head)->prior=NULL;
(*tail)->next=NULL;
/*链表为空时让头指向尾*/
(*head)->next=(*tail);
(*tail)->prior=(*head);
}
/*判定是否为空*/
status emptylinklist(dlinklist head,dlinklist tail){
if(head->next==tail)
return TRUE;
else
return FALSE;
}
/*尾插法创建链表*/
status createdlinklisttail(dlinklist head,dlinklist tail,elemtype data){
dlinklist pmove=tail,pinsert;
pinsert=(dlinklist)malloc(sizeof(node));
if(!pinsert)
return ERROR;
pinsert->data=data;
pinsert->next=NULL;
pinsert->prior=NULL;
tail->prior->next=pinsert;
pinsert->prior=tail->prior;
pinsert->next=tail;
tail->prior=pinsert;
}
/*头插法创建链表*/
status createdlinklisthead(dlinklist head,dlinklist tail,elemtype data){
dlinklist pmove=head,qmove=tail,pinsert;
pinsert=(dlinklist)malloc(sizeof(node));
if(!pinsert)
return ERROR;
else{
pinsert->data=data;
pinsert->prior=pmove;
pinsert->next=pmove->next;
pmove->next->prior=pinsert;
pmove->next=pinsert;
}
}
/*正序打印链表*/
status traverselist(dlinklist head,dlinklist tail){
/*dlinklist pmove=head->next;
while(pmove!=tail){
printf("%d ",pmove->data);
pmove=pmove->next;
}
printf("\n");
return OK;*/
dlinklist pmove=head->next;
while(pmove!=tail){
visit(pmove->data);
pmove=pmove->next;
}
printf("\n");
}
/*返回第一个值为data的元素的位序*/
status locateelem(dlinklist head,dlinklist tail,elemtype data){
dlinklist pmove=head->next;
int pos=1;
while(pmove&&pmove->data!=data){
pmove=pmove->next;
pos++;
}
return pos;
}
/*返回表长*/
status listlength(dlinklist head,dlinklist tail){
dlinklist pmove=head->next;
int length=0;
while(pmove!=tail){
pmove=pmove->next;
length++;
}
return length;
}
/*逆序打印链表*/
status inverse(dlinklist head,dlinklist tail){
dlinklist pmove=tail->prior;
while(pmove!=head){
visit(pmove->data);
pmove=pmove->prior;
}
printf("\n");
}
/*删除链表中第pos个位置的元素,并用data返回*/
status deleteelem(dlinklist head,dlinklist tail,int pos,elemtype *data){
int i=1;
dlinklist pmove=head->next;
while(pmove&&i<pos){
pmove=pmove->next;
i++;
}
if(!pmove||i>pos){
printf("输入数据非法\n");
return ERROR;
}
else{
*data=pmove->data;
pmove->next->prior=pmove->prior;
pmove->prior->next=pmove->next;
free(pmove);
}
}
/*在链表尾插入元素*/
status inserttail(dlinklist head,dlinklist tail,elemtype data){
dlinklist pinsert;
pinsert=(dlinklist)malloc(sizeof(node));
pinsert->data=data;
pinsert->next=NULL;
pinsert->prior=NULL;
tail->prior->next=pinsert;
pinsert->prior=tail->prior;
pinsert->next=tail;
tail->prior=pinsert;
return OK;
}
int main(void){
dlinklist head,tail;
int i=0;
elemtype data=0;
initdlinklist(&head,&tail);
if(emptylinklist(head,tail))
printf("链表为空\n");
else
printf("链表不为空\n");
printf("头插法创建链表\n");
for(i=0;i<10;i++){
createdlinklisthead(head,tail,i);
}
traverselist(head,tail);
for(i=0;i<10;i++){
printf("表中值为%d的元素的位置为",i);
printf("%d位\n",locateelem(head,tail,i));
}
printf("表长为%d\n",listlength(head,tail));
printf("逆序打印链表");
inverse(head,tail);
for(i=0;i<10;i++){
deleteelem(head,tail,1,&data);
printf("被删除的元素为%d\n",data);
}
traverselist(head,tail);
if(emptylinklist(head,tail))
printf("链表为空\n");
else
printf("链表不为空\n");
printf("尾插法创建链表\n");
for(i=0;i<10;i++){
//inserttail(head,tail,i);
createdlinklisttail(head,tail,i);
}
traverselist(head,tail);
printf("逆序打印链表");
inverse(head,tail);
}
对象的数组表示
有些语言不支持指针和对象数据,使用数组和数组下标构造对象及指针
使用三个数组来实现双链表。key[x] next[x] prev[x],共同表示链表中的一个对象。
对象的单数组表示
一个对象占用连续的三个元素的位置,然后使用指针表示不同的值,如A[i]和A[i+2],表示不同的对象
分配和释放对象
一般使用废料收集器来确定哪些对象没有用。
将自由对象安排成一个单链表,称为自由表。
自由表是一个栈:下一个分配的对象是最近被释放的那个,使用pop和push的方式实现对象的去分配和分配的。
使用链接数据结构表示有根树
用一个对象来表示树的一个结点。对链表,假设每个结点都有一个关键字域,其余包含指向其他节点的指针。
二叉树
用域p,left,right来存放指向二叉树T中的父亲、左儿子、右儿子的指针
如果p[x]=NIL,则x为根。
同理:若right[x] or left[x]=NIL则左或者右为空。整个数T的根由属性root[T]指向,如果root[T]=NIL则树为空。
分支数无限制的有根树,可以使用最左孩子,最右兄弟指针。左孩子,右兄弟的表示方式。
树的其他表示方式:
使用数组加上下标的方式,指向父亲没有指向子女的指针。