0.PTA得分截图
1.本周学习总结
1.1 总结栈和队列内容
-
栈的存储结构及操作
- 栈只能在一端作插入、删除操作,即栈顶一端,具有后入先出的特性(Last In First Out,即LIFO),栈的存储结构分顺序存储结构、链式存储结构两种形式
- 栈的顺序存储结构,此时top初值为-1
typedef struct{ int data[MAXSIZE]; int top; //存放栈顶元素在数组中的下标 }SqStack;
- 栈的顺序存储结构的另一种描述,此时top定义为栈顶的上一个位置,即top初值为0
typedef struct SqStack { int *Data; //存储元素的数组 int top; //栈顶指针 int MaxSize; //堆栈最大容量 }SqStack,*Stack;
- 栈的链式存储结构
typedef struct StackNode { int data; //结点数据域 struct StackNode* next; //结点指针域 }StackNode,* Linktop; typedef struct LinkStack { Linktop top; //栈顶结点,定义了一个指向上个结构体的指针 int count; //元素个数 }LinkStack;
- 创建一个顺序栈
Stack CreateStack( int MaxSize ) { Stack S = (Stack)malloc(sizeof(struct SNode)); S->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType)); S->Top = 0; S->MaxSize = MaxSize; return S; }
- 顺序栈的入栈操作,此时top初值为-1
bool Push(Stack S, int X) { if (S->top == S->MaxSize-1) //若栈满,返回false { printf("Stack Full\n"); return false; } else { S->top++; //栈顶指针加一,元素入栈 S->Data[S->top] = X; } return true; }
- 顺序栈的入栈操作,此时top初值为0
bool Push(Stack S, ElementType X) { if (S->top == S->MaxSize) //若栈满,返回false { printf("Stack Full\n"); return false; } else { S->Data[S->top] = X; //元素入栈,栈顶指针加一 S->top++; } return true; }
- 顺序栈的出栈操作,此时top初值为-1
bool Pop(Stack S) { if (S->top == -1) //若栈为空,返回false { printf("Stack Empty\n"); return false; } else { S->top--; //栈顶元素出栈 } return true; }
- 顺序栈的出栈操作,此时top初值为0
bool Pop(Stack S) { if (S->top == 0) //若栈为空,返回false { printf("Stack Empty\n"); return false; } else { S->top--; //栈顶元素出栈 } return true; }
- 创建一个空链栈
LinkStack CreateStack() { LinkStack *p; p = new LinkStack; //给p创建空间 p->count = 0; p->top = NULL; return p; }
- 链栈的入栈操作
LinkStack Push(LinkStack p,int x) { if(p==NULL) return NULL; Linktop temp; temp = new StackNode; temp->data = x; temp->next = p->top; p->top = temp; p->count ++; return p; }
- 链栈的出栈操作
bool Pop(LinkStack &p) { Linktop temp; temp = p->top; if(p->top==NULL) { return false; } else { p->top = p->top->next; delete temp; p->count--; } return true; }
- 销毁链栈
void DestroyStack(LinkStack &s) { LiStack p=s,q=s->next; while (q!=NULL) { free(p); p=q; q=p->next; } delete p; //此时p指向尾结点,释放其空间 }
- C++中stack容器:使用该容器时需要包含相应的头文件
stack<int>st; //新建并初始化一个元素类型全为整型的栈st st.empty(); //判断栈是否为空,若为空,函数返回true;否则返回false st.top(); //访问栈顶元素 st.pop(); //栈顶元素出栈,出栈操作只是删除栈顶的元素,并不返回该元素 st.push(x); //把元素x入栈 st.size(); //访问栈中的元素个数
-
栈的应用
- 判断回文数时可借助栈来实现,初始化两个栈,分别存储最高位到最低位的各位数字以及最低位到最高位的各位数字,最后比对这两个栈是否一样
- 表达式符号配对时,可借助栈来实现,符合条件的元素入栈后可与其余元素进行配对
- 浏览网页后要执行回退操作时,使用的数据结构也是栈,遵循着后入先出的原则
-
队列的存储结构及操作
- 队列仅允许在其一端进行插入( 队尾(rear)),而在另一端进行删除(队首(front)),与栈相反,队列是一种先进先出(FIFO)的线性表
- 队列的顺序存储结构,用数组存储队列元素
typedef struct { int data[MAXSIZE]; int front; //队首指针,指向队首元素 int rear; //队尾指针,指向队尾元素 }SqQueue,*Queue;
- 构造一个空队列Q
bool InitQueue(SqQueue &Q) { Q = new Queue; //为队列分配一个最大容量为MAXSIZE的数组空间 if (!Q->data) //存储分配失败 { return false; } Q->front = Q->rear = 0; //头指针和尾指针置为零,队列为空 return true; }
- 循环队列入队操作
bool push(Queue q,int e) { //插入到队尾 if((q->rear+1)%MAXSIZE==q->front) { return false; } q->data[q->rear]=e; q->rear=(q->rear+1)%MAXSIZE; return true; }
- 循环队列出队操作
bool pop(Queue q) { if(q->front==q->rear) //队列为空,返回false { return false; } q->front =(q->front+1)%MAXSIZE; return true; }
- 队列的链式存储结构
typedef struct QNode{ int data; //结点数据域 struct QNode *next; //结点指针域 } QNode,*QueuePtr; typedef struct { QueuePtr front,rear; //队首、队尾指针 }LinkQueue;
- 链式队列入队操作
bool EnQueue(LinkQueue &Q,int e) { QueuePtr temp; temp=new QNode; temp->data=e; temp->next=NULL; Q.rear->next=temp; //尾插法插入节点 Q.rear=temp; return true; }
- 链式队列出队操作
bool DeQueue(LinkQueue &Q,int &e) { QueuePtr p; if(Q.front==Q.rear) { return false; } p=Q.front->next; e=p->data; Q.front->next=p->next; //修改头结点的指针域 if(Q.rear==p) Q.rear=Q.front; //如果最后一个元素被删,队尾指针指向头结点 delete p; //释放原队头元素的空间 return true; }
- C++中queue容器:使用该容器时需要包含相应的头文件
queue<int>qu; //新建并初始化一个元素类型全为整型的队列qu queue.empty(); //判断队列是否为空,若为空,函数返回true;否则返回false queue.pop(); //删除队列中的队头元素 queue.front(); //访问队列的队头元素 queue.back(); //访问队列的队尾元素 queue.push(x); //把元素x插入到队列的尾部 queue.size(); //访问队列中元素的个数
-
队列应用
- 生活中,去银行办理业务,等地铁时都有应用到队列这种数据结构,即遵循着先入先出(FIFO)的原则
1.2.谈谈你对栈和队列的认识及学习体会
- 栈和队列本质上还是线性表,元素之间具有线性关系,在C++中他们有各自的容器,只要声明了头文件,就可以使用其中的函数,会方便很多
- 元素入栈是后入先出(LIFO),元素入队列是先入先出(FIFO),生活中有很多场景是栈结构,队列结构的体现。如浏览网页的回退操作就是栈结构后入先出的体现,生活中排队时就遵循着队列先入先出的原则等等,这些实际场景会加深自己对栈和队列的理解
- 栈和队列的逻辑结构都是线性结构,存储结构有顺序存储结构和链式存储结构,在运用这两者时,有一些知识点和线性表的知识点是一样的,如链式存储结构中的尾插法插入元素,顺序存储结构中的数组添加元素等
2.PTA实验作业
2.1 题目1:7-3 jmu-ds-符号配对
2.1.1 代码截图
2.1.2 本题PTA提交列表说明
- 部分正确(5):在运用循环遍历字符串后,没有进行对栈是否为空的判断,加入一句if(!s.empty())语句就过了“括号不匹配,栈不空”这个测试点。
- 部分正确(10):题目要求”若栈顶为空,则输出no”,原来代码运行时会先空一行再进行输出no。后来把初始化栈的语句放到主函数中,并把栈作为自定义函数的一个参数,最后在主函数中进行栈空的判断,从而输出正确的格式,过了“括号不匹配,栈空”这个测试点。
2.2 题目2:7-7 银行业务队列简单模拟
2.2.1 代码截图
2.2.2 本题PTA提交列表说明
- 格式错误:题目要求最后一个编号后不能有多余的空格,之前我是把全部空格都在元素之前输出,但第一个输出的元素前就有了空格,不符合题意,只需用j++语句控制空格的输出就行
- 部分正确:
此时若只输入一个偶数,则在这个偶数前会有一个空格,不符合题目要求。直接在循环遍历队列的过程前面加上条件判断语句,进行单个元素的直接输出
3.阅读代码
3.1 题目:栈的压入、弹出序列
- 解题代码:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
stack<int> st;
int j = 0;
for(int i=0;i<pushed.size();i++)
{
st.push(pushed[i]);
while(st.size() && st.top()==popped[j])
{
st.pop();
j++;
}
}
return st.size()==0;
}
3.1.1 该题的设计思路
- 先把pushed序列元素入栈,如果栈顶元素和popped序列元素相同,则持续抛出。最后如果栈为空,则为true,否则为false
- 算法的时间复杂度和空间复杂度:令pushed.size()=n,则时间复杂度T(n)=O(n)。空间复杂度S(n)=O(n)
3.1.2 该题的伪代码
初始化栈st
for (定义变量i实现对pushed序列的遍历)
{
pushed[i]元素入栈
while (栈不空且栈顶元素和popped序列元素相同)
{
栈顶元素出栈
popped序列指向下一元素
}
end while
}
若栈为空,返回true;否则返回false
3.1.3 运行结果
3.1.4 分析该题目解题优势及难点
- 该算法借助栈,在遍历pushed序列的过程中不断把其中元素入栈,再分别与popped序列的元素对比,若相等,则弹出栈顶元素,关键是判断最后栈是否为空。注意算法中用的容器是vector,而进行vector操作前应添加相应的头文件
3.2 题目:汉诺塔问题
- 解题代码:
void hanota(vector<int>& A, vector<int>& B, vector<int>& C)
{
h(A,B,C,A.size());
}
void h(vector<int>& A, vector<int>& B, vector<int>& C, int n)
{
if(n == 1)
{
C.push_back(A.back());
A.pop_back();
return;
}
h(A,C,B,n-1);
C.push_back(A.back());
A.pop_back();
h(B,A,C,n-1);
}
3.2.1 该题的设计思路
- 运用递归的解法,每次都把递归调用函数参数中第一座塔中最大的那个环加到C塔上
- 算法的时间复杂度和空间复杂度:令A塔中环的个数为n,则时间复杂度T(n)=O(n),空间复杂度S(n)=O(n2)
3.2.2 该题的伪代码
if (n为1,即为递归出口)
{
载体A中的最后一元素传到载体C中
}
否则递归调用函数h(A,C,B,n-1),把载体A中除最大元素外的n-1个元素传到载体B中
载体A中的最大元素传到载体C中
继续递归调用函数h(B,A,C,n-1),实现把载体B中的n-1个元素传到载体C中
3.2.3 运行结果
3.2.4 分析该题目解题优势及难点
- 此算法使用了递归调用,巧妙地借助了辅助塔B来进行环的移动,最后实现塔C中环的摆放顺序与塔A中环的摆放顺序一样,注意算法中用的容器是vector,而进行vector操作前应添加相应的头文件