540 likes | 893 Views
第 3 单元 线性数据结构(二). 1.3 栈和队列 (P32~P46) 1.4 串和数组 (P47~P55). 1.3 栈和队列 一、栈的逻辑结构和运算. 1. 堆栈 (Stack) 概念 1) 只允许在同一端进行插入和删除操作的特殊线性表。 2) 允许进行插入和删除操作的一端称为栈顶,另一端为栈底; 3) 栈底固定,而栈顶浮动; 4) 特点:后进先出( LIFO )。 5) 空栈 : 元素个数为零的栈。. 2. 栈的基本运算. Setnull(Stack) 置栈为空;
E N D
第3单元 线性数据结构(二) • 1.3 栈和队列 (P32~P46) • 1.4 串和数组 (P47~P55)
1.3 栈和队列一、栈的逻辑结构和运算 • 1.堆栈(Stack)概念 • 1) 只允许在同一端进行插入和删除操作的特殊线性表。 • 2) 允许进行插入和删除操作的一端称为栈顶,另一端为栈底; • 3) 栈底固定,而栈顶浮动; • 4) 特点:后进先出(LIFO)。 • 5) 空栈:元素个数为零的栈。
2. 栈的基本运算 • Setnull(Stack) 置栈为空; • Empty(Stack) 判断栈是否为空; • Push(Stack,x) 进栈---在栈顶插入元素x • Pop(Stack) 出栈---删除栈顶元素; • Gettop(Stack) 取栈顶元素 • 顺序栈: 采用顺序存储结构的栈 • 链栈: 采用链式存储结构的栈
例1-10 • 有三个元素的进栈序列是1,2,3。写出可能的出栈序列。 出栈序列 操作序列 1 2 3 s x s x s x 1 3 2 s x s s x x 2 1 3 s s x x s x 2 3 1 s s x s x x 3 2 1 s s s x x x
3.与堆栈操作有关的术语 • 1) 栈顶指针TOP • 用来指出栈顶元素所在的位置 • 2) 栈空条件: top = -1 • 3) 栈满条件: top = MAXSIZE-1 • 4) 上溢 • 栈满后再进行入栈操作,产生上溢。 • 5) 下溢 • 栈已空,再执行出栈操作,产生下溢。
二、栈的存储结构之一:顺序栈 • 1) 采用顺序存储结构的栈 • 2) 实现: 使用一维数组 • 3) 栈顶位置随进栈和出栈而变化。 • 4) C语言描述: • #define MAXSIZE n • int stack[MAXSIZE]; • int top = -1; /*下标从0开始*/
栈操作举例 TOP a1 a2 …… an MAXSIZE-1 栈顶 栈底 1. A B C 2. (空栈) (A、B、C进栈) top=2 top=-1 A B C D E F 4. 3. A B top=MAXSIZE-1 (C出栈) top=1 (栈满)
算法1-8 进栈算法 • step1 判断栈满否, • 若满,显示溢出信息,停止执行 • 否则,执行step 2; • Step2 栈顶指针top上移(加1); • Step3 在top所指的位置插入元素x。
算法1-8 进栈算法程序 • push(int x) • { if(top == MAXSIZE -1) • { printf(“栈上溢!\n”); • exit (1); } • else • { top ++; /* 栈指针加 1 */ • stack[top]=x ; /* 元素 x进栈 */ • } • }
算法1-9 出栈算法 • step1 判断栈是否为空; • 若空,输出下溢信息,并停止执行; • 否则,执行step2; • step2 弹出(删除)栈顶元素; • step3 栈顶指针top下移(减1)。 • step4 返回栈顶元素
算法1-9 出栈算法程序 • pop( ) • { int x; • if( top = = -1) • { printf(“栈下溢\n”); exit(1);} • else • { x = stack [top];top - - ; } • return x ; • }
5) 两栈共享一个栈空间 • 两栈共享:两个栈底分别设在两端,栈顶指针top1和top2相对中间位置移动,栈间分界线不定。 • a. • b. • c. 空栈 top1 =0 top2=MAXSIZE-1 栈1、栈2均有元素 top1 top2 栈满 top1 top2
二、栈的存储结构之二:链栈 • 1. 顺序栈的问题 • 1) 最多实现2个栈的共享 • 2) 不能用于最大空间事先不知的情况 • 解决方法: 链栈
链栈存储结构 • 2. 链栈存储结构的C语言描述: • struct snode • { intdata; • struct snode *link; } ; • typedef struct snode SNODE ; • SNODE *top; • 链栈空:top = NULL • 链栈满: t = NULL /*t为申请的结点 • 申请结点失败
链栈示意图 ai top 栈顶 …... a3 a2 栈底 a1 NULL
算法1-10 进栈操作算法 • step1 申请一个链栈结点, • 若 t=NULL,则链满; • 否则,执行step2 ; • step2 在top所指结点之前插入新结点,并将top指向新申请的结点t。
算法1-10 进栈操作程序 • push(int x) • { SNODE *t; • t=(SNODE * )malloc(sizeof(SNODE)); • if (t = = NULL ) • { printf(“栈已满\n”); • exit(1); } • else • { t->data = x; t->link = top;top= t; } • }
算法1-11 出栈操作算法 • step1 若链栈空,输出栈溢出信息; • 否则,执行step 2。 • step2 删除top所指结点,并使top • 指向被删除结点的后继结点 • step3 释放被删除结点的存储空间 • step4 返回栈顶元素
算法1-11 出栈操作程序 • pop( ) • { SNODE *p; int x; • if ( top = = NULL ) • { printf(“栈下溢\n”); exit(1); } • else • { p=top; top=top->link; • x = p ->data ; free(p) ; } • return x; • }
三、队列 • 1. 队列概念 • 1) 只允许在表的一端进行删除操作,在表的另一端进行插入的特殊线性表。 • 2) 队尾(rear):进行插入操作的一端 • 3) 队头(front):进行删除操作的一端 • 4) 特点:先进先出(FIFO)。 入队 插入 出队 删除 a1 a2 … an front rear
2、队列的操作 • Setnull(Queue) 清空队列 • Empty(Queue) 判断队列是否为空 • Addqueue(Queue,x) 入队(插入) • Delqueue(Queue) 出队(删除) • Frontqueue(Queue) 取队头元素
四、队列的顺序存储 • 1. 定义空队列: 用一维数组 • #define MAXSIZE n • int queue[MAXSIZE]; • int front=-1, rear=-1; • front:队头指针; • rear:队尾指针; • 空队列:front = rear • 满队列:rear = MAXSIZE-1
2.入队出队操作 • 约定: • front: 总是指向队头元素的前一个位置; • rear: 总是指向队尾元素的位置 • 结果: 对任何元素,操作都一样 • 出队: 入队: • front = front + 1 ; rear=rear+1; • x = queue [front]; queue[rear]=x; a1 a2 … an front rear
入队时,rear在变 front rear A B C D E rear front 举例:顺序队列的入队、出队操作 • 1)空队列 • 2)A、B、C、D、E入队 • 3)A、B、C出队 出队时,front在变 D E rear front
4)F、G、H入队 • 5)D、E、F、G、H出队,出现假“溢出” D E F G H rear front rear front
3. 假溢出 • 假溢出 • 队列是空的情况下出现的溢出。 • 解决方法 • 1) 整个队列左移,费时 • 2) 队列的首尾相连--循环队列 • 目的 • 队列中真正没有空位置时,才产生溢出。
2.入队(队尾指针移动) rear=(rear+1)%MAXSIZE; 等价于: if (rear+1>=MAXSIZE) rear = 0; else rear=rear+1; 4、循环队列 • 1.出队(队头指针移动) • front=(front+1)%MAXSIZE; • 等价于: • if • (front+1>= MAXSIZE) • front = 0 ; • else • front=front+1;
循环队列队空、队满条件 3 2 1 • 约定: • 队头指针指示的 • 结点不用于存储 • 元素,只作标志 • 即保留一个节点 • 队空条件 • front = rear ; • 队满条件 • front = (rear+1) % MAXSIZE 空 0 front MAXSIZE -1 ... rear 2 1 a3 满 a2 0 a1 ... i ai MAXSIZE -1 ... rear i+1 front
算法1-12:循环队列入队算法 • step1 判别队列是否已满; • step2 队尾指针后移一个位置,将新结点元素值存入当前结点单元。 • 程序(略)
算法1-13 循环队列出队算法 • step1 判别队列是否为空;若空,则显示‘下溢’; • step2 队头指针后移一个位置。 • step3 返回队头元素 • 程序(略)
五.队列的链式存储结构 • 用带头结点的单链表作为队列的链式存储结构。 • C语言描述 • struct qnode • { int data ; • struct qnode * next;}; • typedef struct qnode QNODE ; • QNODE *front , *rear;
链队列的表示 • 链队列满: T = = NULL 没有存储空间 • 链队列空: front = =rear • 空队列: • 非空队列: front NULL rear front a1 a2 ... an NULL rear
算法1-14 链队列的入队算法 • step1 申请建立一个新结点T; • step2 判别T是否为NULL;若是,表示 • 队列已满; • step3 若非空,将T插入链中,修改rear • 指针。
链队列的入队操作 • addqueue(int x) • { QNODE *t; • t = (QNODE*)malloc(sizeof(QNODE)); • if ( t = = NULL) • { printf(”无可用空间\n”);exit(1);} • else • { rear -> next = t; rear = t; • t ->data = x; t ->next = NULL ; } • }
an ^ 链队列的出队操作 • 两种情况: • 队列长度为1,修改头结点指针域和队尾指针。 • 队列长度大于1,只修改头结点指针域 front Queue anNULL rear T front Queue NULL rear ... front a1 a2 anNULL Queue rear ... a1 a2 ^ anNULL Queue front rear
栈的应用----例1: 递归过程 • 计算5的阶乘(5!=5×4×3×2×1) • { if ( n = = 1) return (1); • else return ( n * f(n-1)); • } f(1)=1 top f(2) 2*f(1) f(2)=2*f(1) f(3) 3*f(2) f(3)=3*f(2) f(4) 4*f(3) f(4)=4*f(3) f(5) 5*f(4) f(5)=5*f(4)
栈的应用----例2:程序的嵌套调用 • main sub1 sub2 sub3 top sub2 sub1 main
1.4 串和数组 • 一. 串及其运算 • 1. 串的概念 • 1) 定义: 特殊的线性表,每个数据元素仅由单个字符组成, 如: A="very good" • 2) 串的长度: 串中的字符个数 • 3) 空串:串长度为0 • 4) 空格串: 组成串的元素都是空格 • 5) 子串:串中任意个连续字符组成的序列 • 6) 两个串的相等:
2.串的基本操作 • LENGTH(S) 求串S的长度 • SUBSTR(S,start,len) 求S的子串 • CONCAT(S1,S2) • S2联接在S1末尾 • INDEX(S1,S2) • 确定S2在S1中的位置 • REPLACE(S1,S2,S3) • 用S3替换串S1中所有与串S2相等且不重叠的子串
二.串的存储结构 • 1. 顺序存储结构 • 用连续存储单元存储串的字符序列 • 使用字编址方式时,设1字4个字节,则: • (1)非紧缩存储:一个字的存储单元中只存放1个字符。 • 特点:存储密度低,浪费空间 • (2) 紧缩存储 一个单元中存放4个字符; • 特点:节省空间 • 访问时要花费分离时间
2. 链表存储结构 • (1) 结点由数据域和指针域组成 • 图1-38 • (2) 指针域通常两个字节 • 若数据域占一个字节: • 有效存储密度=1/3 • 若数据域占四个字节: • 有效存储密度=4/6=2/3
3.堆存储结构 • 堆结构:一种动态存储结构,定义一个很大的连续空间和相应的指针结构。 • 指针用来指示串在堆中的位置 • 例如,a=‘BEI’,b=‘ JING’, c=‘’,d=‘SHANGHAI’; 串名串长 起始地址 B E I J I N G S H a 3 1 A N G H A I b 5 4 c 0 9 d 8 9
三.数组 • 1、数组的定义 • 数组是有限个数组元素的集合 • 数组中所有元素有相同的数据类型 • 每个数组元素值可以用数组名和一组下标值唯一的确定;
2.数组元素之间的关系 • m行n列二维数组可以看作是m个或n个一维数组: • Amxn = ((a11a12…a1n),(a21a22…a2n),.. • (am1am2…amn)) • 或: a11 a12 a1n • a21 a22 a2n • Amxn = • am1 am2 amn ... ... ... ...
3.数组的操作 • 两种基本的操作: • 给定下标,存取相应的数组元素; • 给定下标,修改相应数组元素的值。
4.数组的顺序存储结构 • 无论几维数组,在计算机中都是按一维数组来存放。 • 二维数组存放通常采用两种方式: • 按行优先顺序 • 按列优先顺序
1) 按行优先顺序存储 • 按行优先将数组看作若干个行向量 • 数组中的每个元素由元素的两个下标表达式唯一的确定。 • 地址计算公式: • LOC(aij)=LOC(a11)+(i-1)*n+(j-1))*L • L: 每个元素所占的存储单元
2)按列优先顺序存储 • 将数组看作若干个列向量。 • 数组中的每个元素由元素的两个下标表达式唯一的确定。 • 地址计算公式: • LOC(aij)=LOC(a11)+((j-1)*m+(i-1))*L • L: 每个元素所占的存储单元。
5.矩阵的压缩存储 • 压缩的含义: • 相同值的多个元素占用一个存储单元; • 零元素不分配存储单元。 • (1)特殊矩阵的压缩存储 • 特殊矩阵:值相同的元素或非零元素分布有一定规律的矩阵 • 压缩:将二维数组的元素压缩到一维数组 • 关键:两个数组的下标之间建立映象关系
例: 对称矩阵的压缩存储 • 对称矩阵的元素满足: • aij = aji 1 i ,j n • n*n 个元素压缩存放到n(n+1)/2 个单元的一维数组中。 • Aij在一维数组中的地址为: • i(i-1)/2+j 当ij • LOC(aij) = • j(j-1)/2+i 当i<j