数据结构 学习笔记03——栈与队列
欢迎来我的个人网站:http://www.rxwcv.cn
栈是限制仅在表的一端进行插入和删除运算的线性表又称为后进先出表(LIFO表)。插入、删除端称为栈顶,另一端称栈底。表中无元素称空栈。基本运算有:
1) initstack(s),构造一个空栈;
2) stackempty(s),判栈空;
3) stackfull(s),判栈满;
4) push(s,x),进栈;
5) pop (s),退栈;
6) stacktop(s),取栈顶元素。
1.顺序栈
结构如下:
typedef struct { DataType data[MAXSIZE]; int top; }SeqStack;
即,它需要一个栈顶top。
相关算法的关键步骤如下
//置栈空 void Initial(SeqStack *S) { S->top=-1; } //判栈空 int IsEmpty(SeqStack *S) { return S->top==-1; } //进栈 void Push(SeqStack *S,DataType x) { S->data[++S->top]=x; } //出栈 DataType Pop(SeqStack *S) { return S->data[S->top--]; } //栈顶减1,返回已经出栈的元素
2.链栈
结构如下:
typedef struct node { DataType data; struct node *next; //知道结构名称的作用了吧,这里就要用到node }LinkStack; typedef struct { LinkStack *top; }TopNode;
我们需要一个专门指向栈顶的指针(注意不是栈顶指针,而是指向栈顶的指针)。所以我们定义一个结构体TopNode,T.top就是指向栈顶的指针。或者我们用一个数组也行:
LinkStack * TopNode[1];
那么TopNode[0]就用来作指向栈顶的指针,函数的形参写成LinkStack * TopNode[],传实参的时候用数组名TopNode,在函数内部就可以使用TopNode[0]。这种方面类似于函数形参是一个结构体TopNode *T,那么传实参的时候传&T,在函数内部使用T->top。
至于用数组和用结构哪个好,我觉得用结构还要用typedef定义类型,效率可能稍低。所以,用结构含义清楚,用数组效率更高。
/*头插法特点:无头结点,top指针始终指向最后插入的那个元素*/ LinkStack *CreListTou() { int x; LinkStack *S; TopNode T; T.top=NULL; scanf("%d",&x); while(x!=-1) //输入-1代表结束 { S=malloc(sizeof(LinkStack)); S->data=x; /*关键代码: S->next等于T.top ,然后T.top反转来等于S*/ S->next=T.top; T.top=S; //修改top指向 scanf("%d",&x); } return T.top; } //置栈空 void Initial(TopNode *T) { T->top=NULL; } //进栈 void Push(TopNode *T,DataType x) { LinkStack *S; S=malloc(sizeof(LinkStack)); S->data=x; //与建栈时相同,将S的后继设置为原栈顶,S成为新的栈顶 S->next=T->top; T->top=S; } //出栈 DataType Pop(TopNode *T) { LinkStack *p; DataType x; /*关键算法:先把T.top指向原栈顶的next,然后free掉原栈顶*/ p=T->top; x=p->data; T->top=p->next;// T.top指向原栈顶的next free(p); return x; }
以上是c语言
C++版:
操作:push,pop(弹出栈顶元素), peek (访问栈顶元素)
检测栈状态:stackEmpty(检测栈是否为空),stackFull(检测栈是否已满)
stack线性表实现
const int MaxStackSize=50; class stack { private: DataType Stacklist[MaxStackSize]; int top; public: //initilize stack(void); void push(const DataType item); DataType pop(); void clearStack(); DataType seek() const; int stackEmpty() const; int stackFull() const; } //DataType is predefined with typedef; stack::stack():top(-1); stack::push(const DataType item) { if(top==Maxtacksize-1) { //the stack is full error //error message; exit(0); } top++; Stacklist[top]=item; } DataType stack::pop() { if(top==-1) { //the stack is empty //error message; exit(0); } DateType temp=Stacklist[top]; top--; return temp; } DataType stack:peek() const { if(top==-1) { //error message exit(1); } return Sacklist[top]; } int Stack:StackEmpty() const { return top==-1; } int Stack:StackFull() const { return top==MaxStackSize; }
stack链表实现
typedef int elemType; struct sNode{ elemType data; struct sNode *next; }; /* 1).初始化链栈为空 */ void initStack(struct sNode* *hs)//初始化 { *hs = NULL; return; } /* 2).向链中插入一个元素(入栈) */ void push(struct sNode* *hs, elemType x) { struct sNode *newP; newP = malloc(sizeof(struct sNode)); if(newP == NULL){ printf("内在空间分配失败! "); exit(1); } newP->data = x; /* 给新分配的结点赋值 */ /* 向栈顶插入新结点 */ newP->next = *hs; *hs = newP; return; } /* 3).从链栈中删除一个元素并返回它(出栈) */ elemType pop(struct sNode* *hs) { struct sNode *p; elemType temp; if(*hs == NULL){ printf("栈空无法删除! "); exit(1); } /* 暂存栈顶结点指针,并使栈顶指针指向下一个结点 */ p = *hs; *hs = p->next; /* 暂存原栈顶元素以便返回,同时回收原栈顶结点 */ temp = p->data; free(p); return temp; /* 返回原栈顶元素 */ } /* 4).读取栈顶元素 */ elemType peek(struct sNode* *hs) { /* 对于空栈则退出运行 */ if(*hs == NULL){ printf("栈空,无栈顶元素! "); exit(1); } return (*hs)->data; /* 返回栈顶结点的值 */ } /* 5).判断链栈是否为空,为空返回1,否则返回0 */ int emptyStack(struct sNode* *hs) { if(*hs == NULL){ return 1; }else{ return 0; } } /* 6).清除链栈为空 */ void clearStack(struct sNode* *hs) { struct sNode *cp, *np; cp = *hs; /* 使cp指向栈顶结点 */ /* 从栈底依次删除每个结点 */ while(cp != NULL){ np = cp->next; free(cp); cp = np; } *hs = NULL; /* 置链栈为空 */ return; }
、
队列的基本定义和计算。
队列是一种运算受限的线性表,允许删除的一端称队首,允许插入的一端称队尾。队列又称为先进先出线性表,FIFO表。
队列的基本运算:
1) initqueue(q),置空队;
2) queueempty(q),判队空;
3) queuefull(q),判队满;
4) enqueue(q,x),入队;
5) dequeue(q),出队;
6) queuefront(q),返回队头元素。
顺序队列。
队列的顺序存储结构称顺序队列。设置front和rear指针表示队头和队尾元素在向量空间的位置。顺序队列中存在“假上溢”现象,由于入队和出队操作使头尾指针只增不减导致被删元素的空间无法利用,队尾指针超过向量空间的上界而不能入队。
为克服“假上溢”现象,将向量空间想象为首尾相连的循环向量,存储在其中的队列称循环队列。i=(i+1)%queuesize
循环队列的边界条件处理:由于无法用front==rear来判断队列的“空”和“满”。解决的方法有:
1) 另设一个布尔变量以区别队列的空和满;
2) 少用一个元素,在入队前测试rear在循环意义下加1是否等于front;
3) 使用一个记数器记录元素总数。
循环队列的类型定义:
#define queuesize 100 typedef char datatype; typedef struct{ int front; int rear; int count; datatype data[queuesize]; }cirqueue; 1) 置队空。 Void initqueue(cirqueue *q) { q->front=q->rear=0; q->count=0; } 2) 判队空。 Int queueempty(cirqueue *q) { return q->count==0; } 3) 判队满。 Int queuefull(cirqueue *q) { return q->count==queuesize; } 4) 入队。 Void enqueue(cirqueue *q ,datatype x) { if(queuefull(q)) error(“queue overfolw”); q->count++; q->data[q->rear]=x; q->rear=(q->rear+1)%queuesize; } 5) 出队。 Datatype dequeue(cirqueue *q) { datatype temp; if(queueempty(q)) error(“queue underflow”); temp=q->data[q->front]; q->count--; q->front=(q->front+1)%queuesize; return temp; } 6) 取队头元素。 Datatype queuefront(cirqueue *q) { if(queueempty(q)) error(“queue is empty”); return q->data[q->front]; }
链队列
队列的链式存储结构称链队列,链队列由一个头指针和一个尾指针唯一确定。
链队列的定义:
typedef struct queuenode { datatype data; struct queue *next; }queuenode; typedef struct { queuenode *front; queuenode *rear; }linkqueue; 1) 建空队。 Void initqueue(linkqueue *q) { q->front=q->rear=NULL; } 2) 判队空。 Int queueempty(linkqueue *q) { return q->front==NULL&&q->rear==NULL; } 3) 入队。 Void enqueue(linkqueue *q,datatype x) { queuenode *p=(queuenode *)malloc(sizeof(queuenode)); p->data=x; p->next=NULL; if(queueempty(q)) q-front=q->rear=p; else{ q->rear->next=p; q->rear=p; } } 4) 出队。 Datatype dequeue(linkqueue *q) { datatype x; queuenode *p; if(queueempty(q)) error(“queue is underflow”); p=q->front; x=p->data; q->front=p->next; if(q->rear==p) q->rear=NULL; free(p); return x; } 5) 取队头元素。 Datatype queuefront(linkqueue *q) { if(queueempty(q)) error(“queue is empty”); return q->front->data; }
最后,附上我上机实验——计算表达式的实验报告以及源代码。
欢迎来我的个人网站:http://www.rxwcv.cn