1 / 95

第 3 章 栈、队列和数组

第 3 章 栈、队列和数组. 3.1 栈 3.2 队列 3.3 数组和广义表. 3.1.1 栈的定义和运算. Return. 出栈. 入栈. 栈顶 top. an. a2. 栈底 bottom. a1. 图 3-1 栈的示意图. 1 .栈的定义

aden
Download Presentation

第 3 章 栈、队列和数组

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. 第 3 章 栈、队列和数组 3.1 栈 3.2 队列 3.3 数组和广义表

  2. 3.1.1 栈的定义和运算 Return 出栈 入栈 栈顶 top an a2 栈底 bottom a1 图3-1栈的示意图 1.栈的定义 栈(stack)是一种只允许在一端进行插入和删除的线性表,它是一种操作受限的线性表。在表中只允许进行插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。栈的插入操作通常称为入栈或进栈(push),而栈的删除操作则称为出栈或退栈(pop)。当栈中无数据元素时,称为空栈。 栈是按照后进先出(LIFO)的原则组织数据的,因此,栈也被称为“后进先出”的线性表。 ...

  3. 2.栈的运算 1.初始化栈init_stack(S) 操作结果:构造一个空栈S。 2.判断栈是否为空stack_empty(S) 初始条件:栈S已存在 操作结果:若栈S为空栈,则返回TRUE;否则,返回FALSE。 3.取栈顶元素值stack_top(S,x) 初始条件:栈S已存在且非空 操作结果: 用x返回S的栈顶元素

  4. 4.入栈 push_stack(S,x) 初始条件:栈S已存在 操作结果:插入元素x为新的栈顶元素 5.出栈 pop_stack(S) 初始条件:栈S已存在且非空 操作结果: 返回S的栈顶元素 6.判断栈是否已满 stack_full(S) 初始条件:栈S已存在 操作结果:栈S已满返回TRUE;否则返回FALSE。

  5. 3.1.2 顺序栈 顺序栈是用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时由于栈的操作的特殊性,还必须附设一个位置指针top(栈顶指针)来动态地指示栈顶元素在顺序栈中的位置。 通常以top=-1表示空栈。

  6. data a1 a2 a3 a4 … an seqstack top 顺序栈的存储结构定义 #define maxsize 50 typedef struct stack { elementtype data[maxsize];/*用来存放栈中元素的一维数组*/ int top;/*用来存放栈顶元素的下标*/ }seqstack;

  7. S->top=-1 S->top=0 S->top=4 S->top=2 下图3-2展示了顺序栈入栈、出栈中数据元素与栈顶指针的变化。 E D C C B B A A A (a) (b) (c) (d) (a)空栈;(b)插入元素A后;(c)插入元素B、C、D、E后;(d)删除元素E、D后 seqstack *S;

  8. 顺序栈运算实现 1.初始化: void init_stack(seqstack * S){S->top = -1;} 2. 判栈空: BOOL stack_empty(seqstack S){return (S.top == -1);} 3. 判栈满: BOOL stack_full(seqstack S){ if ( S.top == maxsize-1 ) return(TRUE); else return FALSE; } 4. 读栈顶元素:void stack_top(seqstack * S,elementtype &x){ if ( S -> top == -1 ) error(“栈空”); else x=S->data[s->top]; }

  9. 5.入栈操作 void push_stack(seqstack *S, elementtype x) //将元素x插入到栈S中,作为S的新栈顶 { if (S->top== maxsize -1) error("栈满"); else { S->top++; S->data[S->top]=x; } data top you 2 min 1 push_stack(S,’you’) xx 0

  10. 6.出栈操作 void pop_stack(seqstack *S,elementtype &x) {/*若栈s不为空,则删除栈顶元素*/ { if(s->top<0) error("栈空,不能删除"); else { x=S->data[S->top]; S->top- -; } data top min 2 you 1 xx 0 x=‘min’

  11. 双向栈在一维数组中的实现 自由区 0 Stack_Size -1 lefttop rightto 图3-3 两个栈共享邻接空间 栈的共享中最常见的是两栈的共享。假设两个栈共享一维数组S->elem[Stack_Size],则可以利用栈的“栈底位置不变,栈顶位置动态变化”的特性,两个栈底分别为0和Stack_Size-1,而它们的栈顶都往中间方向延伸。因此,只要整个数组S->elem[Stack_Size]未被占满,无论哪个栈的入栈都不会发生上溢。

  12. 3.1.3 链栈 栈也可以采用链式存储结构表示,这种结构的栈简称为链栈。 一个链栈(不带头结点)可由栈顶指针top唯一确定,当top为NULL时,是一个空栈。 链栈的C语言定义为: typedef struct StackNode { elementtype data; Struct StackNode *next; }StackNode,*LinkStack;

  13. 链栈示意图 p top C top B B top A ^ A A ^ ^ (a) (b) (c) 图3-6 链栈的存储结构图 (a)含有两个元素A、B的栈;(b)插入元素C后的栈;(c)删除元素C、B后的栈

  14. 栈底 …... top ^ data next top top 栈底 p x …... ^ q top 栈底 top …... ^ • 入栈过程 • 出栈过程

  15. 链栈入栈、出栈的算法实现 top p 1)入栈操作void Push (LinkStack &top, elementtype e){ /*将元素e压入链栈top中*/p=(LinkStack)malloc(sizeof(StackNode)); if(!p) error("OVERFLOW"); p->data =e; p->next=top; top=p; } e

  16. p top b c d ^ 2)出栈操作void Pop(LinkStack &top,elementtype &e){ /*从链栈top中删除栈顶元素*/ if (top= =NULL) error(“空栈”); else{ p=top; top=top->next; e=p->data; free(p); } e=‘a’ a

  17. 练习 四个元素入栈的顺序是A,B,C,D.不可能的出栈顺序是: (a)ABCD (b)DCBA (c)BADC (d)ADCB (e)DACB

  18. 3.1.4 栈的应用举例 • 数制转换 • 2. 递归函数的栈的使用

  19. 1. 数制转换 将一个非负的十进制整数N转换为另一个等价的基为B的B进制数的问题,很容易通过"除B取余法"来解决。【例】将十进制数13转化为二进制数。 2 13 1 6 2 3 2 0 1 2 1 0 1

  20. S->top 算法实现 2 13 1 6 2 void conversion(int N,int B){//假设N是非负的十进制整数,输出等值的B进制数init_stack(S);   while(N){  //从右向左产生B进制的各位数字,并将其进栈push_stack( S,N%B);         N=N/B;       }   while(!stack_empty( S)){  //栈非空时退栈输出pop_stack( S,e);       cout<<e;              }  } 3 0 2 1 1 2 e= 1 0 1 1 0 1

  21. 2、递归函数的栈的使用 递归过程及其实现 递归:函数直接或间接的调用自身叫递归。 实现:建立递归工作栈 例题 void print(int w) { int i; if ( w!=0) { print(w-1); for(i=1;i<=w;++i) printf(“%3d,”,w); printf(“\n”); } } 调用print(3) 运行结果: 1, 2,2, 3,3,3,

  22. w w 1 0 w print(0); 2 返回 w (4)输出:1 (4)输出:1 主程序 print(1); 3 (3) 输出:2, 2 (3) 输出:2, 2 print(2); w=3; (2) 输出:3, 3, 3 (2) 输出:3, 3, 3 print(w) (1) (1) S->top (3)w=1 (1 ) 3 (4)w=0 (2)w=2 (3)w=1 (2) 2 (1)w=3 (2)w=2 (1) 3 top (1)w=3 top (4) 0 top (3) 1 (3) 1 top (2)w=2 (2) 2 (2) 2 (1)w=3 (1) 3 (1) 3 (1)w=3 S->top S->top S->top 结束 递归调用执行情况如下: if ( w!=0) { print(w-1); for(i=1;i<=w;++i) printf(“%3d,”,w); printf(“\n”); }

  23. Return 3.2 队 列 3.2.1 队列的定义和运算 3.2.2 链队列 3.2.3 顺序队列(循环队列)

  24. 3.2.1 队列的抽象数据类型定义 在日常生活中队列很常见,如,我们经常排队购物或购票. 队列在计算机系统中的应用也非常广泛。例如:操作系统中的作业排队。

  25. 1、队列的定义及特点 出队 a1 a2 a3…………………….an 入队 队列Q=(a1,a2,……,an) front rear • 定义:允许在线性表的一端插入,另一端进行删除操作的线性表称为队列.插入的一端为队尾,删除的一端为队头。 • ※队尾(rear)——允许插入的一端 • ※队头(front)——允许删除的一端 • 队列特点:先进先出(FIFO)

  26. 2 队列运算 (1)初始化:init_queue(Q); (2)判队空:queue_empty(Q); (3)取队头:queue_front(Q,x); (4)入队:enqueue(Q,x); (5)出队:outqueue(Q); (6)判队满:queue_full(Q);

  27. 3.2.2 链队列 1、存储结构定义: typedef struct { elementtype data; struct node * next; } node; typedef struct { node *front,*rear; } linkqueue ; Q->front x y z Q->rear linkqueue Q;

  28. front ^ Q rear 2 .链队列运算实现(初始化) (1)初始化队列: void init_queue(linkqueue &Q) { Q.front = (node *)malloc(sizeof(node)); Q.front -> next = NULL; Q.rear = Q.front; }

  29. 2.链队列运算实现 (2)判队空: BOOL queue_empty( linkqueue Q) {return ( Q.front == Q.rear );} (3)取队头: void queue_front( linkqueue Q, elementtype &x) { if ( empty(Q) ) error(“队列空”); else x = Q.front -> next -> data; }

  30. p (4)入队列: void En_queue(linkqueue *Q, elementtype e) {p= (node *)malloc(sizeof(node)); if(!p) exit(OVERFLOW); p->data = e; p->next=null; Q->rear->next =p; Q->rear =p;} e Q->front m n Q->rear

  31. p x (5)出队列 void Out_queue(linkqueue *Q,elementtype &e) { if Q->front = = Q->rear error("队列为空"); //队列Q为空队列 p=Q->front->next; e=p->data; Q->front->next = p->next if(Q.rear ==p) Q.rear=Q.front; //若Q只有一个结点 free(p);} e=‘x’ Q.front z y Q.rear

  32. 3.2.3 顺序队列 Q.rear Q.front 1.顺序队列的存储结构定义: #define maxsize 100//最大队列长度 typedef struct{ elementtype data[maxsize]; int front,rear ; }seqqueue; Seqqueue Q; 5 4 3 2 1 0 Q.rear 5 4 3 2 1 0 k i g s Q.front Q.rear = 4; Q.front = 0 Q.rear= =Q.front= = 0 初始化队列为空

  33. 队列的顺序存储结构 5 5 5 5 Q.rear 4 4 4 4 Q.rear Q.rear Q.rear Q.rear Q.rear 3 3 3 3 Q.front Q.front Q.front 2 2 2 2 1 1 1 1 Q.front 0 0 0 0 教材上约定: rear指示队尾元素; front指示队头元素上一位置。 道理相同! Q.rear J6 J5 Q.front J4 J3 J3 J2 J2 Q.front=0 Q.rear=0 J1 J1 Q.front J4,J5,J6入队 J1,J2,J3出队 队空 J1,J1,J3入队 设两个指针Q.front,Q.rear,约定: rear指示队尾元素下一位置; front指示队头元素 初值Q.front=Q.rear=0 空队列条件:Q.front==Q.rear 入队列:Q.data[Q.rear++]=x; 出队列:x=Q.data[Q.front++];

  34. 2.顺序队列存储中的缺点: Q.rear Q.rear Q.front n 5 4 3 2 1 0 w 5 4 3 2 1 0 l j Q.front Q.rear = MAXSIZE 队列满 Q.rear = = Q.front 为空,但不能使用

  35. 解决缺点的方法: 1、采用平移元素的方法: Q.rear z y z x rear y Q.front 2、将整个队列作为循环队列处理: x • 入队列: front z Q.rear=(Q.rear +1)%MAXSIZE 4 3 2 1 0 z y y x x Q.front Q.front Q.rear m Q.rear

  36. (2) 出队列: 4 3 2 1 0 Q.rear m Q.front Q.front=(Q.front + 1)% MAXSIZE

  37. (3) 在Q.rear和Q.front可以循环指示时,如何判断队列的空和满: 令:Q.rear = = Q.front 为空 令:(Q.rear + 1)%MAXSIZE = = Q.front为满 c Q.rear b d Q.rear w v Q.front v Q.front q c Q.rear Q.front 队列为满 队列为空

  38. 3.循环队列的重要算法: (1)构造一个空队列Q void init_queue (seqqueue Q) { Q.front = Q.rear=0 }

  39. (2)求队列Q的长度 int QueueLength (SqQueue Q) {return ((Q.rear-Q.front+MAXSIZE)%MAXSIZE); }

  40. (3) 循环队列的入队列: void En_queque(seqqueque *Q, elementtype e) { if ((Q.rear +1)%MAXSIZE = = Q.front) error("队列满"); Q.rear= (Q.rear +1)% MAXSIZE; Q->data[Q.rear]=e; }

  41. (4)循环队列出队列: void Out_queue (seqqueque *Q, elementtype &e) { if (Q->front = = Q->rear) error(“队空”); else {Q->front= (Q->front + 1 ) % MAXSIZE; e=Q->data[Q->front]; } }

  42. 0 3.2.4 队列的应用 例:打印杨辉三角 • 1 • 1 • 1 2 1 • 3 3 1 • 1 4 6 4 1 • 5 10 10 5 1 • 1 6 15 20 15 6 1 • 1 7 21 35 35 21 7 1

  43. 3. 队列的应用 • 银行业务模拟程序 • 舞伴问题—假设在周末舞会上,男士们和女士们进入舞厅时,各自排成一队。跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。若两队初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。现要求写一算法模拟上述舞伴配对问题。

  44. 4、图的深度优先遍历—栈的应用(见第7章图)4、图的深度优先遍历—栈的应用(见第7章图) 5、图的广度优先遍历—队列的应用(见第7章图)

  45. Return 3.3 数组和广义表 3.3.1 数组的定义和运算 3.3.2 数组的顺序表示和实现 3.3.3 矩阵的压缩存储 3.3.4 广义表的定义 3.3.5 广义表的存储结构

  46. 3.3.1 数组的定义和运算 数组的定义 数组是大家都已经很熟悉的一种数据类型,几乎所有高级语言程序设计中都设定了数组类型。 1.一维数组 一维数组可以看成是一个线性表或一个向量(第2章已经介绍),它在计算机内是存放在一块连续的存储单元中,适合于随机查找。。 (a0,a1,a3,……,an-1)

  47. 2.二维数组 二维数组中的每一个元素最多可有两个直接前驱和两个直接后继(边界除外),故是一种典型的非线性结构。例如,设A是一个有m行n列的二维数组,则A可以表示为: 二维数组可以看成是这样一个定长的线性表, 它的每个数据元素也是一个定长的线性表。

  48. 二维数组可以看成是一个线性表 A=(a0,a1,……,an-1) 其中每个数据元素aj是一个列向量形式的线性表 A=(a0 , a1 , …… ,an-1)

  49. 或者,可以看成是一个线性表 A=(a0,a1,……,am-1) 其中每个数据元素aj是一个行向量形式的线性表 A =( a0 , a1 , . . . am-1 ai0 aij-1 aij )

  50. 3.n维数组 同理,n维数组可以看成是这样一个定长的线性表, 它的每个数据元素也是n-1维的数组。 n维数组最多可有n个直接前驱和n个直接后继,故n维数组是一种非线性结构。

More Related