数据结构学习记录(一)

堆栈与队列

一、知识要点

1、堆栈

  • 堆栈的定义

    • 堆栈(Stack)是一种具有一定约束的线性表,插入和删除操作都作用在一个称为栈顶(Top)的端点位置。
    • 通常把数据插入称为压入栈(Push),删除数据称为弹出栈(Pop)。在堆栈中,最后入栈的数据被最先弹出,所以堆栈也被称为后入先出
    • 堆栈的抽象数据类型定义:
      • 类型名称:堆栈(Stack)
      • 数据对象集:一个有0个或多个元素有穷线性表
      • 操作集:对于一个具体长度为正整数Maxsize的堆栈S,记堆栈中任一元素ElementType,堆栈的基本操作有:
        • Stack CreateStack(int MaxSize):生成空堆栈,其最大长度为MaxSize。
        • bool IsFull(Stack S) :判断堆栈S是否已满。满则返回true,否则返回false。
        • bool Push(Stack S, ElementType X):若S已满,则返回false,否则将元素X压入栈顶,返回true。
        • bool IsEmpty(Stack S):判断堆栈S是否为空,是则返回true,否则返回false。
        • ElementType Pop(Stack S):删除并返回栈顶元素。
  • 堆栈的实现

    • 栈的顺序储存实现

      #include<stdio.h>
      #include<string.h>
      #include<stdbool.h>
      #include<stdlib.h>
      #include<errno.h>
      //顺序栈的数据结构
      struct SNode
      {
          ElementType *Data;//存储元素的数组
          int Top;//栈顶指针
          int MaxSize;//堆栈最大容量
      };
      typedef struct SNode *Stack;
      
      //生成空堆栈
      Stack CreateStack(int MaxSize)
      {
          Stack S = (Stack)malloc(sizeof(struct SNode));//为堆栈开辟空间
          S->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));//给所有元素开辟空间
          S->Top = -1;
          S->MaxSize = MaxSize;
          return S;
      }
      
      //判断栈满
      bool IsFull(Stack S)
      {
          if(S->Top == S->MaxSize-1)
              return true;
          else
              return false;
      }
      
      //入栈
      bool Push(Stack S, ElementType X)
      {
          if(IsFull(S))
          {
              printf("堆栈满\n");
              return false;
          }
          else
          {
              S->Data[(S->Top)] = X;
              (S->Top)++;
              return true;
          }
      }
      
      //判断栈空
      bool IsEmpty(Stack S)
      {
          return (S->Top == -1)
      }
      
      //出栈
      ElementType Pop(Stack S)
      {
          if(IsEmpty(S))
          {
              printf("堆栈空\n");
             	return ERROR;
          }
          else
          {
              return (S->Data[S->Top]--)
          }
      }
      
    • 栈的链式储存结构

      #include<stdio.h>
      #include<string.h>
      #include<stdbool.h>
      #include<stdlib.h>
      #include<errno.h>
      
      //链式栈的数据结构
      typedef struct SNode *Stack;
      struct SNode
      {
          ElementType Data;
          Stack Next;
      };
      
      //创建堆栈的头节点
      Stack CreateStack()
      {
          Stack S;
          S = (Stack)malloc(sizeof(struct SNode));//给头节点分配空间
          S->Next = NULL;
          return S;
      }
      
      //判断堆栈空
      bool Empty(Stack S)
      {
          return (S->Next == NULL);
      }
      
      //入栈
      bool Push(Stack S, ElementType X)
      {
          //创建新节点,将新节点放在头节点后
          Stack TmpCell;
          TmpCell = (Stack)malloc(sizeof(struct SNode));//为新节点分配空间
          TmpCell->Data = X;
          TmpCell->Next = S->Next;
          S->Next = TmpCell;
          return true;
      }
      
      //出栈
      ElementType Pop(Stack S)
      {
          //返回栈顶元素并删除
          Stack FirstCell;
          ElementType TopCell;
          
          if(Empty(S))
          {
              printf("堆栈空\n");
              return ERROR;
          }
          else
          {
              FirstCell = S->Next;
              TopCell = FirstCell->Data;
              S->Next = FirstCell->Next;
              free(FirstCell);//释放栈顶元素内存
              return TopCell;
          }
      }
      

2、队列

  • 队列的定义

    • 队列(Queue)也是一个有序线性表,但队列的插入和删除操作是分别在线性表的两个不同端点同时进行的
    • 如果将元素A,B,C,D依次插入队列,那么第一个从队列删除的元素将是A,即先插入的将被率先删除,因此队列通常被称为先进先出表
    • 堆栈的抽象数据类型定义:
      • 类型名称:队列(Queue)
      • 数据对象集:一个有0个或多个元素有穷线性表
      • 操作集:对于一个具体长度为正整数Maxsize的队列S,记堆栈中任一元素ElementType,堆栈的基本操作有:
        • Queue CreateQueue(int MaxSize):生成空队列,其最大长度为MaxSize。
        • bool IsFull(Queue Q):判断队列Q是否已满,是则返回true,否则返回false。
        • bool AddQ(Queue Q, ElementType X):将元素X压入队列Q。若队列满返回false,否则返回true。
        • bool IsEmpty(Queue Q):判断队列Q是否为空。
        • ElementType DeleteQ(Queue Q):删除并返回队列头元素,若队列为空则返回错误信息。
  • 队列的实现

    • 队列的顺序储存实现

      //队列最简单的实现方法是用数组
      //要保证元素不会在数组溢出,就要把数组首尾相连
      #include<stdio.h>
      #include<string.h>
      #include<stdbool.h>
      #include<stdlib.h>
      #include<errno.h>
      
      //创建队列顺序数据结构
      struct QNode
      {
          ElementType* Data;	//存储元素的数组
          int Front, Rear;	//队列的头、尾指针
          int MaxSize;		//队列最大容量
      };
      typedef struct QNode *Queue;
      
      //创建空队列
      Queue CreateQueue(int MaxSize)
      {
          Queue Q = (Queue)malloc(sizeof(struct QNode));	//为队列赋予空间
          Q->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));		//为数组赋予空间
          Q->Front = Q->Rear = 0;
          Q->MaxSize = MaxSize;
          return Q;
      }
      
      //判断队列是否已满
      bool IsFull(Queue Q)
      {
          return ((Q->Rear + 1) % Q->MaxSize == Q->Front);
      }
      
      //压入队列
      bool AddQ(Queue Q, ElementType X)
      {
          if(IsFull(Q))
          {
              printf("队列满\n");
              return false;
          }
          else
          {
              Q->Rear = (Q->Rear + 1) % Q->MaxSize;
              Q->Data[Q->Rear] = X;
              return ture;
          }
      }
      
      //判断队列为空
      bool Empty(Queue Q)
      {
          return (Q->front == Q->Rear);
      }
      
      //返回并删除队列头元素
      ElementType DeleteQ(Queue Q)
      {
          if(Empty(Q))
          {
              printf("队列空\n");
              return ERROR;
          }
          else
          {
              Q->Front = (Q->Front + 1) % Q->MaxSize;
              return Q->Data[Q->Front];
          }
      }
      
    • 队列的链式储存实现

      //队列的头(front)必须指向链表的头节点,尾(rear)必须指向链表的尾节点
      //不能反过来,如果反过来,front就无法找到上一个节点
      
      #include<stdio.h>
      #include<string.h>
      #include<stdbool.h>
      #include<stdlib.h>
      #include<errno.h>
      
      //创建队列链式数据结构
      struct Node
      {
          ElementType Data;
          struct Node *Next;
      };
      typedef struct Node *Position;
      
      //创建队列的头尾指针
      struct QNode
      {
          Position Front, Rear;
      };
      typedef struct QNode *Queue;
      
      //创建空队列
      Queue CreateQueue()
      {
          Queue Q = (Queue)malloc(sizeof(struct QNode));
          Q->Front = Q->Rear = NULL;
          return Q;
      }
      
      //判断队列是否为空
      bool IsEmpty(Queue Q)
      {
          return (Q->Front == NULL);
      }
      
      //压入队列
      bool AddQ(Queue Q, ElementType X)
      {
          //创建新节点
          Position RearCell = (Position)malloc(sizeof(struct Node));
          RearCell->Data = X;
          RearCell->Next = NULL;
          //当队列为空时
          if(IsEmpty(Q))
          {
              Q->Front = Q->Rear = RearCell;
          }
          else
          {
              Q->Rear->Next = RearCell;
              Q->Rear = RearCell;
          }
          return true;
      }
      
      //弹出队列
      ElementType DeleteQ(Queue Q)
      {
          Position FrontCell;
          ElementType FrontElem;
          
          //当队列为空时,返回错误信息
          if(Empty(Q))
          {
              printf("队列空\n");
              return ERROR;
          }
          else
          {
              FrontCell = Q->Front;
              //当队列只有一个元素时
              if(Q->Front == Q->Rear)
              {
                  Q->Front = Q->Rear = NULL;
              }
              else
              {
                  Q->Front = Q->Front->Next;
              }
              FrontElem = FrontCell->Data;
              
              //释放节点内存
              free(FrontCell);
              return FrontElem;
          }
      }
      

3、应用实例

  • 多项式加法计算

  • #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <malloc.h>
    #include <stdlib.h>
    //比较函数
    int Compare(int a, int b);
    //置尾函数
    void Attach(int a, int b, Polynomial *p);
    
    //多项式加法运算
    
    //创建多项式数据类型
    struct PolyNode
    {
        //系数
        int coef;
        //指数
        int expon;
        //指向下一个节点的指针
        struct PolyNode *next;
    };
    typedef struct PolyNode *Polynomial;
    Polynomial p1, p2;
    
    //置尾函数
    void Attach(int a, int b, Polynomial p)
    {
        //创建多项式节点
        Polynomial P;
        //为新节点赋予内存和赋值
        P = (Polynomial)malloc(sizeof(struct PolyNode));
        P->coef = a;
        P->expon = b;
        P->next = NULL;
        p->next = P;
        p = P;
    }
    //多项式相加函数
    Polynomial PolyAdd(Polynomial p1, Polynomial p2)
    {
        //创建和多项式链表头节点和尾节点
        Polynomial front, rear, temp;
        //定义系数和变量
        int sum;
        //给头节点赋予内存
        front = (Polynomial)malloc(sizeof(struct PolyNode));
        rear = front;
        //当p1和p2都没加完时
        while (p1 != NULL && p2 != NULL)
        {
            //对p1指向节点的指数和p2的相比较
            switch(Compare(p1->expon, p2->expon))   //Compare函数需自己实现
            //当p1指向节点的指数大于p2时
            case 1:
                //将p1指向的节点放到和多项式链表表尾
                Attach(p1->coef, p1->expon, rear);
                //将p1向后移
                p1 = p1->next;
                break;
            //当p1指向节点的指数小于p2时
            case -1:
                //将p2指向的节点放到和多项式链表表尾
                Attach(p2->coef, p2->expon, rear);
                //将p2向后移
                p2 = p2->next;
                break;
            //当p1指向节点的指数等于于p2时
            case 0:
                //将p1和p2所指向节点的系数相加
                sum = p1->coef + p2->coef;
                //如果和不为0
                if (sum != 0)
                    Attach(sum, p1->expon, rear);
                //将p1和p2向后移
                p1 = p1->next;
                p2 = p2->next;
                break;
        }
        //如果某个多项式加完了,那么就将另一个多项式剩余的项放在和多项式后面
        if (p1 == NULL)
        {
            Attach(p2->coef, p2->expon, rear);
            p2 = p2->next;
        }
        if (p2 = NULL)
        {
            Attach(p1->coef, p1->expon, rear);
            p1 = p1->next;
        }
        //将和多项式尾部节点指向空
        rear->next = NULL;
        //销毁头节点,并返回和多项式表头
        temp = front;
        front = front->next;
        free(temp);
        return front;
    }
    
  • 迷宫问题

    • 应用堆栈求解迷宫路径问题基本思路如下:

      • 将初始入口坐标和起始方向信息放入堆栈中。
      • 从堆栈中弹出上次位置信息,设定当前位置和当前尝试方向;若堆栈为空而出口未找到,则该迷宫没有解,程序退出。
      • 在当前位置,从当前方向按顺序尝试剩余方向上的可通行:
        • 若某一方向可通,则将当前位置信息及目前方向信息存入堆栈;
        • 若该可通位置是出口,则成功退出,堆栈中的从栈顶到栈底的各位置顺序构成迷宫路径;
        • 若该可通位置不是出口,将可通位置设为当前位置,并将第一个方向设为当前方向;转到第三步
      • 若8个方向均不可通,则转第二步
    • //相应数据结构为:
      #define MAXMATRIXSIZE 100	//迷宫矩阵最大行列数
      #define MAXSTACKSIZE 100	//堆栈最大规模
      
      //偏移量结构定义
      struct Offsets
      {
          short int Vert;	//纵向偏移
          short int Horiz;	//横向偏移
      };
      
      //迷宫中位置结构
      struct MazePosition
      {
          short int Row;	//行号
          short int Col;	//列号
          short int Dir;	//对应偏移量数组的方向号
      };
      typedef struct MazePosition ElementType;	//堆栈元素类型
      
      //设计一个寻找迷宫路径的函数Path
      void Path(int Maze[][MAXMATRIXSIZE], int EXITROW, int EXITCOL)
      {
          //默认迷宫Maze的入口为(1,1),出口为(EXITROW,EXITCOL)
          //迷宫八个方向的偏移量数组
          struct Offsets Move[8] = {{-1, 0}, {-1, -1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
          int Mark[MAXMATRIXSIZE][MAXMATRIXSIZE];	//标记位置是否走过
          Stack S;	//辅助求解的堆栈
          struct MazPosition P;
          short Row, Col, NextRow, NextCol, Dir;
          bool Found = false;
          
          S = CreateStack(MAXMATRIXSIZE);		//初始化堆栈
          
          Mark[EXITROW][EXITCOL] = 1;	//从出口位置开始标记为走过
          
          //将出口位置及下一个方向放入堆栈
          P.Row = EXITROW;
          P.Col = EXITROW;
          P.Dir = 0;
          Push(S, P);
          
          while(!IsEmpty(S) && !Found)
          {
              //当堆栈非空且没找到入口时
              P = Pop(S);	//取出栈顶元素为当前位置
              Row = P.Pow;
              Col = P.Col;
              Dir = P.Dir;
              
              while(Dir < 8 && !found)
              {
                  //当方向可探且没找到入口时
                  //尝试往下一个方向Dir移动
                  NextRow = Row + Move[Dir].Vert;
                  NextCol = Col + Move[Dir].Horiz;
                  if(NextRow == 1 && NextCol == 1)
                  {
                      //如果到达入口,则标记为找到
                      Found = true;
                  }
                  else
                  {
                      //如果下一个位置不是出口
                      if(!Maze[NextRow][NextCol] && !Mark[NextRow][NextCol])
                      {
                          //并且可通还没走过
                          Mark[NextRow][NextCol] = 1;	//标记为走过
                          //将当前位置和下一个方向存入栈
                          P.Row = Row;
                          P.Col = Col;
                          P.Dir = Dir+1;
                          Push(S, P);
                          //更新当前位置,从方向0开始
                          Row = NextRow;
                          Col = NextCol;
                          Dir = 0;
                      }
                      else
                          Dir++;
                  }//结束八方向探索
              }//结束搜索
              if(Found)
              {
                  //找到一个路径,并输出该路径
                  printf("找到路径如下\n");
                  peintf("行 列\n");
                  printf("1 1\n");	//打印入口
                  printf("%d %d\n", Row, Col);	//不要忘记最后一步未入栈
                  while(!IsEmpty(S))
                  {
                      P = Pop(S);
                      printf("%d %d\n", P.Row, P.Col);
                  }
              }
              else	//若没找到路径
                  printf("该迷宫无解。\n");
          }
      }
      

二、学习心得

呵呵哒

posted on 2023-08-19 23:34  嗷呜ニャー~  阅读(38)  评论(0编辑  收藏  举报