数据结构之栈、队列、递归
一、栈
- 1.用数组实现一个顺序栈
- 2.用链表实现一个链式栈
- 3.编程模拟实现一个浏览器的前进、后退功能
1.1用数组实现一个顺序栈
- 栈是一种特殊的线性表,插入和删除操作只能在栈顶进行,具有后进先出(LIFO)的特点。栈的实现,有两种存储结构,分别是顺序存储结构和链式存储结构。
- 下面,就用数组实现栈的顺序存储结构。
template<typename T>
class ArrayStack
{
public:
ArrayStack(); //创建栈
~ArrayStack(); //销毁栈
void push(T t); //向栈顶添加一个t元素
T peek(); //向栈中取出栈顶元素
T pop(); //在栈中删除栈顶元素
int size(); //大小
int isEmpty(); //判断是否为空
private:
T *arr;
int count;
};
//创建栈,默认大小是20
template<class T>
ArrayStack<T>::ArrayStack()
{
arr=new T[20];
if(!arr){
cout<<"arr malloc error"<<endl;
}
}
//销毁栈
template<class T>
ArrayStack<T>::~ArrayStack()
{
if(arr){
delete []arr;
arr=NULL;
}
}
//向栈顶添加一个元素
template<class T>
void ArrayStack<T>::push(T t)
{
arr[count++]=t;
}
//返回栈顶元素
template<class T>
T ArrayStack<T>::peek()
{
return arr[count-1];
}
//删除栈顶元素,并返回其值
template<class T>
T ArrayStack<T>::pop()
{
int num=arr[count-1];
count--;
retutn num;
}
//返回栈的大小
template<class T>
int ArrayStack<T>::size()
{
return count;
}
//判断栈是否为空
template<class T>
int ArrayStack<T>::isEmpty()
{
return size()==0;
}
1.2 用链表实现一个链式栈
- note:链栈是借用单链表实现的栈。其不同于顺序栈之处在于:
1、链栈的空间是程序运行期间根据需要动态分配的,机器内存是它的上限。而顺序栈则是
静态分配内存的。
2、链栈动态分配内存的特性使得它一般无需考虑栈溢出的问题。
- 下面用链表实现栈的链式存储结构
//
typedef struct Node
{
int data;
struct Node *link;
}node,*pnode;
//基本操作
pnode createStack(); //创建一个空栈
void push(pnode top,int data); //入栈
void pop(pnode top); //出栈
int isEmpty(pnode top); //判断是否为空栈
//创建一个空栈
pnode createStack()
{
pnode top=(pnode)malloc(sizeof(node));
if(top==NULL){
cout<<"Error"<<endl;
return 0;
}else{
top->link=NULL;
}
return top;
}
//判断是否为空栈
int isEmpty(pnode top)
{
if(top->link == NULL)
{
return 0;
}
else
{
return 1;
}
}
//入栈
void push(pnode top)
{
pnode pnew=(pnode)malloc(sizeof(node));
if(pnew==NULL){
cout<<"error"<<endl;
return ;
}
pnew->data=data;
pnew->link=top->link;
top->link=pnew;
}
//出栈
void pop(pnode top)
{
if(!isEmpty(top)){
cout<<"Stack is Empty"<<endl;
return ;
}
else{
pnode tmp;
tmp=top->link;
top->link=tmp->link;
free(tmp);
cout<<"Success"<<endl;
}
}
1.3编程模拟实现一个浏览器的前进、后退功能
-
思路:使用两个栈a和b实现。将先浏览的网页依次压栈存入a栈中,当后退时,网页再依次从a栈中弹出,依次压栈进入b栈中;当网页前进时,在从b栈中出栈取出数据,放入a栈中。若a栈中为空时,说明没有页面可以继续后退浏览了;当b栈为空时,说明没有页面可以进行前进浏览了。
-
代码
由于能力有限,代码没有实现,待向各位大佬学习后再给出~
二、队列
- 1.用数组实现一个顺序队列
- 2.用链表实现一个链式队列
- 3.实现一个循环队列
2.1用数组实现一个顺序队列
//队列的顺序存储
#define MaxSize 20
typedef struct
{
int data[MaxSize]; //数组
int front,rear; //队头,队尾
}SqQueue;
//初始化队列
void initQueue(SqQueue* &q)
{
q=(SqQueue*)malloc(sizeof(SqQueue));
q->front=q->rear =-1;
}
//销毁队列
void destroy(SqQueue* &q)
{
free(q);
}
//判断队空
bool queueEmpty(SqQueue* q)
{
if(q->front == q->rear)
printf("Empty\n");
else
printf("Not Empty\n");
return (q->front == q->rear);
}
//队列的特点:队头出队;队尾进队
//进队
bool enQueue(SqQueue* &q,int e)
{
if(q->rear == MaxSize -1)
return false;
q->rear++;
q->data[q->rear] = e;
return true;
}
//出队
bool deQueue(SqQueue* &q,int &e)
{
if(q->front == q->rear)
return false;
q->front++;
e=q->data[q->front];
return true;
}
2.2用链表实现一个链式队列
//用单链表实现链队
//数据结点
typedef struct qnode
{
int data;
struct qnode* next;
}DataNode;
//链队头结点
typedef struct
{
DataNode* front;
DataNode* rear;
}LinkQnode;
//初始化
void initQueue(LinkQnode* &q)
{
q=(LinkQnode*)malloc(sizeof(LinkQnode));
q->front=q->rear=NULL;
}
//销毁队列
void destoryQueue(LinkQnode* &q)
{
DataNote* pre=q->front; //pre指向队首结点
DataQnode *p;
if(pre!=NULL)
{
p=pre->next;
while(p!=NULL)
{
free(pre);
pre=p;
p=p->next;
}
free(pre); //释放最后一个链队结点
}
free(q); //释放链队结点
}
//判断队空
bool QueueEmpty(LinkQnode* q)
{
if(q->rear==NULL || q->front==NULL)
printf("Empty\n");
else
printf("not Empty\n");
return (q->rear ==NULL);
}
//队的长度
int getSize(LinkQnode* q)
{
int size=0;
DataNode *p=q->front;
while(p)
{
size++;
p=p->next;
}
return size;
}
//进队
void push(LinkQnode* &q,int e)
{
DataQnode* p;
p=(DataQnode*)malloc(sizeof(DataNode));
p->data=e;
p->next=NULL:
if(q->rear==NULL) //若队为空,则新结点既是首结点又是尾结点
{
q->front=q->rear=p;
}
else
{
q->rear->next=p; //将p链到队尾
q->rear=p; //尾结点变成p
}
}
//出队
bool pop(LinkQnode* &q,int &e)
{
DataNode *t;
if(q->rear==NULL) //队空报错
return false;
t=q->front; //t指向队首
if(q->front==q->rear) //若队中只有一个结点
{
q->front=q->rear=NULL;
}
else
{
q->front=q->front->next;
}
e=t->data;
free(t);
return true;
}
//遍历队列
void printQueue(LinkQnode* q)
{
DataNode *p=q->front;
while(p)
{
printf("%d ",p->data)
p=p->next;
}
}
2.3实现一个循环队列
循环队列:使顺序队列的首尾相连,解决了顺序队列假溢出的问题
- 循环队列特点:
- 队头指针的计算: front=(front+1)%MaxSize
- 队尾指针的计算: rear=(rear+1)%MaxSize
- 队空:q->rear=q->front
- 队满:(q->rear+1)==q->front
//
//队列的顺序存储
#define MaxSize 20
typedef struct
{
int data[MaxSize]; //数组
int front,rear; //队头,队尾
}SqQueue;
//初始化队列
void initQueue(SqQueue* &q)
{
q=(SqQueue*)malloc(sizeof(SqQueue));
q->front=q->rear =0;
}
//销毁队列
void destroy(SqQueue* &q)
{
free(q);
}
//判断队空
bool queueEmpty(SqQueue* q)
{
if(q->front == q->rear)
printf("Empty\n");
else
printf("Not Empty\n");
return (q->front == q->rear);
}
//进队
bool enQueue(SqQueue* &q,int e)
{
if((q->rear+1)%MaxSize == q->front) //队满
return false;
q->rear=(q->rear+1)%MaxSize;
q->data[q->rear] = e;
return true;
}
//出队
bool deQueue(SqQueue* &q,int &e)
{
if(q->front == q->rear) //队空
return false;
q->front=(q->front+1)%MaxSize;
e=q->data[q->front];
return true;
}
三、递归
- 1.编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
- 2.编程实现求阶乘 n!
- 3.编程实现一组数据集合的全排列
Note:递归思想
递归就是一个大问题可以转化为规模较小的与之相似的子问题,解决大问题的方法和解决小问题的方法相同,所以就有了递归函数就是函数调用自身的说法。另外递归还要有明显的结束条件,避免无限递归下去。
3.1编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
//斐波那契数列求值 f(n)=f(n-1)+f(n-2)
//递归实现
int f(int n)
{
int sum;
//出口
if(n==1 || n==2)
return n;
else
sum=f(n-1)+f(n-2);
return sum;
}
3.2编程实现求阶乘 n!
//递归实现求阶乘 n!
int f(int n)
{
//递归出口:0!=1;1!=1
if(n==0 || n==1)
return 1;
return n*f(n-1);
}
3.3编程实现一组数据集合的全排列
#include<iostream>
using namespace std;
//递归实现全排列
//list数组存放排列的数,k表示排列到第几个,m表示数组最大长度
void perm(int list[],int k,int m)
{
if(k==m) //出口 k=m时表示到最后,输出排列
{
for(int i=0;i<=m;i++)
cout<<list[i]<<" ";
cout<<endl;
}
else
{
for(int i=k;i<=m;i++)
{
swap(list[i],list[k]);
perm(list,k+1,m);
swap(list[i],list[k]);
}
}
}
//交换
void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int main()
{
int a[]={3,4,5};
int m=2;
perm(a,0,2);
}
四、LeetCode练习题(未完待续···)
4.1栈
- 4.1.1 Valid Parentheses(有效的括号)
题目链接
//此题就是括号的模式匹配问题
//用栈实现
class Solution {
public:
bool isValid(string s) {
stack<char> st;
for (char& c : s) {
switch (c) {
case '(':
case '{':
case '[': st.push(c); break;
case ')': if (st.empty() || st.top()!='(') return false; else st.pop(); break;
case '}': if (st.empty() || st.top()!='{') return false; else st.pop(); break;
case ']': if (st.empty() || st.top()!='[') return false; else st.pop(); break;
default: ;
}
}
return st.empty() ;
}
};
- 4.1.2 Longest Valid Parentheses(最长有效的括号)
- 4.1.3 Evaluate Reverse Polish Notatio(逆波兰表达式求值)
4.2队列
- 4.2.1 Design Circular Deque(设计一个双端队列)
- 4.2.2 Sliding Window Maximum(滑动窗口最大值)
4.3递归
- 4.3.1Climbing Stairs(爬楼梯)