合肥网站建设怎么样,大良营销网站建设效果,适合员工的培训课程,电子商务网站购物流程图栈
1.栈的定义
栈#xff1a;栈是仅限与在表尾进行插入或者删除的线性表
我们把允许一端插入和删除的一端叫做栈顶#xff0c;另一端叫栈底#xff0c;不含任何元素的栈叫做空栈#xff0c;栈又叫做后进先出的线性表#xff0c;简称LIFO结构
2.栈的理解
对于定义里面…栈
1.栈的定义
栈栈是仅限与在表尾进行插入或者删除的线性表
我们把允许一端插入和删除的一端叫做栈顶另一端叫栈底不含任何元素的栈叫做空栈栈又叫做后进先出的线性表简称LIFO结构
2.栈的理解
对于定义里面的在表尾进行插入和删除这里的表尾就是栈顶而不是栈底这个理解会在用单链表实现栈的时候会着重说明一下栈的插入叫做入栈栈的删除叫做出栈。
这里为了更加形象的去理解栈你可以把他当作一个装弹夹的过程因为装弹的时候就是第一颗装进去是在最下面的最后一颗装进去是在最上面的所以非常符合后进先出的特点。 再者就是我们进入网页的时候想回到上一个页面会点击返回页面这个操作也是栈的应用因为返回的是最新点开的所以可以理解为出栈的操作。 因为栈是线性表所以可以分为顺序存储结构和链式存储结构 3.栈的顺序存储结构及实现
栈的顺序存储也就是用顺序表去实现所以我们称为顺序栈这里用顺序表完美契合了栈的特点所以非常合适。
顺序栈的实现有以下几步 1.栈的初始化 2.栈的销毁 3.栈的入栈 4.栈的出栈 首先栈的初始化和顺序表一样但是我们需要一个坐标去表示栈顶在栈满的时候去判断是否会溢出这个坐标我们设置为top,初始化的时候我们有两个选择一种是初始化为-1一种是初始化为0两种各有好处前者是可以用top表示当前元素后者更加符合更多人的认知这里我采用的是后者 栈的结构定义
typedef int STDataType;//这里有可能是其他元素所以采用取别名的方式
typedef struct Stack
{STDataType* a;int top;//栈顶元素int capacity;//容量大小
}ST;
栈的初始化
void STInit(ST* pst)
{assert(pst);pst-a NULL;pst-capacity 0;pst-top 0;//这里我们采用第二种方式
}
栈的入栈操作
void STPush(ST* pst, STDataType x)
{assert(pst);if (pst-top pst-capacity)//这里无论是满还是空都成立所以写在一起//如果top开始是-1那么这里应该top1{int newcapacity pst-capacity 0 ? 4 : pst-capacity * 2;//如果是空就给一个初值 //否则直接扩容到2倍STDataType* tmp (STDataType*)realloc(pst-a,newcapacity*sizeof(STDataType));//申 请一片空间if (tmp NULL){perror(realloc fail);return;}else{pst-a tmp;pst-capacity newcapacity;}}pst-a[pst-top] x;//申请成功那么直接赋值top再pst-top;
} 栈的出栈操作
void STPop(ST* pst)
{assert(pst);assert(!STEmpty(pst));//如果是空就不能出栈了pst-top--;//直接--就行
} 栈的销毁操作
void STDestory(ST* pst)
{assert(pst);free(pst-a);pst-a NULL;pst-capacity 0;pst-top 0;
}
栈的判空 bool STEmpty(ST* pst)
{assert(pst);return pst-top 0;
}
由于栈用顺序结构有点弊端所以我们可以用两个栈去优化它也就是两个相同的栈相对这样可以让顺序栈进一步优化 但是如果两个栈都增加那么也是没效果的这种结构适用于一个整加一个减少就比如买股票
有人买就有人卖出 4.栈的链式存储结构及实现
栈的链式存储结构简称为链栈用链表来实现其实有很多方法但这里还是推荐用单链表其他的都有点不对劲首先我们肯定需要一个栈顶指针 这里我们有两种选择一种是把尾结点当作栈顶每次插入或删除都在尾结点或者是把头节点做栈顶每次插入或者删除在头节点如果用尾结点做栈顶那么我需要把指针放在尾结点的前一个所以为了避免混淆我们使用头插头删的方法去实现 链栈的优势是不存在栈满的情况如果真的发生了那说明计算机已经面临奔溃了
链栈的结构定义
typedef int STDataType;
typedef struct StackNode
{struct StackNode* next;STDataType data;
}ST;
typedef struct Stack
{ST* top;int size;
}Stack;//这里多定义一个结构体用top指针去指向第一个结点
链栈的初始化
void StackInit(Stack* st)
{assert(st);st-top NULL;st-size 0;
}
链栈的销毁 void StackDestory(Stack* st)
{assert(st);ST* cur st-top;while (cur){ST* next cur-next;free(cur);cur next;}st-top NULL;st-size 0;
}
链栈的入栈
void StackPush(Stack* st, STDataType x)
{assert(st);ST* newnode (ST*)malloc(sizeof(ST));if (newnode NULL){perror(malloc fail);return;}else {newnode-data x;newnode-next NULL;if (st-top NULL){st-top newnode;}else{newnode-next st-top;st-top newnode;}}st-size;
}
链栈的出栈 void StackPop(Stack* st)
{assert(st);assert(!StackEmpty(st));ST* cur st-top;st-top st-top-next;free(cur);st-size--;
}
链栈的判空
bool StackEmpty(Stack* st)
{assert(st);return st-top NULL;
}
链栈的返回栈顶元素
STDataType StackTop(Stack* st)
{assert(st);assert(!StackEmpty(st));return st-top-data;
}
队列
1.队列的定义
队列队列是只允许在一端进行插入操作在另一端进行删除操作的线性表 队列是一种先进先出的线性表运行插入一段的叫队尾允许出队列的叫队头
没有元素的队列叫空队列
2.队列的理解
对于队列的理解我们可以理解为一条河流或者是排队取餐他们都是有顺序的先来的先吃饭后来的后吃饭这是不是很想一些餐厅一样他们那个系统也就是队列的应用。 队列存储方式和栈是一样的有两种方式
1.顺序队列顺序表实现队列
2.链队列用链表实现队列
这两者的选择上其实用链表更加合适顺序表不是很适合因为用顺序表需要挪动数据非常麻烦所以下面用链表实现顺序表的实现就不展示了
3.队列的链式存储结构
因为队列是一个先进先出的数据结构所以我们用链表的时候也就是头删和尾插队列的结构定义
typedef int QDataType;
typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QNode;typedef struct Queue
{QNode* phead;//一个指向头QNode* ptail;//一个指向尾int size;
}Queue;//这里和栈的链表实现有些类似都有两个结构体//一个结构体表示结点一个结构体表示位置指针
队列初始化
void QueueInit(Queue* pq)
{assert(pq);pq-phead NULL;pq-ptail NULL;pq-size 0;
}
队列的销毁 void QueueDestory(Queue* pq)
{assert(pq);QNode* cur pq-phead;while (cur){QNode* next cur-next;free(cur);cur next;}pq-phead NULL;pq-ptail NULL;pq-size 0;
} 队列的入列
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode (QNode*)malloc(sizeof(QNode));if (newnode NULL){perror(malloc fail);return;}else{newnode-data x;newnode-next NULL;if (pq-phead NULL){pq-phead pq-ptail newnode;}else{pq-ptail-next newnode;pq-ptail newnode;}}pq-size;
}
队列的出列
void QueuePop(Queue* pq)
{//头删assert(pq);assert(!QueueEmpty(pq));//一个结点//多个结点if (pq-phead-next NULL){free(pq-phead);pq-phead pq-ptail NULL;}else{QNode* next pq-phead-next;free(pq-phead);pq-phead next;}
}
队列的获取第一个元素
QDataType QueueTop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq-phead-data;
} 队列的判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq-phead NULL pq-ptailNULL;//两个指针都有//或者写成pq-size0
}
队列的获取尾元素
这个很多人感觉没用但他在做一些题目的时候会有很大作用 QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq-ptail-data;
} 说完链队列我们来看看顺序队列上面其实说了顺序队列的不好但是如果我们把它改成循环队列那就会很好如果是普通队列那么它出队的时间复杂度为O(N)循环队列为O(1)而且普通队列会浪费很多空间具体看下面的动图 队空的条件就是frontrear,那满就不能用这个了 所以我们可以多开一个空间rear指向最后一个元素的下一个位置这个位置没有元素是空的当rear再走一步也就和front相等了用这个来判断是否是满的由于是循环的所以我们用(rear1)%MAXSIZEfront来判断
所以我们这样做就可以把顺序队列给优化
循环队列的结构定义
typedef int QDataType;
#define MAXSIZE 50
typedef struct QueueSL
{QDataType a[MAXSIZE];//数组表示int front;//一个头int rear;//一个尾
}QL;
循环队列的初始化 void QLInit(QL* pq)
{assert(pq);pq-front 0;pq-rear 0;
}
循环队列的销毁
void QLDestory(QL* pq)
{assert(pq);free(pq-a);pq-front 0;pq-rear 0;
}
循环队列的入队列 void QLPush(QL* pq, QDataType x)
{assert(pq);if ((pq-rear 1) % MAXSIZE pq-front){return;}else{pq-a[pq-rear] x;pq-rear (pq-rear 1) % MAXSIZE;//这里可以看看上面的动态图然后自己画图//不能直接写rear,但跑到最后一个元素的时候还要回来}
}循环队列的出列
void QLPop(QL* pq)
{assert(pq);assert(!QLEmpty(pq));//这里判断是否为空空了就不能删了pq-front (pq-front 1) % MAXSIZE;//和上面一样
} 循环队列判空
bool QLEmpty(QL* pq)
{assert(pq);return (pq-rear pq-front);
}以上就是栈和队列的基本操作和基础知识其实还有很多知识点这些放在后面的文章说需要用到c
有了以上的基础下面我们写几个题目去巩固一下
oj题
用栈实现队列 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作push、pop、peek、empty 实现 MyQueue 类 void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返回队列开头的元素boolean empty() 如果队列为空返回 true 否则返回 false 说明 你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。你所使用的语言也许不支持栈。你可以使用 list 或者 deque双端队列来模拟一个栈只要是标准的栈操作即可。 示例 1 输入
[MyQueue, push, push, peek, pop, empty]
[[], [1], [2], [], [], []]
输出
[null, null, null, 1, 1, false]解释
MyQueue myQueue new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false提示 1 x 9最多调用 100 次 push、pop、peek 和 empty假设所有操作都是有效的 例如一个空的队列不会调用 pop 或者 peek 操作 进阶 你能否实现每个操作均摊时间复杂度为 O(1) 的队列换句话说执行 n 个操作的总时间复杂度为 O(n) 即使其中一个操作可能花费较长时间。 这道题思路就是一个栈出数据一个栈进数据就可以实现队列因为队列是先进先出栈是后进先出所以把栈倒过来就变成了先进先出
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);void STInit(ST* pst)
{assert(pst);pst-a NULL;//pst-top -1; // top 指向栈顶数据pst-top 0; // top 指向栈顶数据的下一个位置pst-capacity 0;
}void STDestroy(ST* pst)
{assert(pst);free(pst-a);pst-a NULL;pst-top pst-capacity 0;
}void STPush(ST* pst, STDataType x)
{if (pst-top pst-capacity){int newCapacity pst-capacity 0 ? 4 : pst-capacity * 2;STDataType* tmp (STDataType*)realloc(pst-a, newCapacity * sizeof(STDataType));if (tmp NULL){perror(realloc fail);return;}pst-a tmp;pst-capacity newCapacity;}pst-a[pst-top] x;pst-top;
}void STPop(ST* pst)
{assert(pst);assert(!STEmpty(pst));pst-top--;
}STDataType STTop(ST* pst)
{assert(pst);assert(!STEmpty(pst));return pst-a[pst-top - 1];
}bool STEmpty(ST* pst)
{assert(pst);/*if (pst-top 0){return true;}else{return false;}*/return pst-top 0;
}int STSize(ST* pst)
{assert(pst);return pst-top;
}
//以上是栈的实现可以自己写一个因为我们用的c所以不是太方便
typedef struct {ST pushst;ST popst;//用结构体去存两个栈
} MyQueue;MyQueue* myQueueCreate() {//开辟一片空间去存也就是创造MyQueue*obj(MyQueue*)malloc(sizeof(MyQueue));if(objNULL){perror(malloc fail);return NULL;}STInit(obj-pushst);//记得初始化STInit(obj-popst);return obj;
}void myQueuePush(MyQueue* obj, int x) {STPush(obj-pushst,x);//把数据全部丢进进数据的那个栈
}int myQueuePeek(MyQueue* obj) {if(STEmpty(obj-popst))//判断是不是空如果不是空直接返回第一个元素就行//如果不是就需要把元素全部导进来{while(!STEmpty(obj-pushst)){STPush(obj-popst,STTop(obj-pushst));STPop(obj-pushst);//导一个元素就删一个}}return STTop(obj-popst);
}
int myQueuePop(MyQueue* obj) {//和上面的思路很想所以直接套过来就行多了一个删除int frontmyQueuePeek(obj);STPop(obj-popst);return front;
}bool myQueueEmpty(MyQueue* obj) {return STEmpty(obj-pushst)STEmpty(obj-popst);
}void myQueueFree(MyQueue* obj) {STDestroy(obj-pushst);STDestroy(obj-popst);free(obj);} 用队列实现栈 请你仅使用两个队列实现一个后入先出LIFO的栈并支持普通栈的全部四种操作push、top、pop 和 empty。 实现 MyStack 类 void push(int x) 将元素 x 压入栈顶。int pop() 移除并返回栈顶元素。int top() 返回栈顶元素。boolean empty() 如果栈是空的返回 true 否则返回 false 。 注意 你只能使用队列的标准操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。你所使用的语言也许不支持队列。 你可以使用 list 列表或者 deque双端队列来模拟一个队列 , 只要是标准的队列操作即可。 示例 输入
[MyStack, push, push, top, pop, empty]
[[], [1], [2], [], [], []]
输出
[null, null, null, 2, 2, false]解释
MyStack myStack new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False提示 1 x 9最多调用100 次 push、pop、top 和 empty每次调用 pop 和 top 都保证栈不为空 进阶你能否仅用一个队列来实现栈。 这道题的思路和上一个题有点像但是不能完全套用因为我们队列是先进先出我们要实现后进先出我们得把有数据的队列里面的前n-1个数据放到没有数据的地方然后把最后那个元素弹出也就是实现了后进先出后面的没个元素操作都是这样。 typedef int QDataType;
typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{assert(pq);pq-phead NULL;pq-ptail NULL;pq-size 0;
}
void QueueDestory(Queue* pq)
{assert(pq);QNode* cur pq-phead;while (cur){QNode* next cur-next;free(cur);cur next;}pq-phead NULL;pq-ptail NULL;pq-size 0;
}
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode (QNode*)malloc(sizeof(QNode));if (newnode NULL){perror(malloc fail);return;}newnode-data x;newnode-next NULL;if (pq-ptail NULL){assert(pq-phead NULL);pq-phead newnode;pq-ptail newnode;}else{pq-ptail-next newnode;pq-ptail newnode;}pq-size;
}
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq-phead-next NULL){free(pq-phead);pq-ptailpq-phead NULL;}else{QNode* next pq-phead-next;free(pq-phead);pq-phead next;}pq-size--;
}
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq-phead-data;
}
QDataType QueueBack(Queue* pq)
{assert(pq);return pq-ptail-data;
}
bool QueueEmpty(Queue* pq)
{assert(pq);return pq-phead NULL pq-ptail NULL;
}
int QueueSize(Queue* pq)
{assert(pq);return pq-size;
}//队列的实现typedef struct {Queue q1;Queue q2;
} MyStack;//和上个题一样的思路MyStack* myStackCreate() {MyStack *obj(MyStack*)malloc(sizeof(MyStack));QueueInit(obj-q1);QueueInit(obj-q2);return obj;
}void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(obj-q1))//往没有数据的地方放数据{QueuePush(obj-q1,x);}else{QueuePush(obj-q2,x);}}
//导数据
int myStackPop(MyStack* obj) {Queue*pEmptyQobj-q1;//因为我们不知道哪个是空所以要把它找出来Queue*pNoEmptyQobj-q2;if(!QueueEmpty(obj-q1)){pEmptyQobj-q2;pNoEmptyQobj-q1;}while(QueueSize(pNoEmptyQ)1)//找到以后就开始导数据把最后一个留下{QueuePush(pEmptyQ,QueueFront(pNoEmptyQ));QueuePop(pNoEmptyQ);}int topQueueFront(pNoEmptyQ);然后返回最后一个元素这也是要导出的元素QueuePop(pNoEmptyQ);return top;
}int myStackTop(MyStack* obj) {Queue*pEmptyQobj-q1;Queue*pNoEmptyQobj-q2;if(!QueueEmpty(obj-q1))//找哪个不是空{pEmptyQobj-q2;pNoEmptyQobj-q1;}return QueueBack(pNoEmptyQ);//这里队列的尾指针起到了作用是不是很妙也就不用遍历了
}bool myStackEmpty(MyStack* obj) {return QueueEmpty(obj-q1)QueueEmpty(obj-q2);
}void myStackFree(MyStack* obj) {free(obj);} 总结 希望本文章对你有帮助大概就是栈和队列的实现以及为什么要这样实现都在里面了可能还有很多知识点因为牵扯到c所以准备往后更里面的oj题可能很好的打磨希望你能认真看完