第2章:线性数据结构
1、实现顺序栈的判空操作
/**********
【题目】试写一算法,实现顺序栈的判空操作
StackEmpty_Sq(SqStack S)。
顺序栈的类型定义为:
typedef struct {
ElemType *elem; // 存储空间的基址
int top; // 栈顶元素的下一个位置,简称栈顶位标
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack; // 顺序栈
***********/
Status StackEmpty_Sq(SqStack S)
/* 对顺序栈S判空。 */
/* 若S是空栈,则返回TRUE;否则返回FALSE */
{
if(S.top != 0) return FALSE;
else return TRUE;
}
2、实现顺序栈的取栈顶元素操作
/**********
【题目】试写一算法,实现顺序栈的取栈顶元素操作
GetTop_Sq(SqStack S, ElemType &e)。
顺序栈的类型定义为:
typedef struct {
ElemType *elem; // 存储空间的基址
int top; // 栈顶元素的下一个位置,简称栈顶位标
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack; // 顺序栈
***********/
Status GetTop_Sq(SqStack S, ElemType &e)
/* 取顺序栈S的栈顶元素到e,并返回OK; */
/* 若失败,则返回ERROR。 */
{
if(S.top == 0) return ERROR;
e = S.elem[S.top - 1];
return OK;
}
3、实现顺序栈的出栈操作
/**********
【题目】试写一算法,实现顺序栈的出栈操作
Pop_Sq(SqStack &S, ElemType &e)。
顺序栈的类型定义为:
typedef struct {
ElemType *elem; // 存储空间的基址
int top; // 栈顶元素的下一个位置,简称栈顶位标
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack; // 顺序栈
***********/
Status Pop_Sq(SqStack &S, ElemType &e)
/* 顺序栈S的栈顶元素出栈到e,并返回OK;*/
/* 若失败,则返回ERROR。 */
{
if(S.top == 0) return ERROR;
//这里--S.top既可以找到栈顶元素
//也可以实现栈顶位标减1
e = S.elem[--S.top];
return OK;
}
4、重新定义顺序栈,构建初始容量和扩容增量分别为size和inc的空顺序栈S
/**********
【题目】若顺序栈的类型重新定义如下。试编写算法,
构建初始容量和扩容增量分别为size和inc的空顺序栈S。
typedef struct {
ElemType *elem; // 存储空间的基址
ElemType *top; // 栈顶元素的下一个位置
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack2;
***********/
Status InitStack_Sq2(SqStack2 &S, int size, int inc)
/* 构建初始容量和扩容增量分别为size和inc的空顺序栈S。*/
/* 若成功,则返回OK;否则返回ERROR。 */
{
//考虑size、inc值不合理的情况
if(size <= 0 || inc <= 0) return ERROR;
//这里开辟的空间是ElemType而不是SqStack2
S.elem = S.top = (ElemType *)malloc(sizeof(ElemType)*size);
if(S.elem == NULL) return ERROR;
S.size = size;
S.increment = inc;
return OK;
}
5、重新定义顺序栈,实现顺序栈的判空操作
/**********
【题目】若顺序栈的类型重新定义如下。试编写算法,
实现顺序栈的判空操作。
typedef struct {
ElemType *elem; // 存储空间的基址
ElemType *top; // 栈顶元素的下一个位置
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack2;
***********/
Status StackEmpty_Sq2(SqStack2 S)
/* 对顺序栈S判空。 */
/* 若S是空栈,则返回TRUE;否则返回FALSE */
{
//同地址为空栈
if(S.elem != S.top)return FALSE;
else return TRUE;
}
6、重新定义顺序栈,实现顺序栈的入栈操作
/**********
【题目】若顺序栈的类型重新定义如下。试编写算法,
实现顺序栈的入栈操作。
typedef struct {
ElemType *elem; // 存储空间的基址
ElemType *top; // 栈顶元素的下一个位置
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack2;
***********/
Status Push_Sq2(SqStack2 &S, ElemType e)
/* 若顺序栈S是满的,则扩容,若失败则返回ERROR。*/
/* 将e压入S,返回OK。 */
{
int i = 0;
ElemType *temp = S.elem;
while(temp != S.top)/*找到两者地址相等,此时i表示有多少个元素,类似顺序栈1中top的作用*/
{
i++;
temp ++;
}
temp = S.elem;
if(i == S.size){
/*内存重新分配,之所以用另一个变量来存储有以下原因:内存分配有几种情况(这个函数不太安全)*/
/*
1、若已有内存后面还存在increment个连续地址,则把后面的increment内存纳入数组中,此时temp的地址跟S.elem一致;
2、如果原来的内存后面没有足够的空闲空间用来分配,那么会在栈中重新找一块newSize大小的内存,然后把已有内存的数据复制到新内存中(数据已移动),把新内存的地址返回给temp,而老块内存重新放回栈中
3、如果没有足够可用的内存用来完成重新分配(扩大原来的内存块或者分配新的内存块),则返回null而原来的内存块保持不变。
4、如果S.elem为null,则realloc()和malloc()类似。分配一个newsize的内存块,返回一个指向该内存块的指针。
5、如果newsize大小为0,那么释放S.elem指向的内存,并返回null。
综上,这就是为什么重新分配内存时要定义一个临时指针,并最后把该指针指向NULL
*/
temp = (ElemType *)realloc(S.elem,sizeof(ElemType)*(S.size
+S.increment));
if(temp == NULL)return ERROR;
S.elem = temp;
/*注意,之前这里如果没有下面这行代码,
若如果原本已有六个元素(size),
则测试不通过,经调试发现:
那个temp在内存重新分配后不一定是指向S.elem的值
所以这时候S.top跟S.elem的地址差距甚大
*/
S.top = S.elem + i;
S.size += S.increment;
temp = NULL;
}
*(S.top ++) = e;
return OK;
}
7、重新定义顺序栈,实现顺序栈的出栈操作
/**********
【题目】若顺序栈的类型重新定义如下。试编写算法,
实现顺序栈的出栈操作。
typedef struct {
ElemType *elem; // 存储空间的基址
ElemType *top; // 栈顶元素的下一个位置
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack2;
***********/
Status Pop_Sq2(SqStack2 &S, ElemType &e)
/* 若顺序栈S是空的,则返回ERROR; */
/* 否则将S的栈顶元素出栈到e,返回OK。*/
{
if(S.elem == S.top) return ERROR;
e = *(--S.top);
return OK;
}
8、借助辅助栈,复制顺序栈S1得到S2
/**********
【题目】试写一算法,借助辅助栈,复制顺序栈S1得到S2。
顺序栈的类型定义为:
typedef struct {
ElemType *elem; // 存储空间的基址
int top; // 栈顶元素的下一个位置,简称栈顶位标
int size; // 当前分配的存储容量
int increment; // 扩容时,增加的存储容量
} SqStack; // 顺序栈
可调用顺序栈接口中下列函数:
Status InitStack_Sq(SqStack &S, int size, int inc); // 初始化顺序栈S
Status DestroyStack_Sq(SqStack &S); // 销毁顺序栈S
Status StackEmpty_Sq(SqStack S); // 栈S判空,若空则返回TRUE,否则FALSE
Status Push_Sq(SqStack &S, ElemType e); // 将元素e压入栈S
Status Pop_Sq(SqStack &S, ElemType &e); // 栈S的栈顶元素出栈到e
***********/
Status CopyStack_Sq(SqStack S1, SqStack &S2)
/* 借助辅助栈,复制顺序栈S1得到S2。 */
/* 若复制成功,则返回TRUE;否则FALSE。 */
{
//先初始化
Status status = InitStack_Sq(S2,S1.size,S1.increment);
if(status == OVERFLOW)return FALSE;
if(S1.top == 0) return TRUE;
for(int i = 0;i < S1.top;i ++){
Push_Sq(S2,S1.elem[i]);
}
DestroyStack_Sq(S1);
return OK;
}
9、求循环队列的长度
/**********
【题目】试写一算法,求循环队列的长度。
循环队列的类型定义为:
typedef struct {
ElemType *base; // 存储空间的基址
int front; // 队头位标
int rear; // 队尾位标,指示队尾元素的下一位置
int maxSize; // 最大长度
} SqQueue;
***********/
int QueueLength_Sq(SqQueue Q)
/* 返回队列Q中元素个数,即队列的长度。 */
{
//保留一个内存空间不用的方案
//有两种情况:rear在front后;rear在front前
return (Q.rear-Q.front+Q.maxSize)%Q.maxSize;
}
10、设置一个标志域tag标记队列的空或满,编写与此结构相应的入队列和出队列的算法
/**********
【题目】如果希望循环队列中的元素都能得到利用,
则可设置一个标志域tag,并以tag值为0或1来区分尾
指针和头指针值相同时的队列状态是"空"还是"满"。
试