1 / 40

栈和队列

栈和队列. 栈和队列. 两种操作受限的线性表 两种应用广泛的抽象数据类型 (ADT). 栈. 出栈. 进栈. a n. 栈顶. a 3. a 2. 栈底. a 1. 栈的定义和术语. 栈:限制在一端进行插入、删除操作的线性表 栈又称为“后进先出” (Last In First Out ------LIFO) 线性表 限定插入或删除操作只能在表尾 ( 栈顶 ) 进行,分别称为进栈和出栈。指示栈顶位置的指针称为栈顶指针。 栈底:不可对栈进行输入、输出的一端 栈的长度:栈中元素的个数 空栈:元素个数为 0 的栈

Download Presentation

栈和队列

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 栈和队列

  2. 栈和队列 • 两种操作受限的线性表 • 两种应用广泛的抽象数据类型(ADT)

  3. 出栈 进栈 an 栈顶 a3 a2 栈底 a1 栈的定义和术语 • 栈:限制在一端进行插入、删除操作的线性表 • 栈又称为“后进先出”(Last In First Out ------LIFO)线性表 • 限定插入或删除操作只能在表尾(栈顶)进行,分别称为进栈和出栈。指示栈顶位置的指针称为栈顶指针。 • 栈底:不可对栈进行输入、输出的一端 • 栈的长度:栈中元素的个数 • 空栈:元素个数为0的栈 • 栈可采用顺序存储结构或链表结构来实现,分别称为顺序栈和链栈

  4. 例: 对于一个栈,给出输入项A、B、C,如果输入项序列 由ABC组成,试给出所有可能的输出序列。 A进 A出 B进 B出 C进 C出 ABC A进 A出 B进 C进 C出 B出 ACB A进 B进 B出 A出 C进 C出 BAC A进 B进 B出 C进 C出 A出 BCA A进 B进 C进 C出 B出 A出 CBA 不可能产生输出序列CAB

  5. 栈的ADT定义 ADT Stack{ D = {ai | ai ElemSet, i = 1,2,…n } R = {<ai-1, ai> | ai-1, ai D, i = 2,…n} 约定an端为栈顶, a1端为栈底 P: InitStack(&S) ClearStack(&S) StackEmpty(S) StackLength(S) GetTop(S, &e) Push(&S, e) Pop(&S, &e) StackTraverse(S, visit()); }ADT Stack

  6. 栈的表示和实现 • 栈的顺序存储表示---顺序栈 • 在连续的存储单元内依次存放栈中的数据元素,并设指针top来指示栈顶元素的位置 • top为0时表示空栈 #define STACK_INIT_SIZE 100; #define STACKINCREMENT 10; typedef struct{ SElemType *base; //栈底指针 SElemType *top; //栈顶指针 int stacksize; //栈的当前最大可用容量 }SqStack; SqStack S;

  7. base 指向栈底的位置,base=NULL,栈结构不存在 • top 初值指向栈底,top=base ,为栈空 • 插入栈顶元素,top=top+1 • 删除栈顶元素,top=top-1

  8. 顺序栈的实现 Status InitStack(SqStack &S){ S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType)); if (!S.base) exit(OVERFLOW); S.top = S.base; S.stacksize = STACK_INIT_SIZE; return OK; } Status GetTop(SqStack S, SElemType &e){ if (S.top == S.base) return ERROR; e = *(S.top –1); return OK; }

  9. 顺序栈的实现 Status Push(SqStack &S, SElemType e){ if (S.top – S.base >= S.stacksize){ //栈满,追加空间 S.base = (ElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(ElemType)); if (!S.base) exit(OVERFLOW); S.top = S.base + S.stacksize; S.stacksize += STACKINCREMENT; } *S.top ++ = e; //进栈 return OK; } Status Pop(SqStack &S, SElemType &e){ if (S.top == S.base) return ERROR; e = * -- S.top; //出栈 return OK; }

  10. 顺序栈的实现 void ClearStack(&S) { S.top = S.base; } int StackLength(S) { return (S.top –S.base); } status StackEmpty(S) { if (S.top == S.base) return TRUE else return FALSE; }

  11. data next S 栈顶 栈底 栈的表示和实现 • 栈的链式表示---链栈 • 链式栈无栈满问题,空间可扩充,通过指针连接所有栈中的元素 • 链式栈的栈顶在链头,表头结点即栈顶元素 • 插入与删除仅在栈顶处执行 • 适合于多栈操作 typedef struct{ SElemType data; struct StackNode *next; }StackNode, *StackPtr; StackPtr S;

  12. 链栈的实现 Status InitStack(StackPtr &S){ S = (StackPtr)malloc(sizeof(StackNode)); if (!S) exit(OVERFLOW); S.next = NULL; return OK; } Status GetTop(StackPtr S, SElemType &e){ if (!S) return ERROR; e = S->data; return OK; }

  13. p e S 链栈的实现 Status Push(StackPtr &S, SElemType e){ p = (StackPtr)malloc(sizeof(StackNode)); p->data = e; p->next = S; S = p; return OK; } Status Pop(StackPtr &S, SElemType &e){ if (!S) return ERROR; //空栈 p = S; e = S->data; S = S->next; free(p); return OK; } S

  14. 栈的应用 • 数制转换 • 括号匹配检验 • 行编辑程序 • 迷宫求解 • 表达式求值

  15. 数制转换 • 数制转换原理 • N = (N div d) * d + N mod d • 如(1348)10 = (250 4)8 • 实现 void conversion() { InitStack(S); scanf(“%d”, N); while (N) { Push(S, N % 8); N=N/8; } while (!StackEmpty(s)){ Pop(S,e); printf(“%d”, e); } }

  16. 栈与递归的实现 • 递归的定义 若一个对象部分地包含它自己, 或用它自己给自己定义, 则称这个对象是递归的;若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。 • 在以下三种情况下,常常用到递归方法。 • 定义是递归的 • 数据结构是递归的 • 问题的解法是递归的

  17. 1 若n=0 n * Fact(n-1) 若n>0 栈与递归的实现 • 递归函数 • 一个直接调用自己或通过一系列的调用语句间接地调用自己的函数 • 如:求阶乘的递归实现 • Fact(n) = int Fact(int n) 1 { 2 if (n == 0) 3 return 1; 4 else 5 return n * Fact(n-1); 6 }

  18. 递归方法求解的条件 • 一个问题可以化解为若干个性质相同、解法相同的“小问题” • 明确的递归出口 • 换种说法: • 不需要递归处理的最小的、基本的问题 • 将特定问题简化成一个或多个更小的问题的通用方法,由此前进,最终将问题一直简化为基本问题 • 解题的方式用递归解法简单 • 汉诺塔问题、八皇后问题

  19. 0 若n=0 1 若n=1 Fib(n-1) + Fib(n-2) 其他情形 栈与递归的实现 • 如:Fibonacci数列的递归实现 • Fib(n) = int Fib(int n) 1 { 2 if (n == 0 || n ==1) 3 return n; 4 else 5 return Fib(n-1) + Fib(n-2); 6 } 递归出口 递归体

  20. 栈与递归的实现 • 如:Hanoi塔的递归实现 • 规则:每次只能移动一个圆盘,圆盘可插在X,Y,Z中的任一塔座上,任何时刻都不能将一个较大的圆盘压在较小的圆盘之上 void hanoi(int n, char x, char y, char z) 1 { 2 if (n == 1) 3 move(x, 1, z); //将编号为1的圆盘从x移到z 4 else { 5 hanoi(n-1, x, z, y);//将x上编号为1至n-1的圆盘移到y, z作辅助塔 6 move(x, n, z);//将编号为n的圆盘从x移到z 7 hanoi(n-1, y, x, z); //将y上编号为1至n-1的圆盘移到z, x作辅助塔 8 } 9 }

  21. 队列

  22. 队列的概念 • 队列又称为“先进先出”(FIFO)线性表 • 限定插入操作只能在队尾进行,而删除操作只能在队首进行 • 队列也可以采用顺序存储结构或链表结构来实现,分别称为顺序队列和链队列

  23. 队列的ADT定义 ADT Queue{ D = {ai | ai ElemSet, i = 1,2,…n } R = {<ai-1, ai> | ai-1, ai D, i = 2,…n} 约定a1端为队首, an端为队尾 P: InitQueue(&Q) DestroyQueue(&Q) ClearQueue(&Q) QueueEmpty(Q) QueueLength(Q) GetHead(Q,&e) EnQueue(&Q, e) DeQueue(&Q, &e) QueueTraverse(Q, visit()) }ADT Queue

  24. 5 4 3 2 1 Q.rear 0 Q.front 空队列 队列的表示和实现 • 队列的顺序表示---顺序队列 • 用一组连续的存储单元依次存放从队首到队尾的元素,附设两个指针front和rear分别指向队首元素和队尾元素的下一位置 #define MAXQSIZE 100 typedef struct { QElemType *base; int front; int rear }SqQueue; SqQueue Q;

  25. 5 5 4 4 Q.rear 3 Q.rear 3 J3 J3 2 2 Q.front J2 1 1 Q.rear J1 0 0 J6 5 Q.front 5 J5 4 4 J1, J2 , J3相继入队 J1, J2 相继出队 J4 3 3 Q.front 2 2 1 1 0 Q.rear 0 Q.front 队列满 空队列 顺序队列的实现 • 当front = rear = 0时表示空队列 • 当插入新元素到队尾时,rear加1 • 当删除队首元素时,front加1 • rear 始终指向队列元素的下一个位置 • front = rear可能表示队空,也可能表示队满 • (应另设标志以便区别队空状态) • 这种顺序队列空间利用率低,考虑采用循环队列

  26. 非循环队列的操作小结 • 队空:Q.front=Q.rear • 队满:Q.rear-Q.front=maxsize(求队长) • 入队:新元素按rear指示位置加入,再将队尾指针加1 ,即 rear = rear + 1 • 出队:将front指示的元素取出,再将队头指针加1,即front = front + 1。

  27. maxsize-1 Q.rear c maxsize-1 c 0 d b 4 b Q.front 3 a a 2 1 Q.rear 1 Q.front 0 2 假满 循环队列及其实现 考虑队满条件,即当Q.rear=maxsize时,是否整个存储空间都已占用? 循环队列 • 循环队列 • 为了提高队列的空间利用率,提出循环队列 • 结构与顺序队列相同,只是界线处理稍有不同

  28. 模运算 • 入队,rear指针变化: • Q.rear= (Q.rear+1)%maxsize • 出队,front指针变化: • Q.front=( Q.front +1)%maxsize

  29. Q.rear J5 5 J4 4 0 3 1 J3 2 Q.front 一般情况 Q.rear 5 Q.front 4 0 3 1 J3 Q.rear 2 删除此元素 Q.front 队空时 包含一个元素的队列

  30. J5 5 J6 J4 4 0 3 1 J7 J3 2 J8 Q.front Q.rear 队满时 J5 插入一个元素 5 J6 J4 4 0 3 1 J7 J3 2 • 队满和队空时,均有Q.front=Q.rear。 • 因此,只凭Q.front=Q.rear还无法区分是满还是空。 • 如何判定队满还是空?是循环队列要解决的新问题。 Q.front Q.rear 有一个空位置的队列

  31. 方法一 :用一个计数变量来记载队列中的元素个数。 • 初始化队列时c=0; • 当入队时,计数变量+1( c=c+1 ) • 当出队时,计数变量-1 (c=c-1) • 当计数变量=maxsize时,队满 • 当计数变量=0时,队空 • 方法二:设一个标志位用来区别队列是空还是满。 • 初始化队列时:Q.front=Q.rear,标志位为false • 入队后,使Q.front=Q.rear,则置标志位为true • 出队后,将标志位置为false • 当Q.front=Q.rear, 且标志位为true时,队满。 • 当Q.front=Q.rear, 但标志位为false时,队空。 • 其他为非空非满。

  32. 方法三:牺牲一个元素空间,来区别队空或队满。方法三:牺牲一个元素空间,来区别队空或队满。 入队前,先判Q.rear+1是否等于Q.front, 若是则为队满。 而当Q.front=Q.rear时,为队空。 前例:当J7入队后,就认为队已满, 而当J8再要入队时,就拒绝入队。

  33. #define MAXQSIZE 100 typedef struct{ QElemType *base; int front; int rear; }SqQueue; • Status InitQueue(SqQueue &Q){ • Q.base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType)); • if (!Q.base) exit(OVERFLOW); • Q.front = Q.rear = 0; • return OK; • }

  34. 循环队列及其实现 int QueueLength(SqQueue Q){ return (Q.rear – Q.front + MAXQSIZE) % MAXQSIZE; } Status EnQueue(SqQueue &Q, QElemType e){ if ((Q.rear + 1) % MAXQSIZE == Q.front) return ERROR; Q.base[Q.rear] = e; Q.rear = (Q.rear +1) % MAXQSIZE; return OK; } Status DeQueue(SqQueue &Q, QElemType &e){ if (Q.front == Q.rear) return ERROR; e = Q.base[Q.front]; Q.front = (Q.front + 1) % MAXSIZE; return OK; }

  35. 队列的表示和实现 • 队列的链式表示---链队列 • 用链表表示队列,附设队首指针front和队尾指针rear typedef struct { QElemType data; struct QNode *next; }QNode, *QueuePtr; typedef struct{ QueuePtr front; QueuePtr rear; }LinkQueue; LinkQueue Q; data next Q.front 头结点 队首 Q.rear 队尾

  36. 链队列的实现 Status InitQueue(LinkQueue & Q){ Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode)); if (!Q.front) exit(OVERFLOW); Q.front->next = NULL; return OK; } Status DestroyQueue(LinkQueue & Q){ while(Q.front){ Q.rear = Q.front->next; free(Q.front); Q.front = Q.rear; } return OK; }

  37. 链队列的实现 Status EnQueue(LinkQueue & Q, QElemType e){ p = (QueuePtr)malloc(sizeof(QNode)); if (!p) exit(OVERFLOW); P->data = e; p->next = NULL; Q.rear->next = p; Q.rear = p; return OK; } Status DeQueue(LinkQueue & Q, QElemType &e){ if (Q.front == Q.rear) return ERROR; p = Q.front->next; e = p->data; Q.front->next = p->next; if (Q.rear == p) Q.rear = Q.front; //当最后一个元素被删除后, //队尾指针将丢失,故需对队尾指针重新赋值 free(p); return OK; }

  38. Q.front Q.front Q.front Q.front Q.rear Q.rear Q.rear Q.rear 链队列的实现 空队 元素x入队 x 元素y入队 x y 元素x出队 x y

  39. 队列的应用 • 操作系统中的作业、进程排队 • 银行业务模拟 • 等等

More Related