基本数据结构(栈,队列,树)

本文深入解析栈、队列、链表等数据结构的核心概念与操作,包括动态集合、上溢下溢、插入删除等关键算法,以及单链表、双链表、已排序链表等多种链表形式的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

栈和队列都是动态集合,在这种结构中,可以用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则树为空。

分支数无限制的有根树,可以使用最左孩子,最右兄弟指针。左孩子,右兄弟的表示方式。

树的其他表示方式:
使用数组加上下标的方式,指向父亲没有指向子女的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值