栈和队列学习

栈和队列学习

使用到的链表函数

  • 链式队列和链式栈都用到了链表的一些函数,在这里声明
// 定义链表
struct Node {
  int data;
  struct Node* next;
};

// 创建链表头
struct Node* createList() {
  struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
  headNode->next = NULL;
  return headNode;
}

// 创建节点 (和创建链表头基本相同)
struct Node* createNode(int data) {
  struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  newNode->data = data;
  newNode->next = NULL;
  return newNode;
}

链式栈 (Stack)

  • 栈:先进后出
  • 和链表的表头插入法特别像 (和链表唯一的区别就是栈没有头节点)

链式栈的定义

  • 链式栈和链表特别像
  • 代码示例:
// 定义链表
struct Node {
  int data;
  struct Node* next;
};

// 定义栈
struct Stack {
  struct Node* topStack;
  int size;
};
  • 在定义链表的外部新增栈顶指针 (topStack) 和 栈的元素个数 (size)

初始化栈

  • 为栈分配内存空间,初始化栈的两个值
  • 代码示例:
// 初始化栈
struct Stack* createStack() {
  // 分配内存空间
  struct Stack* newStack = (struct Stack*)malloc(sizeof(struct Stack));
  // 初始化两个值
  newStack->size = 0;
  newStack->topStack = NULL;
  return newStack;
}

入栈 (push)

  • 栈比较特殊,他没有头节点,第一个入栈的相当于链表的最后一个元素 (很符合先进后出的原理)

  • 然后类似于头插法,只不过没有头节点,所以只需要插入的元素指向下一个元素,不需要有其他元素指向它 (直到下一个元素要入栈时)

  • 代码示例:

// 入栈操作(push)
void pushStack(struct Stack* stack,int data) {
  // 新建一个节点
  struct Node* newNode = createNode(data);
  // 新节点指向上一个入栈的元素
  newNode->next = stack->topStack;
  // 栈顶指针指向新入栈的元素
  stack->topStack = newNode;
  // 栈的大小加一
  stack->size++;
}
  • 看这个图片应该能更好理解
    Stack

读取栈顶元素

  • 因为栈顶指针永远都指向栈顶,所以直接利用它就可以输出栈顶元素了
  • 代码示例:
// 读取栈顶元素
int topStack(struct Stack* stack) {
  // 栈不能为空
  if (stack->size == 0)
    return NULL;
  return stack->topStack->data;
}

出栈 (pop)

  • 类似于链表的删除,而且是只能从链表头开始删除的那种
  • 删除步骤就是先将下一个元素的地址用一个中间变量指针保存起来,然后栈顶指针 (topStack) 指向下一个元素,然后 free 掉上一个节点就可以了
  • 通过判断 size 的值来判断链表是否为空
  • 代码示例:
// 出栈
void popStack(struct Stack* stack) {
  if (stack->size == 0)
    cout << "栈为空,无法出栈" << endl;
  else
  {
    // 先临时保存下一个节点的地址
    struct Node* nextNode = stack->topStack->next;
    // free掉上一个节点
    free(stack->topStack);
    // 栈顶指针指向下一个节点
    stack->topStack = nextNode;
    nextNode = NULL;
    stack->size--;
  }
}

栈的主函数

  • 测试栈的函数:
int main() {
  struct Stack* myStack = createStack();
  for (int i = 0; i < 5; i++) {
    pushStack(myStack, i);
  }
  for (int i = 0; i < 5; i++) {
    cout << topStack(myStack) <<"\t"<< endl;
    popStack(myStack);
  }
  return 0;
}

// 输出结果
// 4
// 3
// 2
// 1
// 0

链式队列

  • 队列:先进先出
  • 和尾插法特别像 (同样没有头节点)

链式队列的定义

  • 创建一个结构体,里面包括头节点,尾节点和队列长度
  • 代码示例:
// 定义队列
struct Queue {
  struct Node* frontNode; // 头节点
  struct Node* tailNode;  // 尾节点
  int size;  // 队列大小
};

创建队列

  • 分配内存空间,并且给里面的数据赋初值
  • 代码示例:
// 创建队列
struct Queue* createQueue() {
  // 分配内存空间
  struct Queue* myQueue = (struct Queue*)malloc(sizeof(struct Queue));
  // 赋初值
  myQueue->frontNode = myQueue->tailNode = NULL;
  myQueue->size = 0;
  return myQueue;
}

入队

  • 入队分成两种情况,一种队为空时,另一种队不为空时
    • 队为空时入队队里只有一个元素,这时头节点和尾节点都指向那唯一一个元素
    • 对不为空时头节点指向头元素,尾节点指向尾元素
  • 代码示例:
// 入队
void pushQueue(struct Queue* myQueue,int data) {
  // 新建一个节点
  struct Node* newNode = createNode(data);
  // 如果队为0,那么头节点和尾节点都指向第一个入队的元素
  if (myQueue->size == 0) {
    myQueue->frontNode = myQueue->tailNode = newNode;
  }
   // 队里面有两个及以上的元素时,头节点不变,尾节点向后移动一位
  else {
    myQueue->tailNode->next = newNode;
    myQueue->tailNode = newNode;
    myQueue->size++;
  }
  // 队列长度加一
  myQueue->size++;
}

出队

  • 先把队头元素的下一个元素的地址储存起来,然后 free 掉队头元素 (frontNode指向的元素),然后队头指针 (frontNode) 指向刚开始储存的地址
  • 代码示例:
// 出队
void popQueue(struct Queue* myQueue) {
  if (myQueue->size == 0) {
    cout << "栈为空" << endl;
  }
  else
  {
    // 暂存下一个元素地址
    struct Node* nextNode = myQueue->frontNode->next;
    free(myQueue->frontNode);
    // 指向刚才储存的元素地址
    myQueue->frontNode = nextNode;
    myQueue->size--;
  }
}

获取队顶元素

  • 先判断一下队是否为空,然后返回 frontNode 指向的元素就可以了
  • 代码示例:
// 获取队顶元素
int frontQueue(struct Queue* myQueue) {
  if (myQueue->size == 0) {
    cout << "栈为空" << endl;
    return NULL;
  }
  else {
    return myQueue->frontNode->data;
  }
}

队列主函数

  • 测试上面所写的函数
int main() {
  struct Queue* myQueue = createQueue();
  for (int i = 0; i < 5; i++) {
    pushQueue(myQueue, i);
  }
  // myQueue = {0,1,2,3,4}
  for (int i = 0; i < 5; i++) {
    cout << frontQueue(myQueue) << endl;
    popQueue(myQueue);
  }
  return 0;
}

// 输出结果:
// 0
// 1
// 2
// 3
// 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值