栈和队列

来源:互联网 发布:虚拟机软件vmware下载 编辑:程序博客网 时间:2024/05/22 06:48
    最近由于实验室事情缠身,每天只能挤出两个小时来学数据结构。想赶快学完,所以学的比较粗糙。但是感觉还是应该停下来总结一下,欲速则不达啊。如果只是粗略的看完,估计对自己的提高不大。由于在平时的学习中用的并不多,所以对数据结构的理解不够深入,只能算是入门的介绍性,帮助自己梳理一下。这一篇主要将讲栈和队列。
    栈和队列本质上也是线性表,只是它们是操作受限的线性表。个人的感觉是栈和队列相对链表还说还简单一些,可能自己的理解还不深,在应用上用的不多,初学者,大家见谅。
    栈是限定在表尾进行插入或删除操作的线性表。表尾称为栈顶,表头称为栈底。栈是一种后进先出的线性表。
    和线性表一样,栈也可以分为顺序栈和链栈。但是因为栈规定只能从栈顶进行插入和删除操作,所以顺序栈完全可以满足需求。在栈中需要注意的是栈顶指针指向的地址是没有内容的,但是分配了空间,如下图所示。
    
    不管是哪种数据结构,最重要的是结构的定义。顺序栈的定义如下:
    
typedef char SElemType;
typedef struct stack{SElemType *base;SElemType *top;int stacksize;}SqStack;
    
    其中,stacksize指示栈的当前可使用的最大容量。base和top分别指示栈底指针和栈顶指针。
    栈的基本操作函数实现:
    
//初始化栈Status InitialStack(SqStack *stack){(*stack).base = (*stack).top = (SElemType*)malloc(sizeof(SElemType)*MAXSIZE);if(!(*stack).base){exit(OVERFLOW);}(*stack).stacksize = MAXSIZE; return OK;}
  
入栈
Status InsertElem(SqStack *stack,SElemType e){char *q = NULL;//需要判断是否为栈满if(((*stack).top-(*stack).base)>=(*stack).stacksize){   q = (SElemType*)realloc((*stack).base,sizeof(SElemType)*((*stack).stacksize+INCREMENTSIZE));   if(!(*stack).base)   {exit(OVERFLOW);   }   (*stack).base = q;   (*stack).top = ((*stack).base) + (*stack).stacksize;//注意修改栈顶指针   (*stack).stacksize += INCREMENTSIZE; //注意修改栈的大小}*(*stack).top = e;(*stack).top++;return OK;}
    在这里我犯了低级错误。sizeof()这个函数的参数应该是变量的类型,如int,char等。我一开始直接填入了常量,即我写的是sizeof((*stack).stacksize+INCREMENTSIZE),导致最后运行结果出现了问题,因为这个时候默认的是int类型,如果你使用的是char型,那么就出现问题了。
   
//出栈Status DeleteElem(SqStack *stack, SElemType *e){if((*stack).base == (*stack).top){return ERROR;}(*stack).top--;//栈顶指针首先要向下移动一位,才能指示有效的填充数据*e = *(*stack).top;return OK;}
   栈的应用就先不讲了,现在的理解深度还达不到。
   
   
队列:
    队列是一种先进先出的线性表。它只允许在队尾进行插入操作,在队头进行删除操作。当然,队列肯定也有两种结构,这里主要以链式队列进行介绍。
    队列的结构如下所示:
    
typedef struct QNode{QElemType data;struct QNode *next;}QNode,*QueuePtr;//只需要头指针和尾指针typedef struct{QueuePtr front;QueuePtr rear;}LinkQueue;
    队列的主要基本操作:
    
/*初始化链队,注意队头指针的下一个指向为空。 */Status InitQueue(LinkQueue &Q){Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));if(!Q.front){exit(OVERFLOW);}Q.front->next = NULL;return OK;}
//获得队头元素,我这里认为是front指针的下一个元素
Status GetHead(LinkQueue &Q, string &e){if(Q.front == Q.rear){return ERROR;}e = *(Q.front->next->data);return OK;}
//插入元素到队列中Status EnQueue(LinkQueue &Q,string e){QueuePtr p;p = (QueuePtr)malloc(sizeof(QNode));if(!p){exit(OVERFLOW);}string *element =  new string;//给每一个新的字符串都需要分配一个空间,让队列中的data指针指向它*element = e;p -> data = element;p -> next = NULL;Q.rear->next = p;Q.rear = p;return OK;}
/* 删除元素,只能从队头进行删除。注意要考虑删除的是最后一个元素的情况。要修改队尾指针*/Status DeQueue(LinkQueue &Q, string &e){QueuePtr p = Q.front->next;if(Q.front == Q.rear){return ERROR;}e = *(p->data);Q.front->next = p->next;//consider p points to the last elementif(p == Q.rear){Q.front = Q.rear;}delete p->data;free(p);return OK;}

/*对队列中的元素进行遍历操作*/Status QueueTraverse(LinkQueue Q, Status (*visit)(QElemType)){QueuePtr p = Q.front->next;int result = 0;while(p != NULL){result = visit(p->data);if(!result){break;}p = p->next;}return OK;}

    在队列中,还有一种常用的类型是循环队列。这种队列的主要作用是提高存储空间的利用率。另外,这种队列在分层机制中可用于缓冲区,通过读写指针控制有效数据的处理。
    首先来看不用循环队列可能的问题,如下所示:
    

    在队列中,存在队头和队尾两个指针,假设分配了一个4个自己的数组空间。在正常情况下,应该如左图所示。当随着进行插入操作,就会出现右图的情况,这个时候,如果我们想再次插入数据,由于队尾指针已指向分配的最大地址处,就会发生数组越界的错误。但是这个时候存储空间却没有用完,就会造成空间浪费。
    这个时候,如果将这段存储空间想像成一个环形队列,可以有效的避免这个问题。
    

    我们规定,当头指针和尾指针相等的时候,队列为空。而当队列满的时候,为了和队列为空的时候进行区别,有两种方法:
    1. 设定标志位来指示空还是满,这个时候队列满也是头尾指针相等。
    2. 以队头指针在队尾指针的下一位上认为是满,注意谁在前谁在后。
    一般情况下,循环队列一般用于顺序存储结构中,队头队尾分别指示的是数组的下标。链队列主要用于无法预估所用大小的情况,但是感觉这个和循环链表相似。
    循环队列的数据结构定义如下
    
typedef struct queue{ElemType front;ElemType rear;ElemType *base;int queuesize;}Queue, *SqQueue;
   循环队列的基本操作如下:
   
//初始化队列Status InitialQueue(Queue *queue){(*queue).base = (ElemType*)malloc(MAXSIZE*sizeof(ElemType));if(!(*queue).base){exit(OVERFLOW);}(*queue).front = (*queue).rear = 0;(*queue).queuesize = MAXSIZE;return OK;}

//插入元素到队列中Status InsertElem(Queue *queue, ElemType e){int location;location = ((*queue).front+1)%(*queue).queuesize;if(location == 0){(*queue).base = (ElemType*)realloc((*queue).base,((*queue).queuesize + INCREMENTSIZE)*sizeof(ElemType));if(!(*queue).base){exit(OVERFLOW);}}(*queue).queuesize += INCREMENTSIZE;location = (*queue).front % (*queue).queuesize;(*queue).base[location] = e;(*queue).front = ((*queue).front + 1)%(*queue).queuesize;return OK;}

//删除队列中的元素Status DeleteElem(Queue *queue, ElemType *e){//如果队列是空if((*queue).front == (*queue).rear){return ERROR;}*e = (*queue).base[(*queue).rear];(*queue).rear = ((*queue).rear + 1)%(*queue).queuesize;return OK;}
   可见,在循环队列中,最重要的就是要搞好数组的下标操作,不是很复杂,可能是我用的不够深入,后续再进行修改。
0 0
原创粉丝点击