1、栈
1.1、栈的概念及特性:栈是一种先进后出的特殊线性表,它只允许从固定的一端进行插入删除等操作,其中插入删除端称为栈顶,另一端称为栈底。
1.2数据结构中栈与程序中栈的区别:
数据结构中的栈:对管理数据的一种手段和方法。可以用来存放数据和地址,只能在一端(栈顶)对数据项进行插入和删除。
程序(内存)中的栈:是确切存在的物理结构,是用来存放不同数据的内存空间。内存中的栈,是由系统自动分配和释放的,是由高地址向低地址扩展的数据机构,是一段连续的内存区域,是对数据结构中的栈这种手段的实现。栈的顶地址和最大容量是系统预先设定好的,在程序编译时,它就是一个确定的常数,但注意并不是所有用到栈技术的区域都是栈区,虽然目前是这样,这是概念定义的准确性问题。
1.3、栈的分类:分为动态栈和静态栈。
1.4、栈的实现:
// 使用顺序表实现栈
typedef int SDataType;
typedef struct Stack
{
SDataType* _array;
int _size;
int _capacity;
int _top; // 标记栈顶位置
}Stack;
// 初始化栈
void StackInit(Stack* ps){
ps->_array = NULL;
ps->_capacity = 10;
ps->_size = 0;
}
void Checkcapacity(Stack* ps){
int Newcapacity = ps->_capacity * 2;
if (ps->_size >= ps->_capacity){
// 申请空间
SDataType* Newarray = (SDataType*)malloc(sizeof(SDataType)*Newcapacity);
// 搬家
int i = 0;
for (i = 0; i < ps->_size; i++){
Newarray[i] = ps->_array[i];
}
// 释放老空间,绑定新空间
free(ps->_array);
ps->_array = Newarray;
ps->_capacity = Newcapacity;
}
}
// 入栈
void StackPush(Stack* ps, SDataType data){
Checkcapacity(ps);
ps->_array[ps->_size] = data;
}
// 出栈
void StackPop(Stack* ps){
ps->_size--;
}
// 获取栈顶元素
SDataType StackTop(Stack* ps){
return ps->_array[0];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps){
return ps->_size;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps){
if (ps->_size == 0){
return 1;
}
return 0;
}
// 销毁栈
void StackDestroy(Stack* ps){
free(ps->_array);
ps->_array = NULL;
ps->_size = 0;
ps->_capacity = 0;
}
2、队列
2.1、队列的概念及特性:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性。进行插入(入队)操作的一端叫队尾,进行删除(出队)操作的一端叫队首。
2.2、队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。
typedef int QDataType;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _pNext;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front; // 队首
QNode* _rear; // 队尾
}Queue;
// 初始化队列
void QueueInit(Queue* q){
q->_front = q->_rear = NULL;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data){
QListNode* node = (QListNode*)malloc(sizeof(QListNode));
node->_data = data;
node->_pNext = NULL;
// 如果队首为空就直接入队
if (q->_front == NULL){
q->_front = node;
q->_rear = node;
}
// 如果队首不为空,就让队尾和队尾->next=node
else{
q->_rear->_pNext = node;
q->_rear = node;
}
}
// 队头出队列
void QueuePop(Queue* q){
QListNode* node = q->_front->_pNext; // 存储队首的下一个结点
free(q->_front); // 释放队首
q->_front = node;
if (q->_front == NULL){
q->_rear = NULL;
}
}
// 获取队列头部元素
QDataType QueueFront(Queue* q){
return q->_front->_data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q){
return q->_rear->_data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q){
int size = 0;
QListNode* cur = q->_front;
while (cur!=NULL)
{
cur = cur->_pNext;
size++;
}
return size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q){
if (q->_front == NULL){
return 1;
}
return 0;
}
// 销毁队列
void QueueDestroy(Queue* q){
// 删除所有结点
QListNode* cur = q->_front;
while (cur != NULL){
cur = cur->_pNext;
free(cur);
}
}
3、栈和队列的区别
3.1、队列先进先出,栈先进后出。
3.2、对插入和删除操作的"限定"不同。
栈是限定只能在表的一端进行插入和删除操作的线性表。
队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
3.3、遍历数据速度不同。
栈只能从头部取数据,也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性。
队列则不同,它基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多。
4、相关面试题
#pragma once
#include <string.h>
//给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
//
//有效字符串需满足:
//
//左括号必须用相同类型的右括号闭合。
//左括号必须以正确的顺序闭合。
//注意空字符串可被认为是有效字符串。
class Solution {
public:
bool isValid(string s) {
stack <char> s_ch;
int size = s.size();
for (int i = 0; i < size; i++){
char ch = s[i];
switch (ch){
case '(':
case '[':
case '{':
s_ch.push(ch);
break;
case ')':
case ']':
case '}': {
if (s_ch.empty()){
return false;
}
char left = s_ch.top();
if (!((left == '(' && ch == ')') ||
(left == '[' && ch == ']') ||
(left == '{' && ch == '}'))) {
return false;
}
s_ch.pop();
break;
}
default:
break;
}
}
if (!s_ch.empty()){
return false;
}
return true;
}
};
// 使用队列实现栈的下列操作:
// push(x) -- 元素 x 入栈
// pop() -- 移除栈顶元素
// top() -- 获取栈顶元素
// empty() -- 返回栈是否为空
class MyStack {
public:
queue <int> q;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
q.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int size = q.size()-1;
for(int i = 0; i < size; i++){
int val = q.front();
q.pop();
q.push(val);
}
int d = q.front();
q.pop();
return d;
}
/** Get the top element. */
int top() {
int size = q.size()-1;
for(int i = 0; i < size; i++ ){
int data = q.front();
q.pop();
q.push(data);
}
int d = q.front();
q.pop();
q.push(d);
return d;
}
/** Returns whether the stack is empty. */
bool empty() {
return q.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* bool param_4 = obj.empty();
*/
// 使用栈实现队列的下列操作:
// push(x) -- 将一个元素放入队列的尾部。
// pop() -- 从队列首部移除元素。
// peek() -- 返回队列首部的元素。
// empty() -- 返回队列是否为空。
class MyQueue {
public:
stack <int> left;
stack <int> right;
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
right.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
if(left.empty()){
int size = right.size();
for(int i = 0; i < size; i++){
int d = right.top();
right.pop();
left.push(d);
}
}
int v = left.top();
left.pop();
return v;
}
/** Get the front element. */
int peek() {
if(left.empty()){
int size = right.size();
for(int i = 0; i < size; i++){
int d = right.top();
right.pop();
left.push(d);
}
}
int v = left.top();
return v;
}
/** Returns whether the queue is empty. */
bool empty() {
if(left.empty() && right.empty()){
return true;
}
return false;
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* bool param_4 = obj.empty();
*/
// 设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
// push(x) -- 将元素 x 推入栈中。
// pop() -- 删除栈顶的元素。
// top() -- 获取栈顶元素。
// getMin() -- 检索栈中的最小元素。
class MinStack {
public:
stack <int> S;
stack <int> s;
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
S.push(x);
if(s.empty() || s.top() > x){
s.push(x);
}
else{
s.push(s.top());
}
}
void pop() {
S.pop();
s.pop();
}
int top() {
return S.top();
}
int getMin() {
return s.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
// 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
// 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
// 你的实现应该支持如下操作:
// MyCircularQueue(k): 构造器,设置队列长度为 k 。
// Front: 从队首获取元素。如果队列为空,返回 -1 。
// Rear: 获取队尾元素。如果队列为空,返回 -1 。
// enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
// deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
// isEmpty(): 检查循环队列是否为空。
// isFull(): 检查循环队列是否已满。
class MyCircularQueue {
public:
int *array;
int capacity;
int size;
int front; // 队首下标
int rear; // 队尾下标
/** Initialize your data structure here. Set the size of the queue to be k. */
MyCircularQueue(int k) {
array = new int [k];
capacity = k;
size = 0;
front = 0;
rear = 0;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
bool enQueue(int value) {
if(size == capacity){
return false;
}
array[rear] = value;
rear = (rear + 1) % capacity;
size++;
return true;
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool deQueue() {
if(size == 0){
return false;
}
front = (front + 1) % capacity;
size--;
return true;
}
/** Get the front item from the queue. */
int Front() {
return array[front];
}
/** Get the last item from the queue. */
int Rear() {
int index = (rear + capacity - 1) % capacity;
return array[index];
}
/** Checks whether the circular queue is empty or not. */
bool isEmpty() {
return size == 0;
}
/** Checks whether the circular queue is full or not. */
bool isFull() {
return size == capacity;
}
};
/**
* Your MyCircularQueue object will be instantiated and called as such:
* MyCircularQueue* obj = new MyCircularQueue(k);
* bool param_1 = obj->enQueue(value);
* bool param_2 = obj->deQueue();
* int param_3 = obj->Front();
* int param_4 = obj->Rear();
* bool param_5 = obj->isEmpty();
* bool param_6 = obj->isFull();
*/