数据结构与算法学习笔记2:栈和队列_一万个括号-程序员宅基地

技术标签: c语言  链表  数据结构与算法学习笔记  数据结构  

栈 stack

  • 受限线性表
  • 先进后出 FILO

题外话:

堆和栈是内存区域的问题,栈和队列是数据结构的问题,两者没有任何关系。

堆和栈的区别

  • 申请空间的方式和回收不一样。

    • 堆区的空间要向系统申请,malloc和new,必须要自己主动释放才能够回收,否则会内存泄漏。
    • 栈只要定义就会自动分配空间,回收也不用管,系统会自动回收。
  • 生命周期不同

    • 栈的生命周期就在定义的大括号里面。
    • 堆的生命周期从申请空间到释放的范围。
    • 所以一般情况下,如果需要返回一块空间的话,一般选择堆区而不会返回栈区,因为栈区会被回收掉。
  • 分配效率不同

    • 栈区是连续的空间,可以连续遍历来得到,速度也快。
    • 堆区是利用的不连续的离散的空间,(底层实现类似于链表),所以如果要用的时候需要遍历啥的,效率相对较低。
  • 生长方向不同

    • 堆区的空间申请时是从小到大生长的
    • 栈区的空间申请时是从大到小生长的
  • 内存碎片问题

    • 堆区会产生内存碎片
    • 栈区不会产生
  • 存储内容问题

    • 堆区放的内容基本是数据和地址(变量地址和空间地址),堆区没有变量的概念,只有被别的变量所指向
    • 栈区放的内容可以放变量和函数的入口地址

静态变量tips

  • 只能定义一次,且在其作用域内保持其值,除非重新给它赋值

  • 只在定义内的文件有效,全局变量才可以跨文件使用

跨文件使用tips:关键字extern

  • 定义一个数组,我们知道可以用指针接着,但跨文件引用的时候千万不可以把原有的数组在跨文件的时候引用成指针,只能声明成数组。
  • 在局部范围内可以使用全局变量,比如一个函数内定义一个局部变量i,然后外面有全局变量i,那么函数内的使用i的时候使用的是函数内的局部变量i,如果想要使用外面的全局变量i,那就需要用extern
  • 三个使用:
    • extern C :以C的标准来编译C++的代码
    • 局部范围内引用全局的变量
    • 跨文件引用
  • 数组方式来实现栈 其弊端在于容量问题 (容量不够则需要用一个新的大容量栈来存 链式没有这个问题)

  • 链式方式来实现栈 其实就相当于链表的头添加头删除 所以会倒序

    • C语言实现链式栈

      #include <stdio.h>
      #include <stdlib.h>
      
      typedef struct node{
              
          int nvalue;
          struct node *pNext;
      }ListStack;
      
      void Push(ListStack **pTop,int nvalue){
              
          //新元素申请空间
          ListStack *pTemp = NULL;
          pTemp = (ListStack*) malloc(sizeof(ListStack));
          pTemp->nvalue = nvalue;
          pTemp->pNext = NULL;
      
          //头增加
          pTemp->pNext = *pTop;
          *pTop = pTemp;
      }
      
      int Pop(ListStack **pTop){
              
          if (*pTop == NULL) {
              
              printf("stack is empty~\n");
              exit(1);    //结束所在进程
          }
      
          //标记
          ListStack *pDel = *pTop;
          int nNum = pDel->nvalue;
      
          //头弹出
          *pTop = (*pTop)->pNext;
          free(pDel);
          pDel = NULL;
          return nNum;
      }
      
      int main()
      {
              
          ListStack *pTop = NULL;
          Push(&pTop, 1);
          Push(&pTop, 2);
          Push(&pTop, 3);
          Push(&pTop, 4);
      
          printf("%d\n",Pop(&pTop));
          printf("%d\n",Pop(&pTop));
          printf("%d\n",Pop(&pTop));
          printf("%d\n",Pop(&pTop));
      
          return 0;
      }
      
  • 栈的八大基本函数

    • Init
    • Push
    • Pop
    • Clear
    • Destroy
    • GetCount
    • GetTop
    • IsEmpty
    //	实现栈的八大基本函数(C语言)
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct node{
          
        int nvalue;
        struct node *pNext;
    }Date;
    
    typedef struct stack{
          
        int nConut;
        Date *pTop;
    }Stack;
    
    void Init(Stack **pStack){
          
        *pStack = (Stack*)malloc(sizeof(Stack));
        (*pStack)->nConut = 0;
        (*pStack)->pTop = NULL;
    }
    
    void Push(Stack *pStack,int nNum){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
    
        Date *pTemp = NULL;
        pTemp = (Date*)malloc(sizeof(Date));
        pTemp->nvalue = nNum;
    
        pTemp->pNext = pStack->pTop;
        pStack->pTop = pTemp;
        pStack->nConut++;
    }
    
    int Pop(Stack *pStack){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
    
        if (pStack->nConut == 0)    
            return -1;
    
        Date *pDel = NULL;
        pDel = pStack->pTop;
        int nNum = pDel->nvalue;
    
        pStack->pTop = pStack->pTop->pNext;
        free(pDel);
        pStack->nConut--;
        return nNum;
    }
    
    void Clear(Stack *pStack){
          
        if (pStack == NULL || pStack->nConut == 0)
            return;
    
        while (pStack->nConut != 0) {
          
            Pop(pStack);
        }
    }
    
    void Destory(Stack **pStack){
          
        Clear(*pStack);
        free(*pStack);
        *pStack = NULL;
    }
    
    int GetTop(Stack *pStack){
          
        if (pStack == NULL || pStack->nConut == 0) {
          
            printf("stack is not exist or stack is empty!\n");
            exit(1);
        }
        return pStack->pTop->nvalue;
    }
    
    int GetCount(Stack *pStack){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
        return pStack->nConut;
    }
    
    int IsEmpty(Stack *pStack){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
        return pStack->nConut == 0 ? 1 : 0; //三目运算符
            // 0 ? 1 : 0
            //如果是0(空)则返回true(1),否则返回false(0)
    }
    
    int main()
    {
          
        Stack *pStack = NULL;
    
        Init(&pStack);
        Push(pStack, 1);
        Push(pStack, 2);
        Push(pStack, 3);
        Push(pStack, 4);
    
        printf("%d\n",GetCount(pStack));
        printf("%d\n",GetTop(pStack)); 
    
        printf("%d\n",Pop(pStack));
        printf("%d\n",Pop(pStack));
        printf("%d\n",Pop(pStack));
        printf("%d\n",Pop(pStack));
    
        printf("%d\n",IsEmpty(pStack));
        Destory(&pStack);
        Push(pStack, 100);
    
        return 0;
    }
    

    题外话:

    指针传递

    • 分析:

    • void fun(char *q){
               
          q = (char*)malloc(100);
      }
      
      int main(){
               
          char *p = NULL;
          fun(p);
          strcpy(p,"haha");
      }
      
    • image-20220418120048006

    • typedef struct S{
               
          char *p;
      }ss;
      
      void fun(S *a){
               
          a->p = malloc(100);
      }
      
      int main(){
               
          S *b = malloc(s);
          fun(b);
          strcpy(b->p,"haha");
      }
      
    • image-20220418132655935

      PS:也可以理解成,a作为一个指针,p是a中的一个指针,那么a->p相当于指针的指针(二重指针,也就类似于上面的地址传递 **p的形式)

    • 总结:值传递(形参修改改不了实参);地址传递(形参修改可以改变实参)。但如果遇到封装的情况,看原封装的指针变量有没有空间,如果有的话,值传递也可以进行空间里内容的修改。

队列 queue

  • 先进先出 FIFO

  • 尾添加 头删除

  • 队列元素的出列是在队头,即下标为0的位置,那也就意味着,队列中的所有元素都得向前移动,以保证队列的队头(也就是下标为0的位置)不为空,此时的时间复杂度为0(n)。

    可有时想想,为什么出队列时一定要全部移动呢,如果不去限制队列的元素必须存储在数组的前n个单元这一条件,出队的性能就会大大增加。也就是说,队头不需要一定在下标为0的位置,比如也可以是a[1]等。

    而为了避免当只有一个元素时,队头和队尾重合使处理变得麻烦,引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear时,此队列不是还剩一个元素,而是空队列。

    对于普通队列来说,最好的方法是使用链表实现,因为对于数组来说,队列可能会出现下面这种情况:

    • 假设是长度为5的数组,初始状态,空队列如所示,front与 rear指针均指向下标为0的位置。然后入队a1、a2、a3、a4, front指针依然指向下标为0位置,而rear指针指向下标为4的位置。

    出队a1、a2,则front指针指向下标为2的位置,rear不变,如下图所示,再入队a5,此时front指针不变,rear指针移动到数组之外。
    在这里插入图片描述

    问题还不止于此。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,我们的队列在下标为0和1的地方还是空闲的。我们把这种现象叫做假溢出

    此时,不可以继续添加元素,否则会造成数组越界而遭致程序出错。然而此时又不应该扩充数组,因为还有大量实际空间未被占用。此时我们应该如何解决这个问题呢?循环队列可以解决这个问题。解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。也就是利用循环来解决空间浪费问题。

  • 队列类型:

    • 单端队列:

      (数组可以实现,但空间受限严重,所以采用链式来实现,尾添加头删除)

      • C语言实现单端队列的几个基础函数

        #include <cstdlib>
        #include <stdio.h>
        #include <stdlib.h>
        
        typedef struct date{
                  
            int nValue;
            struct date *pNext;
        }Date;
        
        typedef struct queue{
                  
            int nCount;
            Date *pHead;
            Date *pTail;
        }ListQueue;
        
        void Init(ListQueue **pListQueue){
                  
            *pListQueue = (ListQueue*)malloc(sizeof(ListQueue));
            (*pListQueue)->nCount = 0;
            (*pListQueue)->pHead = NULL;
            (*pListQueue)->pTail = NULL;
        }
        
        void Push(ListQueue *pListQueue,int nNum){
                  
            if (pListQueue == NULL) {
                  
                printf("queue is not exist.\n");
                exit(1);
            }
            Date *pTemp = NULL;
            pTemp = (Date*)malloc(sizeof(Date));
            pTemp->nValue = nNum;
            pTemp->pNext = NULL;
        
            if (pListQueue->pHead == NULL) {
                  
                pListQueue->pHead = pTemp;
            }else {
                  
                pListQueue->pTail->pNext = pTemp;
            }
            pListQueue->pTail = pTemp;
            pListQueue->nCount++;
        }
        
        int Pop(ListQueue * pListQueue){
                  
            if (pListQueue == NULL) {
                  
                printf("queue is not exist.\n");
                exit(1);
            }
            if (pListQueue->nCount == 0)
                return -1;
            //标记
            Date *pDel = pListQueue->pHead;
            int nNum = pDel->nValue;
            //移动
            pListQueue->pHead = pListQueue->pHead->pNext;
            free(pDel);
            pDel = NULL;
            pListQueue->nCount--;
        
            if (pListQueue->nCount == 0) {
                  
                pListQueue->pTail = NULL;
            }
        
            return nNum;
        }
        
        int IsEmpty(ListQueue *pListQueue){
                  
            if (pListQueue == NULL) {
                  
                printf("queue is not exist.\n");
                exit(1);
            }
            return pListQueue->nCount == 0? 1: 0;
        }
        
        int main()
        {
                  
            ListQueue *pListQueue = NULL;
            Init(&pListQueue);
        
            Push(pListQueue, 1);
            Push(pListQueue, 2);
            Push(pListQueue, 3);
            Push(pListQueue, 4);
            printf("%d\n",IsEmpty(pListQueue));
        
            printf("%d\n",Pop(pListQueue));
            printf("%d\n",Pop(pListQueue));
            printf("%d\n",Pop(pListQueue));
            printf("%d\n",Pop(pListQueue));
            printf("%d\n",IsEmpty(pListQueue));
        
            return 0;
        }
        
    • 双端队列

    • 优先级队列:(后面再学~)

    • 循环队列:

      (用数组实现,从尾到头的时候用 r+1%n ,解决假溢出的问题)

      • r+1%n
      • 首先假设数组长度n=5,r=0表示数据在第一格啦,如此类推r=5的时候数组放满了,r再往下加的话就要变成6了,而6是在数组外了,要放在数组的第1格才对,6 ÷ 5 = 1 余 1,这个余数就是这个数据要放的位置的下标,也就是第一格,r再往下加就是7了,7 ÷ 5 = 1 余 2,这个数据就要放在数组的第二格,也就是r%n。但因为数组的索引是从0开始算,而数组的大小n是从1开始算的,所以r要先加1再对5取余数,也就是r+1%n。

      • 循环队列如何判断队列为空or为满?
        • 当队列空时,条件就是from = rear,当队列满时,我们修改其条件,保留一个元素空间。也就是说,队列满时,数组中还有一个空闲单元。 如下图所示,我们就认为此队列已经满了。

          img

          由于rear可能比front大,也可能比front小,所以尽管它们只相差一个位置时就是满的情况,但也可能是相差整整一圈。所以若队列的最大尺寸为QueueSize,那么队列满的条件是(rear+1) %QueueSize == front (取模“%的目的就是为了整合rear与front大小为一个问题)。

          比如上面这个例子, QueueSize = 5,当 front=0,而 rear=4, (4+1) %5 = 0,所以此时队列满。再比如,front = 2而rear =1。(1 + 1) %5 = 2,所以此时 队列也是满的。而对于下图, front = 2而rear= 0, (0+1) %5 = 1,1!=2,所以此时队列并没有满。

          img

          另外,当rear > front时,此时队列的长度为rear—front。但当rear < front时,队列长度分为两段,一段是QueueSize-front,另一段是0 + rear,加在一起,队列长度为rear-front + QueueSize,因此通用的计算队列长度公式为:(rear—front + QueueSize) % QueueSize

        • 总结:

          • 队空条件:front == rear
          • 队满条件:(rear+1) %QueueSize == front
          • 队列长度:(rear—front + QueueSize) % QueueSize
        • 循环队列中各个参数的含义:

        (1)队列初始化时,front和rear值都为零;

        (2)当队列不为空时,front指向队列的第一个元素,rear指向队列最后一个元素的下一个位置;

        (3)当队列为空时,front与rear的值相等,但不一定为零;

        • 其他判断队列空or满的方法:

          (1)设置一个标志变量flag,当front == rear且flag = 0时为队列空,当front == rear且flag= 1时为队列满。

          (2)可以用一个变量index来计数,如果index=n则为满,index=0则为空,此时r不需要指向后一个元素。

栈和队列 相关题目

括号匹配问题

一万个括号 判断是否完全匹配?

  • 方案:

  • ① 利用栈

    遍历一万个括号,左括号就入栈,遇到一个右括号就将一个左括号出栈,如果右括号没有对应的左括号可以出栈,则匹配失败,如果全部遍历完后栈内还有左括号剩余,也匹配失败。只有过程中没有失败,结束后也没剩余,则匹配成功。

  • ② 计数器(时间消耗和栈一样,思路和栈一样,但没申请新的空间,空间消耗低,所以该方案比较好)

    碰到左括号+1,碰到右括号-1,如果出现负数就失败,如果操作完成后其值不为0,也失败,只有过程中不出现负数且结束后为0,则成功。

    #include <stdio.h>
    #include <string.h>
    
    int BracketsMatch(const char *strBrackets){
          
        if (strBrackets == NULL)    //空 没有括号 所以不匹配
            return 0;
        
        int i = 0;
        int nCount = 0;
    
        while (i < strlen(strBrackets)) {
          
            //计 ( 的数量
            if (strBrackets[i] == '(') {
          
                nCount++;
            }else if (strBrackets[i] == ')'){
          
                //遇到 )与左括号进行匹配 抵消一个
                nCount--;
                //左括号少于右括号 匹配不成功
                if (nCount < 0) {
          
                    return 0;
                }
            } 
            //遍历下一个括号
            i++;
        }
        //左括号数量是否多于右括号
        return nCount > 0 ? 0 : 1;  
                    //如果大于0则不匹配(0),否则匹配(1)
    }
    
    int main()
    {
          
        int nResult = BracketsMatch("()()()((()))");
        printf("%d\n",nResult);
        return 0;
    }
    

约瑟夫环问题

约瑟夫环

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。例如,1、2、3、4、5这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是3、1、5、2,因此最后剩下的数字是4。

方案:

  • ① 循环链表

    遍历,利用计数器判断是否为第m个,是的话把第m个节点删掉,然后计数器置为0重新开始计数,重复该过程,直到循环链表里只剩下一个数字。

  • ② 队列

    先全部入队,然后一个个出队并用计数器判断是否为第m个数字,如果不是则出队后再入队,如果是的话则删除,注意计数器到m后需要清0然后重新计数,如此反复直到队列中只剩下一个数字。

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct date{
          
        int nValue;
        struct date *pNext;
    }Date;
    
    typedef struct queue{
          
        int nCount;
        Date *pHead;
        Date *pTail;
    }ListQueue;
    
    void Init(ListQueue **pListQueue){
          
        *pListQueue = (ListQueue*)malloc(sizeof(ListQueue));
        (*pListQueue)->nCount = 0;
        (*pListQueue)->pHead = NULL;
        (*pListQueue)->pTail = NULL;
    }
    
    void Push(ListQueue *pListQueue,int nNum){
          
        if (pListQueue == NULL) {
          
            printf("queue is not exist.\n");
            exit(1);
        }
        Date *pTemp = NULL;
        pTemp = (Date*)malloc(sizeof(Date));
        pTemp->nValue = nNum;
        pTemp->pNext = NULL;
    
        if (pListQueue->pHead == NULL) {
          
            pListQueue->pHead = pTemp;
        }else {
          
            pListQueue->pTail->pNext = pTemp;
        }
        pListQueue->pTail = pTemp;
        pListQueue->nCount++;
    }
    
    int Pop(ListQueue * pListQueue){
          
        if (pListQueue == NULL) {
          
            printf("queue is not exist.\n");
            exit(1);
        }
        if (pListQueue->nCount == 0)
            return -1;
        //标记
        Date *pDel = pListQueue->pHead;
        int nNum = pDel->nValue;
        //移动
        pListQueue->pHead = pListQueue->pHead->pNext;
        free(pDel);
        pDel = NULL;
        pListQueue->nCount--;
    
        if (pListQueue->nCount == 0) {
          
            pListQueue->pTail = NULL;
        }
        return nNum;
    }
    
    int JosephRing(int n,int k){
          
        //辅助队列
        ListQueue * pQueue = NULL;
        Init(&pQueue);
    
        //计数器
        int nCount = 0;
    
        //入队
        int i = 1;
        while (i <= n) {
          
            Push(pQueue, i);
            i++;
        }
    
        //出环
        while (pQueue->nCount > 1) {
          
            //弹出 计数
            i = Pop(pQueue);
            nCount++;
            //是否满足出环条件
            //满足则计数器置为0,无需重新入队 
            if (nCount == k) {
          
                nCount = 0;
                continue;
            }else {
           //不满足,元素重新入队
                Push(pQueue, i);
            }
        }
        i = Pop(pQueue);
        return i;
    }
    
    int main()
    {
          
        int nResult = JosephRing(5, 3);
        printf("%d\n",nResult);
        return 0;
    } 
    
  • ③ 数组

    两个计数器,一个用于计是否为第m个,另一个看数组内剩余的没有处理的个数,第m个数置-1,循环遍历,直到整个数组只有一个数没有被处理。

队列和栈的相互转换

如何用两个栈实现队列的先进先出功能?

  • S1 输入栈;S2 输出栈

  • 如果入栈,那么判断S2是否有元素存在,如果有,把S2的先依次放入S1,然后再将新元素放入S1

  • 如果出栈,那么判断S1是否有元素存在,如果有,把S1的先依次放入S2,然后再将S2的元素出栈

  • #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct node{
          
        int nvalue;
        struct node *pNext;
    }Date;
    
    typedef struct stack{
          
        int nConut;
        Date *pTop;
    }Stack;
    
    typedef struct queue{
          
        Stack *pStack1;
        Stack *pStack2;
    }Queue;
    
    void Init(Stack **pStack){
          
        *pStack = (Stack*)malloc(sizeof(Stack));
        (*pStack)->nConut = 0;
        (*pStack)->pTop = NULL;
    }
    
    void Push(Stack *pStack,int nNum){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
    
        Date *pTemp = NULL;
        pTemp = (Date*)malloc(sizeof(Date));
        pTemp->nvalue = nNum;
    
        pTemp->pNext = pStack->pTop;
        pStack->pTop = pTemp;
        pStack->nConut++;
    }
    
    int Pop(Stack *pStack){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
    
        if (pStack->nConut == 0)    
            return -1;
    
        Date *pDel = NULL;
        pDel = pStack->pTop;
        int nNum = pDel->nvalue;
    
        pStack->pTop = pStack->pTop->pNext;
        free(pDel);
        pStack->nConut--;
        return nNum;
    }
    
    int IsEmpty(Stack *pStack){
          
        if (pStack == NULL) {
          
            printf("stack is not exist!\n");
            exit(1);
        }
        return pStack->nConut == 0 ? 1 : 0; 
    }
    
    void q_Init(Queue **pQueue){
          
        *pQueue = (Queue*)malloc(sizeof(Queue));
        Init(&(*pQueue)->pStack1);
        Init(&(*pQueue)->pStack2);
    }
    
    void q_Push(Queue *pQueue,int nNum){
          
        //栈1入队
        //考虑栈2是否非空   如果非空,将栈2元素压入栈1
        while (!IsEmpty(pQueue->pStack2)) {
          
            Push(pQueue->pStack1,Pop(pQueue->pStack2));
        }
        //新元素入栈1
        Push(pQueue->pStack1,nNum);
    }
    
    int q_Pop(Queue *pQueue){
          
        //栈2出队
        //考虑栈1是否非空   如果非空,将栈1元素依次放入栈2
        while (!IsEmpty(pQueue->pStack1)) {
          
            Push(pQueue->pStack2,Pop(pQueue->pStack1));
        }
        //将栈2栈顶元素弹出
        int nNum = Pop(pQueue->pStack2);
        return nNum;
    }
    
    int main(){
          
        Queue *pQueue = NULL;
        q_Init(&pQueue);
    
        q_Push(pQueue, 1);
        q_Push(pQueue, 2);
        q_Push(pQueue, 3);
        q_Push(pQueue, 4);
    
        printf("%d\n",q_Pop(pQueue));
        printf("%d\n",q_Pop(pQueue));
        q_Push(pQueue, 5);
        q_Push(pQueue, 6);
        printf("%d\n",q_Pop(pQueue));
        printf("%d\n",q_Pop(pQueue));
        printf("%d\n",q_Pop(pQueue));
        printf("%d\n",q_Pop(pQueue));
    
        return 0;
    }
    

如何用两个队列实现栈的先进后出功能?

  • 比如有两个队列 q1 和 q2 ,将 1 2 3 4 放入队列 q1,我们希望得到 4 3 2 1 的出队情况(类似于栈的先进后出),因此先将1 2 3出队放入q2中,然后把q1中的4出队,再将1 2出队放入q1中,然后再把q2中的3出队,由此类推循环,得到4 3 2 1
  • 中途入队时,注意先完成当次操作,然后将元素入到有元素的队列中,让所有元素在一个队列中,否则会乱。
#include <stdio.h>
#include <stdlib.h>

typedef struct node{
    
    int nvalue;
    struct node *pNext;
}Date;

typedef struct stack{
    
    int nConut;
    Date *pTop;
}Stack;

typedef struct queue{
    
    Stack *pStack1;
    Stack *pStack2;
}Queue;

void Init(Stack **pStack){
    
    *pStack = (Stack*)malloc(sizeof(Stack));
    (*pStack)->nConut = 0;
    (*pStack)->pTop = NULL;
}

void Push(Stack *pStack,int nNum){
    
    if (pStack == NULL) {
    
        printf("stack is not exist!\n");
        exit(1);
    }

    Date *pTemp = NULL;
    pTemp = (Date*)malloc(sizeof(Date));
    pTemp->nvalue = nNum;

    pTemp->pNext = pStack->pTop;
    pStack->pTop = pTemp;
    pStack->nConut++;
}

int Pop(Stack *pStack){
    
    if (pStack == NULL) {
    
        printf("stack is not exist!\n");
        exit(1);
    }

    if (pStack->nConut == 0)    
        return -1;

    Date *pDel = NULL;
    pDel = pStack->pTop;
    int nNum = pDel->nvalue;

    pStack->pTop = pStack->pTop->pNext;
    free(pDel);
    pStack->nConut--;
    return nNum;
}

int IsEmpty(Stack *pStack){
    
    if (pStack == NULL) {
    
        printf("stack is not exist!\n");
        exit(1);
    }
    return pStack->nConut == 0 ? 1 : 0; 
}

void q_Init(Queue **pQueue){
    
    *pQueue = (Queue*)malloc(sizeof(Queue));
    Init(&(*pQueue)->pStack1);
    Init(&(*pQueue)->pStack2);
}

void q_Push(Queue *pQueue,int nNum){
    
    //栈1入队
    //考虑栈2是否非空   如果非空,将栈2元素压入栈1
    while (!IsEmpty(pQueue->pStack2)) {
    
        Push(pQueue->pStack1,Pop(pQueue->pStack2));
    }
    //新元素入栈1
    Push(pQueue->pStack1,nNum);
}

int q_Pop(Queue *pQueue){
    
    //栈2出队
    //考虑栈1是否非空   如果非空,将栈1元素依次放入栈2
    while (!IsEmpty(pQueue->pStack1)) {
    
        Push(pQueue->pStack2,Pop(pQueue->pStack1));
    }
    //将栈2栈顶元素弹出
    int nNum = Pop(pQueue->pStack2);
    return nNum;
}

int main(){
    
    Queue *pQueue = NULL;
    q_Init(&pQueue);

    q_Push(pQueue, 1);
    q_Push(pQueue, 2);
    q_Push(pQueue, 3);
    q_Push(pQueue, 4);

    printf("%d\n",q_Pop(pQueue));
    printf("%d\n",q_Pop(pQueue));
    q_Push(pQueue, 5);
    q_Push(pQueue, 6);
    printf("%d\n",q_Pop(pQueue));
    printf("%d\n",q_Pop(pQueue));
    printf("%d\n",q_Pop(pQueue));
    printf("%d\n",q_Pop(pQueue));

    return 0;
}

如何找到链表的倒数第k个节点

方法一:暴力

  • 遍历链表获得当前表的长度n

  • 正数的n-k个就是倒数的第k个

  • 注意:比较n和k的大小关系,是否k>n

方法二:双指针

  • 1指针先走k步,注意检测有没有结束(k是不是超出了n)
  • 12指针同步一起走,先走的1指针走到尾的时候,后出发的2指针就走到k的位置了
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/h0327x/article/details/124255886

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签