简单记录牛客top101算法题初级题(C语言实现)BM42 用两个栈实现队列 && BM43 包含min函数的栈 && BM44 有效括号序列

1. 栈、堆和队列的简介

1.1 栈的简介

  栈(Stack)是一种非常重要的数据结构, 它基于后进先出(Last-In-First-Out,LIFO)的原则,就是栈中最后插入的元素将会是第一个被移除的元素。

栈的两个基本操作:
 压入(Push):将元素放入栈的顶部,叫做 “压入” 元素。
 弹出(Pop):从栈的顶部移除元素,叫做 “弹出” 元素。

栈的常见的应用场景和示例

  1. 函数调用栈:编程语言使用栈来跟踪函数的调用和返回,以确保正确的控制流程。
  2. 表达式求值:栈可以用于计算数学表达式,特别是中缀表达式转换为后缀表达式,然后进行求值。
  3. 括号匹配:栈可以用于检查括号是否匹配,例如在编写编译器时,用于语法分析。
  4. 逆序输出:栈可以用于逆序输出一系列元素,例如字符串、数组等。
  5. 浏览器的后退功能:浏览器通常使用栈来记录用户的访问历史,以便用户可以返回上一页。

1.2 堆的简介

  堆(Heap)是计算机科学中一种非常重要的数据结构, 它是一种特殊的树形数据结构,通常是一个完全二叉树
堆的主要特点
 1.二叉树结构:堆是一棵完全二叉树,这意味着树的每个节点最多有两个子节点,且从左到右填充节点。
 2.堆序性质:最大堆(Max Heap):在最大堆中,父节点的值大于或等于其每个子节点的值。因此,根节点是堆中的最大元素;最小堆(Min Heap):在最小堆中,父节点的值小于或等于其每个子节点的值。因此,根节点是堆中的最小元素。
堆的常见应用场景:

  1. 优先级队列:堆可用于实现优先级队列,其中每个元素都有一个关联的优先级,堆可以高效地找到具有最高(或最低)优先级的元素。
  2. 堆排序:堆排序是一种高效的排序算法,它利用最大堆或最小堆来排序数据。
  3. 内存管理:操作系统使用堆来分配和释放内存块,以满足程序的动态内存需求。
  4. 图算法:堆可用于解决图算法中的一些问题,如最短路径算法(例如Dijkstra算法)。
  5. 中位数查找:在一组数据中查找中位数时,堆可以用于高效地维护数据集合。

1.3 队列的简介

  队列(Queue)是一种常见的数据结构,它基于先进先出(First-In-First-Out,FIFO)的原则,即最早进入队列的元素将最早被移出队列
队列的主要特点
 1.先进先出:队列中最早进入的元素首先被处理,而最后进入的元素最后被处理。
 2.两个基本操作:入队(Enqueue):将元素添加到队列的末尾;出队(Dequeue):从队列的前端移除元素。
队列的常见应用场景:

  1. 任务调度:操作系统使用队列来管理各种任务,确保它们按照适当的顺序执行。
  2. 打印队列:打印机通常使用队列来管理等待打印的文档,以确保它们按照提交的顺序打印。
  3. 广度优先搜索(Breadth-First Search):在图算法中,队列用于实现广度优先搜索,从起点开始逐层扩展搜索。
  4. 消息传递:在消息传递系统中,消息被放入队列,以确保它们按照发送的顺序被接收和处理。
  5. 缓冲区:队列用于构建缓冲区,例如网络通信中的数据包缓冲。

2.BM42 用两个栈实现队列

  要求:用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。数据范围:n≤1000,存储n个元素的时间复杂度O(n),插入和删除的时间复杂度都是O(1)。

输入:["PSH1","PSH2","POP","POP"]
返回值:1,2
说明:
"PSH1":代表将1插入队列尾部
"PSH2":代表将2插入队列尾部
"POP“:代表删除一个元素,先进先出=>返回1
"POP“:代表删除一个元素,先进先出=>返回2   

2.1 自己的整体思路

  1. 建立一个数组作为接收压入栈的元素,建立另一个数组来接收弹出栈的元素。
  2. 依次实现压栈,弹出栈,返回最后的弹出栈的元素值即可。(和数组的思想是一样的,像打印逆序数组,用新数组接收逆序的值,逆序就实现了队列了)
static int instack[1000];     //声明一个入栈的数组
static int outstack[1000];    //声明一个出栈的数组
static int intop = 0;         //入栈数组索引
static int outtop = 0;        //出栈数组索引

void push(int node) {                   //在队尾插入
    *(instack + intop) = node;          //索引+1,赋值,把值放到插入的数组中
    intop++;
}
int  pop() { 
     if (outtop == 0) {                  //表示outstack数组中没有元素,将instack元素逐个弹出,并存进outstack,依次返回出栈的值即可
        while (intop) {
        //*(outstack + (outtop++)) = *(instack + (--intop));
          *(outstack + outtop) = *(instack + intop - 1);  //索引从0开始,这里的intop是个数,所以要减1
           outtop++;
           intop--;
     }
 }
      return *(outstack + (--outtop));   //这里能返回多个数,强
}

2.2 小结

   栈中的索引i其实记录的是栈中元素的个数,访问栈顶元素,是索引值i减1,对应的值,一定要记住。

3.BM43 包含min函数的栈

  **要求:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。

输入:["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
返回值:-1,2,1,-1
解析:
"PSH-1"表示将-1压入栈中,栈中元素为-1
"PSH2"表示将2压入栈中,栈中元素为2-1
“MIN”表示获取此时栈中最小元素==>返回-1
"TOP"表示获取栈顶元素==>返回2
"POP"表示弹出栈顶元素,弹出2,栈中元素为-1
"PSH1"表示将1压入栈中,栈中元素为1-1
"TOP"表示获取栈顶元素==>返回1
"MIN"表示获取此时栈中最小元素==>返回-1
此栈包含的方法有:
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素

3.1 自己的整体思路

  1. 只用到了简单的压栈和弹出函数的实现。
  2. 寻找最小的值,用一个变量等于栈中的第一个元素值,遍历整个栈(数组)即可找到最小值。
#include 
int instack[300];    //入栈数组
int i = 0;
int minval = 0;
void push(int value){          //往栈里面压入值
    // write code here
    instack[i] = value;
    i++;
}
void pop() {                      //弹出栈元素
    // write code here
     if ( i != 0) {
        // instack[i] = '';
        i--;
     }
}
int top() {                     //获取栈顶元素
    // write code here
    if ( i != 0) {
        return instack[i - 1];
    }
    return instack[i - 1];      //获取栈顶的元素,索引的值是数组的总个数减去1
}
int min() {                //获取栈中的最小元素
    // write code here

    if (i != 0){
        minval = instack[0];
        for (int j = 1; j < i; j++) {  //找到最小的值
            if (minval > instack[j]) { 
                minval = instack[j];
            }
        }
    }
     return minval;
}

3.2 小结

   栈中的索引i其实记录的是栈中元素的个数,访问栈顶元素,是索引值i减1,对应的值,一定要记住。

4.BM44 有效括号序列

  要求:给出一个仅包含字符’(‘,’)‘,’{‘,’}‘,’[‘和’]',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。

输入:"[]"
返回值:true

4.1 自己的整体思路

  开始想要通过两个字符括号的差值来判断,是不是括号正确,但是情况太复杂了,比如有"[()[]]" ,“()[]{}”,“((”,"))"等等,没有实现,通过栈来实现比较合适(本题就是栈的一种用法)。

  1. 如果元素个数是奇数,肯定不成立;偶数则继续往下运行。(但其实不用判断这个也行,后面的判断涵盖了这一条了)
  2. 遍历整个字符数组,如果是左半边的括号都压到栈中去,后面如果遇到右半边的括号就与栈顶的元素比较,看是否能配对,如果能配对就把栈顶元素弹出,继续遍历,如果不能配对,就直接返回false。
  3. 最后要判断栈中的索引是否为0,就是压入栈的元素全部弹出,就是"(("情况。
  4. 如果栈为空,就进入到了右半边括号了,说明就匹配,没有左半边括号,直接返回false,比如这种情况"))"。
char stack[5000];     //一半一半吧,对称的
    int k = 0;
    int n = strlen(s);   //计算字符串的长度
    if(n % 2 != 0){                // "[()[[]()]]",这种根本就考虑不到
        return false;
    }else{                   //[:91 , ]:93,{: 123,  }:125,(:40  ,):41
    for (int i = 0; i < n; i++) {
        if (s[i] == '[' || s[i] == '(' ||s[i] == '{') {
            stack[k] = s[i];                               //压入栈中
            k++;
        }else {
            if (k == 0) {                                 //"))" 栈必须不为null,不然就是这种情况
                   return false; // 栈为空,说明不匹配
            }                                      
        if ((stack[k -1] == '[' && s[i] == ']') ||(stack[k - 1] == '{' && s[i] == '}') || (stack[k - 1] == '(' && s[i] == ')') ) {
                k--;   //弹出栈
            }else {
                return false;
            }
        }
    }
        if (k != 0) {                                 //"((" 最后的栈必须为null,不然就是这种情况
            return false; // 栈为空,说明不匹配
         }
    return true;
 }    
}

4.2 小结

   栈中的索引i其实记录的是栈中元素的个数,访问栈顶元素,是索引值i减1,对应的值,一定要记住。数组实现了栈,数组索引不能越界。

你可能感兴趣的:(编程题练习,c语言,数据结构,栈)