LeetCode刷题总结(C语言版)_栈队列类

编程总结

每每刷完一道题后,其思想和精妙之处没有地方记录,本篇博客用以记录刷题过程中的遇到的算法和技巧
020)有效括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串

思路:将字符串都判断如果是左括号(’(’,’[’,’{’,’(’)都进行入栈,遇到其他括号符号则:1)取出栈顶元素,2)同时将栈顶元素的match括号匹配上,3)然后Pop栈顶元素和match元素进行比对,如果一致继续循环判断,否则退出,直到遍历完所有的字符串长度。


typedef char DataType;
typedef struct node{
    DataType data;
    struct node *next;
} Stack;

Stack* CreateStack();               //创建栈
void StackEmpty(Stack* );           //清空栈
void DestoryStack(Stack*);          //撤销(删除)栈
int IsEmpty(Stack*);                //判空
int PushStack(Stack*, DataType);    //入栈
int PopStack(Stack*);               //出栈
DataType GetTopElement(Stack*);     //取栈顶元素

#define TRUE_T    1
#define FALSE_T   0

bool isValid(char *s) {
    int i = 0;
    Stack *stack_tmp;
    char c = 0;
    char match = 0;
    int length = strlen(s);

    stack_tmp = CreateStack();

    //非成对出现直接return FALSE
    if (length % 2 == 1 ) {
        return FALSE_T;
    }
    //“”,输入空字符串,要返回TRUE
    if (length == 0 ) {
        return TRUE_T;
    }

    for (i = 0; i < length; i++) {
        if (s[i] == '(' || s[i] == '[' || s[i] == '{') {
            PushStack(stack_tmp, s[i]);
        } else {
            if (IsEmpty(stack_tmp)) {
                DestoryStack(stack_tmp);
                return FALSE_T;
            }
            //取出栈顶元素
            c = GetTopElement(stack_tmp);
            PopStack(stack_tmp);

            if (s[i] == ')') {
                match = '(';
            } else if (s[i] == ']') {
                match = '[';
            } else {
               if (s[i] != '}') {
                   DestoryStack(stack_tmp);
                   return FALSE_T;
                }
                match = '{';
            } 

            if (c != match) {
                DestoryStack(stack_tmp);
                return FALSE_T;
            }
        }
    }
    //第一个条件是判断:成对出现"((" ,直接跳过了match判断的操作
    //第二个条件是判断栈如果是非空的,栈里面还有符号,也要返回FALSE
    if (match  == 0 || IsEmpty(stack_tmp) == 0) {
        return FALSE_T;
    }

    DestoryStack(stack_tmp);
    return TRUE_T;
    
}

int main()
{
    int n;
    char *str = "(()(";
    printf("isValid is %d", isValid(str));
    return 0;
}

//创建栈,此时栈中没有任何元素
Stack *CreateStack()
{
    Stack *stack = (Stack *)malloc(sizeof(Stack));

    if (NULL != stack) {
       stack->next = NULL;
       return stack;
    }
    return NULL;
}

//清空栈
void StackEmpty(Stack *stack)
{
    while (!IsEmpty(stack)) {
        PopStack(stack);
    }
}

//撤销栈
void DestoryStack(Stack *stack)
{
    free(stack);
}

int IsEmpty(Stack *stack)
{
    return (stack->next == NULL);
}

//入栈,成功返回1, 失败返回0, 把元素 data 存入栈 stack 中
int PushStack(Stack *stack, DataType data)
{
    Stack *newst = (Stack *)malloc(sizeof(Stack));
    if (NULL != newst) {
        newst->data = data;         // 有亚节点的情况
        newst->next = stack->next;  // 插入到 stack 和 stack->next 之间
        stack->next = newst;        
        return 1;
    }
    return 0;
}
/*
    出栈,成功返回1,失败返回0,出栈不取出元素值,只是删除栈顶元素。
    如出栈要实现,取出元素值,并释放空间,可结合取栈顶元素函数做修改,这里不再给出。
 */
int PopStack(Stack *stack)
{
    Stack *tmpst;
    if (!IsEmpty(stack)) {
        tmpst = stack->next;      // 有亚节点的情况,Pop stack->next
        stack->next = tmpst->next;// stack->next 重新赋予原 stack->next->next 的节点
        free(tmpst);
        return 1;
    }

    return 0;
}
 
//取栈顶元素,仅取出栈顶元素的值,取出之后,该元素,任然存在栈中。成功返回元素值,失败输出提示信息,并返回 -1
DataType GetTopElement(Stack *stack)
{
    if (!IsEmpty(stack)) {
        return stack->next->data;
    }

    return -1;
}

C语言实现一个栈往往较为复杂,多数解题是通过一个数组来模拟栈
下面给出用数组模拟上述算法的代码:

bool isTrue(char a, char b)
{
	if ((a == '('&&b == ')') || (a == '['&&b == ']') || (a == '{'&&b == '}')) {
		return true;
	}

	return false;
}

bool isValid(const char *s) {
	int len = strlen(s);
	char str[10000];
	int top = -1, i = 0; // top为栈顶

	if (len == 0) {
		return true;
	}
	while (i < len)
	{
		if (top == -1 || !(isTrue(str[top], s[i]))) // 不满足括号匹配
		{
			top++;
			str[top] = s[i];  // 入栈
		} else if (isTrue(str[top], s[i])) // 满足括号匹配
		{
			top--;
		}
		i++;
	}
	if (top == -1) { // 最后看栈顶能否回到起始位置
		return true;
	} else {
		return false;
	}
}

224)基本计算器
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格
一个运算符栈opors,一个运算数栈opands。

括号内的计算优先级最高。

碰到’(‘,’(‘入opors栈。

碰到’)’,opors栈中栈顶到’(‘的运算符都要计算出来。 这表示括号内的表达式结束了,然后, ‘(‘ 出栈。

碰到’+”-‘, opors栈中栈顶到’(‘的运算符都要计算出来。 这是左结合性导致的。然后 ‘+”-‘ 入栈。

碰到数字,提取出整数,然后入opands栈。

#include 
#include 
#include 
using namespace std;

class Solution {
private:
    stack<int> num;
    stack<char> op;
    int pri(char a){
        switch(a){
        case '+': return 1;
        case '-': return 1;
        case '*': return 2;
        case '/': return 2;
        case '(': return 3;
        default: return -1;
        }
    }
    void cal(){
        int b=num.top();num.pop();
        int a=num.top();num.pop();
        switch(op.top()){
        case '+':num.push(a+b);break;
        case '-':num.push(a-b);break;
        case '*':num.push(a*b);break;
        case '/':num.push(a/b);break;
        }
        op.pop();
    }
public:
    int calculate(string s) {
        string ss;
        for(int i=0;i<(int)s.size();i++){
            if(isdigit(s[i]))
                ss+=s[i];
            else if(s[i]==' ') continue; //碰到空格
            else{
            	//碰到数字,提取出整数,然后入opands栈
                if(!ss.empty()){
                    num.push(stoi(ss));
                    ss.clear();
                }
                //碰到'(','('入opors栈;碰到当前元素优先级高于栈顶元素,入栈
                if(op.empty()||s[i]=='('||pri(op.top())<pri(s[i]) )
                    op.push(s[i]);
                //碰到’)’,opors栈中栈顶到'(‘的运算符都要计算出来; 这表示括号内的表达式结束了,然后, ‘(‘ 出栈。
                else if(s[i]==')'){
                    while(op.top()!='(') cal();
                    op.pop();
                }//只要满足当前元素优先级高于栈顶元素,都进行计算,直到遇到更高优先级元素,完成计算后,让它进栈
                else{
                    while(!op.empty()&&pri(op.top())<=pri(s[i])) cal();
                    op.push(s[i]);
                }
            }
        }
        if(!ss.empty()) num.push(stoi(ss));
        while(!op.empty()) cal();
        return num.top();
    }
};

int main(){
    Solution s;
    cout<<s.calculate("(1+(4+5+2)-3)+(6+8)")<<endl;
}

队列的典型应用:广度优先遍历
有两种通用的遍历树的策略:

1.深度优先搜索(DFS)
在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。
深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为先序遍历,中序遍历和后序遍历。

2.宽度优先搜索(BFS)
我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。

102)二叉树的层次遍历
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
LeetCode刷题总结(C语言版)_栈队列类_第1张图片
利用二叉树的,先序遍历,根左右
这里需要注意的是最后是通过二维数组存放结果,对于指针的指针的处理需要注意,并且有树深度(returnSize)返回。

void helper(struct TreeNode* root, int **result, int *ColumnSizes, int i, int *maxh) {
	if (root == NULL) { // 递归终止条件
		return ;
	}
	result[i][ColumnSizes[i]] = root->val;   // 二叉树的前序遍历
	ColumnSizes[i]++;    // ColumnSize[i] 存放当前二维数组的列元素
	if (i + 1 > *maxh) { // 更新深度,由于 i+1 为深度,传值给 *maxh 为深度
		*maxh = i + 1;
	}
	helper(root->left, result, ColumnSizes, i + 1, maxh);  // i + 1 表示层次加1
	helper(root->right, result, ColumnSizes, i + 1, maxh); // maxh 表示 树的深度
}

int **levelOrder(struct TreeNode *root, int *returnSize, int **returnColumnSizes) {
	int **result = (int **)malloc(sizeof(int *) * MaxSize); // 二维数组,结果存放处, malloc行空间
	int i;

	for (i = 0; i < MaxSize; i++) {  // malloc列空间
		result[i] = (int *)malloc(sizeof(int) * MaxSize);
	}
	*returnColumnSizes = (int *)malloc(MaxSize * sizeof(int));
	memset(*returnColumnSizes, 0, MaxSize * sizeof(int));
	*returnSize = 0;    // 二维数组里行元素个数,也就是树的深度
	if (root == NULL) { // 异常判断
		return NULL;
	}
	helper(root, result, *returnColumnSizes, 0, returnSize);
	return result;
}

二叉树的层次遍历使用的更多的是利用队列的性质实现BFS,广度优先遍历:
下面还有队列的C语言实现。

#define MAX_QUEUE 10000

struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
 };
 
struct Queue
{
    int front; // 队头删除
    int rear;  // 队尾新增
    int size;
	struct TreeNode *array[MAX_QUEUE]; // 指针数组
};
void QueueInit(struct Queue *queue)
{
	memset(queue, 0, sizeof(*queue));
}
int QueueEmpty(struct Queue *queue)
{
    return (queue->size == 0);
}
void QueuePush(struct Queue *queue, struct TreeNode *node)
{
	queue->array[queue->rear] = node; // 往队尾增加一个元素
	queue->rear++;
	printf("Queue Push:queue->rear val %d\n", node->val);
    queue->size++;
}
int QueuePop(struct Queue *queue, int *data)
{
    if (QueueEmpty(queue)) {
       return 0;
    }
	printf("QueuePop\n");
	queue->front = (queue->front + 1);
	queue->size--;

	return 1;
}
void QueueDestroy(struct Queue* queue)
{
     queue->front = 0;
	 queue->rear = 0;
	 queue->size = 0;
	 free(queue->array);
	 free(queue);
}
struct TreeNode *QueueFront(struct Queue *q)
{
    if (q->size == 0)
        return NULL;
	printf("q->front val %d\n", q->array[q->front]->val);
    return q->array[q->front];
}

int **levelOrder(struct TreeNode *root, int *returnSize, int **returnColumnSizes)
{
	*returnSize = 0;
    *returnColumnSizes = 0;
	if (root == NULL) {
		return NULL;
	}
	int layer = 0;
	int data = 0;
    int **res = (int **)malloc(MAX_QUEUE * sizeof(int *));
    struct Queue *q = (struct Queue *)malloc(sizeof(struct Queue));
	QueueInit(q);
	*returnColumnSizes = (int *)malloc(MAX_QUEUE * sizeof(int)); 

    QueuePush(q, root);      // 插入root头结点
    while (!QueueEmpty(q)) { // 依次入队头结点/左右节点,然后利用队列性质先进先出队列
        int curNum = q->size;
        int *array = (int *)malloc(sizeof(int) * curNum);
        (*returnColumnSizes)[layer] = curNum; // returnColumnSizes 记录一个数组,数组元素是记录结果每一行的元素个数
		printf("--current size curNum-- %d\n", curNum);
        for (int i = 0; i < curNum; i++) {
            struct TreeNode *node = QueueFront(q); // 获取队头节点
            if (node) {
                array[i] = node->val;              // 获取每一行的结果
				printf("-----node->val %d-----\n\n",node->val);
                if (node->left) QueuePush(q, node->left);   // 左节点入队
                if (node->right) QueuePush(q, node->right); // 右节点入队
            }
            QueuePop(q, &data);// 队头节点出队列
        }
        res[layer++] = array;           // res 是结果存放的二维数组,array是每一行结果存放的一维数组
		printf("layer is %d\n", layer); // layer 是记录这个二维数组的行数,即深度
		free(array);
    }
    *returnSize = layer;
    free(q);
    return res;
}

98)验证二叉搜索树
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
LeetCode刷题总结(C语言版)_栈队列类_第2张图片

bool isValid(struct TreeNode *root, long min ,long max) {
	if (root == NULL) {
		return 1; // 递归终止条件
	}
	if (min >= root->val) { //根节点必须大于min
		return false;
	}
	if (max <= root->val) {  //根节点必须小于max
		return false;
	}

	return isValid(root->left, min, root->val) && isValid(root->right, root->val, max);
}
bool isValidBST(struct TreeNode *root) {
	long min = -2147483649;  // 需要注意由于题目的 val 为int型,所以会有用例覆盖到 int的最值(-2147483649 - 2147483647),所以需要将min/max超过这个范围
	long max = 2147483649;   // 否则,如果出现一个根节点为min,将会直接判断false出去,但一个根节点的结果应该是true

	return isValid(root, min, max);
}
  1. 使用队列实现栈的下列操作:
    push(x) – 元素 x 入栈
    pop() – 移除栈顶元素
    top() – 获取栈顶元素
    empty() – 返回栈是否为空
    考虑到队列是一种 FIFO 的数据结构,最后入队的元素应该在最后被出队。因此我们需要维护另外一个队列 q2,这个队列用作临时存储 q1 中出队的元素。q2 中最后入队的元素将作为新的栈顶元素。接着将 q1 中最后剩下的元素出队。我们通过把 q1 和 q2 互相交换的方式来避免把 q2 中的元素往 q1 中拷贝
typedef struct SqQueue {
    int front;
    int rear;
    int dataLen;
    int data[1024];
} SqQueue_t;

// 初始化队列
SqQueue_t *SqQueueCreate(int dataLen) {
    SqQueue_t *queue = (SqQueue_t *)malloc(sizeof(SqQueue_t) + sizeof(int) * dataLen);
    queue->front = 0;
	queue->rear = 0;
    queue->dataLen = dataLen;

    return queue;
}

void SqQueueDestroy(SqQueue_t *queue) {
    free(queue);
}

int SqQueueFull(SqQueue_t *queue) {
    return (queue->front == (queue->rear + 1) % queue->dataLen);
}

int SqQueueEmpty(SqQueue_t *queue) {
    return (queue->front == queue->rear);
}

int SqQueuePush(SqQueue_t *queue, int data) {
    if (SqQueueFull(queue)){
        return -1;
	}
	// 将数据放置到队尾
    queue->data[queue->rear++] = data;  
	printf("Push:queue->rear is %d, data is %d\n", queue->rear, data);

    return 0;
}

int SqQueuePop(SqQueue_t *queue, int *data) {
    if (SqQueueEmpty(queue)) {
        return -1;
	}
	// 先进先出,按队头顺序出队列
    *data = queue->data[queue->front++];
	printf("Pop:queue->front is %d, data is %d\n", queue->front, *data);

    return 0;
}

/*---------------------两个队列实现栈---------------------*/
typedef struct {
    SqQueue_t *qA;
    SqQueue_t *qB;
} MyStack;

/** Initialize your data structure here. */
MyStack *myStackCreate() {
    MyStack *stack = (MyStack *)malloc(sizeof(MyStack));
    stack->qA = SqQueueCreate(20);
    stack->qB = SqQueueCreate(20);

    return stack;
}

/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack *obj) {
    return (SqQueueEmpty(obj->qA) && SqQueueEmpty(obj->qB));
}

/** Returns whether the stack is empty. */
bool myStackFull(MyStack *obj) {
    return (SqQueueFull(obj->qA) || SqQueueFull(obj->qB));
}

/** Push element x onto stack. */
void myStackPush(MyStack *obj, int x) {
    if (myStackFull(obj))
        return;

    SqQueue_t *enQueue = NULL;
    if (!SqQueueEmpty(obj->qA)) {
        enQueue = obj->qA;
    } else {
        enQueue = obj->qB;
    }
    SqQueuePush(enQueue, x);
}

/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack *obj) {
    if (myStackEmpty(obj)) {
        return -1;
	}
	int data;

    SqQueue_t *enQueue = NULL;
    SqQueue_t *deQueue = NULL;
    SqQueue_t *tmpQueue = NULL;

    if (!SqQueueEmpty(obj->qA)) {
        deQueue = obj->qA;  // qA A队列出数据
        enQueue = obj->qB;  // qB B队列收数据
    } else {
        deQueue = obj->qB;
        enQueue = obj->qA;
    }

    while (deQueue->rear != deQueue->front) {
		printf("\n");
        SqQueuePop(deQueue, &data);     // 队列1出数据,队列2收数据
        if (!SqQueueEmpty(deQueue)) {   // 队列1出完最后一个元素,队列2不接收,直接作为最后一个输出,它就是栈的Top元素
            SqQueuePush(enQueue, data);
        }
    }
	printf("rear is %d, front is %d\n", deQueue->rear, deQueue->front);

    return data;
}

/** Get the top element. */
int myStackTop(MyStack *obj) {
    if (myStackEmpty(obj)) {
        return -1;
	}

    SqQueue_t *enQueue = NULL;
    SqQueue_t *deQueue = NULL;
    int data;

	// 交换deQueue 和 enQueue
    if (!SqQueueEmpty(obj->qA)) {
        deQueue = obj->qA;
        enQueue = obj->qB;
    } else {
        deQueue = obj->qB;
        enQueue = obj->qA;
    }

    while (deQueue->rear != deQueue->front) {
        SqQueuePop(deQueue, &data);
        SqQueuePush(enQueue, data);
    }

    return data;
}

void myStackFree(MyStack *obj) {
    SqQueueDestroy(obj->qA);
    SqQueueDestroy(obj->qB);

    free(obj);
}

int main() 
{
	MyStack *stack = myStackCreate();
	myStackPush(stack, 1);
	myStackPush(stack, 2);
	myStackPush(stack, 3);
	myStackPush(stack, 4);
	printf("Pop element is %d\n", myStackPop(stack));

	return 0;
}

出栈时,队列A会先逐一拷贝元素至另一个队列B,直到最后一个元素Pop出去;
下次出栈时,就反过来,此队列A已经空了,作为存储队列,队列B Push进队列A,直到最后一个元素Pop出去。
LeetCode刷题总结(C语言版)_栈队列类_第3张图片

typedef struct Stack {
	int data[MAX_SIZE];
	int top;
	int min;
} MinStack; // 为Stack 起了一个别名,后续可以直接使用MinStack来定义变量

MinStack *minStackCreate() {
	 MinStack *obj = (MinStack *)malloc(sizeof(MinStack));
	if (obj) {
		obj->top = -1;
		obj->min = INT_MAX;
		return obj;
	}
	return NULL;
}

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
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.

#include 
#include 
#include  
#include 

#define MAX_SIZE 100

typedef struct Stack {
	int data[MAX_SIZE];
	int top;
	int min;  // 保留一个最小值
} MinStack;

MinStack *minStackCreate() {
	 MinStack *obj = (MinStack *)malloc(sizeof(MinStack));
	if (obj) {
		obj->top = -1;
		obj->min = INT_MAX;
		return obj;
	}
	return NULL;
}

void minStackPush(MinStack *obj, int x) {
	if (obj->top == MAX_SIZE - 1) { // 达到最大值
		return;
	}

	obj->data[++obj->top] = x;
	obj->min = fmin(obj->min, x);  // 保存最小值 
}

void minStackPop(MinStack *obj) {
	int i = 0;

	if (obj->top < 0) {
		obj->min = INT_MAX;
		return;
	}

	obj->top--;
	obj->min = INT_MAX;
	// top后这里为什么需要重新找 min 点
	for (i = 0; i <= obj->top; i++) {
		obj->min = fmin(obj->min, obj->data[i]);
	}
}

int minStackTop(MinStack *obj) {
	return obj->data[obj->top];    // 返回top值
}

int minStackGetMin(MinStack *obj) {
	return obj->min;               // 返回最小值
}

void minStackFree(MinStack *obj) {
	free(obj);
}

你可能感兴趣的:(匠心)