数据结构与算法 - 线性结构: 堆栈
栈: 是限定仅在表尾进行插入和删除操作的线性表. (后进先出的线性表)
栈顶(top): 允许插入和删除的一端称为栈顶.
栈底(bottom): 不允许插入和删除的一端称为栈底.
空栈: 不包含任何数据元素的栈.
栈的插入操作, 叫做 进栈, 也称为 压栈, 入栈
栈的删除操作, 叫做 出栈, 也称为 弹栈
栈的定义和操作
定义
#define MAXSIZE 10
typedef int SElemType;
typedef struct {
SElemType data[MAXSIZE];
/*
* 用于栈顶指针
*/
int top;
} SqStack;
操作
/**
* 初始化栈
* @param sqStack
*/
void initStack(SqStack *sqStack) {
sqStack->top = -1;
}
/**
* 判断栈是否为空栈
* @param stack
* @return
*/
bool IsEmpty(SqStack *stack) {
return stack->top == -1;
}
/**
* 判断是否满栈
* @param stack
* @return
*/
bool IsFull(SqStack *stack) {
return stack->top == MAXSIZE - 1;
}
/**
* 入栈
* @param stack
* @param item
* @return
*/
bool Push(SqStack *stack, SElemType item) {
if (IsFull(stack)) {
printf("Stack is full. Cannot push element.\n");
return false;
}
stack->data[++stack->top] = item;
return true;
}
/**
* 出栈
* @param stack
* @param item
* @return
*/
bool Pop(SqStack *stack, SElemType *item) {
if (IsEmpty(stack)) {
printf("Stack is empty. Cannot pop element.\n");
return false;
}
*item = stack->data[stack->top--];
return true;
}
/**
* 获取栈顶元素
* @param stack
* @param item
* @return
*/
bool GetTop(SqStack *stack, SElemType *item) {
if (IsEmpty(stack)) {
printf("Stack is empty. No top element.\n");
return false;
}
*item = stack->data[stack->top];
return true;
}
/**
* 清空栈
* @param stack
*/
void ClearStack(SqStack *stack) {
stack->top = -1;
}
/**
* 打印栈中元素
* @param stack
*/
void PrintStack(SqStack *stack) {
if (IsEmpty(stack)) {
printf("Stack is empty.\n");
return;
}
printf("Stack elements: ");
for (int i = stack->top; i >= 0; i--) {
printf("%d ", stack->data[i]);
}
printf("\n");
}
栈的相关例题
最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
- MinStack() 初始化堆栈对象。
- void push(int val) 将元素val推入堆栈。
- void pop() 删除堆栈顶部的元素。
- int top() 获取堆栈顶部的元素。
- int getMin() 获取堆栈中的最小元素。
示例
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
算法实现
typedef struct stack{
int val;
struct stack *next;
struct stack *min;
} MinStack;
/** initialize your data structure here. */
MinStack* minStackCreate() {
MinStack *myStack=(MinStack*)malloc(sizeof(MinStack));
myStack->val=0;
myStack->next=NULL;
myStack->min=NULL;
return myStack;
}
void minStackPush(MinStack* obj, int val) {
MinStack *node=(MinStack*)malloc(sizeof(MinStack));
node->val=val;
if(obj->val==0){
node->next=NULL;
node->min=node;
obj->next=node;
}else{
node->min= val<obj->next->min->val ? node : obj->next->min;
node->next=obj->next;
obj->next=node;
}
obj->val++;
}
void minStackPop(MinStack* obj) {
MinStack *p=obj->next;
obj->next=p->next;
free(p);
obj->val--;
}
int minStackTop(MinStack* obj) {
return obj->next->val;
}
int minStackGetMin(MinStack* obj) {
return obj->next->min->val;
}
void minStackFree(MinStack* obj) {
if(obj->val==0)
return;
else if(obj->next->next==NULL){
free(obj->next);
obj->next=NULL;
return;
}
MinStack *p1, *p2;
p1=obj->next;
p2=p1->next;
while(p2){
free(p1);
p1=p2;
p2=p2->next;
}
free(p1);
obj->val=0;
}
/**
* Your MinStack struct will be instantiated and called as such:
* MinStack* obj = minStackCreate();
* minStackPush(obj, val);
* minStackPop(obj);
* int param_3 = minStackTop(obj);
* int param_4 = minStackGetMin(obj);
* minStackFree(obj);
*/
有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1
输入: s = "()"
输出: true
示例 2
输入: s = "()[]{}"
输出: true
示例 3
输入: s = "(]"
输出: false
算法实现
bool isValid(char * s){
char leftBrackets(char c);
int length=0;
while(s[length++]!='\0');
length--;
char stack[length];
int top=-1;
for(int i=0; i<length; i++){
if(s[i]=='(' || s[i]=='[' || s[i]=='{')
stack[++top]=s[i];
else{
if(top!=-1 && stack[top]==leftBrackets(s[i]))
top--;
else
return false;
}
}
if(top>-1)
return false;
return true;
}
char leftBrackets(char c){
if(c==')')
return '(';
else if(c==']')
return '[';
else
return '{';
}
数据结构与算法 - 线性结构: 队列
在 FIFO 数据结构中,将首先处理添加到队列中的第一个元素。
如上图所示,队列是典型的 FIFO 数据结构。插入(insert)操作也称作入队(enqueue),新元素始终被添加在队列的末尾。 删除(delete)操作也被称为出队(dequeue)。 你只能移除第一个元素。
队列的定义与操作
结构定义
#define MAXSIZE 10
typedef int QElemType;
typedef struct {
QElemType data[MAXSIZE];
int front; // 队头指针
int rear; // 队尾指针
} SqQueue;
操作
/**
* 初始化队列
* @param sqQueue
*/
void initQueue(SqQueue *sqQueue) {
sqQueue->front = 0;
sqQueue->rear = 0;
}
/**
* 判断队列是否为空
* @param sqQueue
* @return
*/
bool IsEmpty(SqQueue *sqQueue) {
return sqQueue->front == sqQueue->rear;
}
/**
* 判断队列是否已满
* @param sqQueue
* @return
*/
bool IsFull(SqQueue *sqQueue) {
return (sqQueue->rear + 1) % MAXSIZE == sqQueue->front;
}
/**
* 入队
* @param queue
* @param item
* @return
*/
bool EnQueue(SqQueue *queue, QElemType item) {
if (IsFull(queue)) {
printf("Queue is full. Cannot enqueue element.\n");
return false;
}
queue->data[queue->rear] = item;
queue->rear = (queue->rear + 1) % MAXSIZE;
return true;
}
/**
* 出队
* @param queue
* @param item
* @return
*/
bool DeQueue(SqQueue *queue, QElemType *item) {
if (IsEmpty(queue)) {
printf("Queue is empty. Cannot dequeue element.\n");
return false;
}
*item = queue->data[queue->front];
queue->front = (queue->front + 1) % MAXSIZE;
return true;
}
/**
* 获取队头元素
* @param queue
* @param item
* @return
*/
bool GetFront(SqQueue *queue, QElemType *item) {
if (IsEmpty(queue)) {
printf("Queue is empty. No front element.\n");
return false;
}
*item = queue->data[queue->front];
return true;
}
/**
* 清空队列
* @param queue
*/
void ClearQueue(SqQueue *queue) {
queue->front = 0;
queue->rear = 0; 广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径。
}
/**
* 打印队列中元素
* @param queue
*/
void PrintQueue(SqQueue *queue) {
if (IsEmpty(queue)) {
printf("Queue is empty.\n");
return;
}
printf("Queue elements: ");
int i = queue->front;
while (i != queue->rear) {
printf("%d ", queue->data[i]);
i = (i + 1) % MAXSIZE;
}
printf("\n");
}
队列和广度优先搜索
广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径。
例题
岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [ ["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [ ["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
算法实现
int numIslands(char** grid, int gridSize, int* gridColSize) {
// 边界条件判断
if (grid == NULL || gridSize == 0)
return 0;
// 统计岛屿的个数
int count = 0;
// 两个循环遍历每一个格子
for (int i = 0; i < gridSize; i++) {
for (int j = 0; j < *gridColSize; j++) {
// 只有当前格子是 '1' 才开始计算
if (grid[i][j] == '1') {
// 如果当前格子是 '1',岛屿的数量加1
count++;
// 然后通过dfs把当前格子的上下左右4个位置为 '1' 的都要置为 '0',
// 因为它们是连着一起的算一个岛屿
dfs(grid, i, j, gridSize, gridColSize);
}
}
}
// 最后返回岛屿的数量
return count;
}
void dfs(char** grid, int i, int j, int gridSize, int* gridColSize) {
// 边界条件判断,不能越界
if (i < 0 || i >= gridSize || j < 0 || j >= *gridColSize || grid[i][j] == '0')
return;
// 把当前格子置为 '0',然后再从它的上下左右 4 个方向继续遍历
grid[i][j] = '0';
dfs(grid, i - 1, j, gridSize, gridColSize); // 上
dfs(grid, i + 1, j, gridSize, gridColSize); // 下
dfs(grid, i, j + 1, gridSize, gridColSize); // 左
dfs(grid, i, j - 1, gridSize, gridColSize); // 右
}
完全平方数
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9
算法实现
bool is_square(int n) {
int sqrt_n = (int) sqrt(n);
return sqrt_n * sqrt_n == n;
}
int numSquares(int n) {
// 一,先判断由1个平方数组成的
// 如果 n 是平方数,直接返回1即可,表示 n 由 1 个平方数组成
if (is_square(n))
return 1;
// 如果 n 是 4 的倍数,就除以 4,因为 4 是 2 的平方,
// 如果 n 可以由 m 个完全平方数组成,那么 4n 也可以由 m 个完全平方数组成
while ((n & 3) == 0)
n >>= 2;
// 二,在判断由4个平方数组成的
// 如果 n 是 4 的倍数,在上面代码的执行中就会一直除以 4,
// 直到不是 4 的倍数为止,所以这里只需要判断 n=(8b+7) 即可
if ((n & 7) == 7)
return 4;
int sqrt_n = (int) sqrt(n);
// 三,接着判断由2个平方数组成的
// 下面判断是否能由 2 个平方数组成
for (int i = 1; i <= sqrt_n; i++) {
if (is_square(n - i * i)) {
return 2;
}
}
// 四,剩下的只能由 3 个平方数组成了
// 如果上面都不成立,根据拉格朗日四平方和定理,只能由 3 个平方数组成了
return 3;
}