栈和队列

    最近由于实验室事情缠身,每天只能挤出两个小时来学数据结构。想赶快学完,所以学的比较粗糙。但是感觉还是应该停下来总结一下,欲速则不达啊。如果只是粗略的看完,估计对自己的提高不大。由于在平时的学习中用的并不多,所以对数据结构的理解不够深入,只能算是入门的介绍性,帮助自己梳理一下。这一篇主要将讲栈和队列。
    栈和队列本质上也是线性表,只是它们是操作受限的线性表。个人的感觉是栈和队列相对链表还说还简单一些,可能自己的理解还不深,在应用上用的不多,初学者,大家见谅。
    栈是限定在表尾进行插入或删除操作的线性表。表尾称为栈顶,表头称为栈底。栈是一种后进先出的线性表。
    和线性表一样,栈也可以分为顺序栈和链栈。但是因为栈规定只能从栈顶进行插入和删除操作,所以顺序栈完全可以满足需求。在栈中需要注意的是栈顶指针指向的地址是没有内容的,但是分配了空间,如下图所示。
    
    不管是哪种数据结构,最重要的是结构的定义。顺序栈的定义如下:
    
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 element
	if(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;
}
   可见,在循环队列中,最重要的就是要搞好数组的下标操作,不是很复杂,可能是我用的不够深入,后续再进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值