数据结构与算法01--堆栈 & 队列

  1. 基于顺序表的堆栈
  1. 基于链式表的堆栈

1 基于顺序表的堆栈

栈是一种特殊的线性表,是限定在线性表表尾进行插入删除操作的线性表。由栈的概念衍生出几个子概念,它们是:

1)栈顶,即允许进行插入、删除操作的一端,又称为表尾,用栈顶指针(top)来指示栈顶元素。

2)栈底,即固定端,又称为表头

3)空栈,即栈当中没有数据元素。

顺序栈是采用顺序存储结构的栈,即使用一组连续的存储单元(一般使用数组)来模拟栈,依次存放栈中的数据元素。

1.1 方案

顺序栈的基本操作包括:

1) 初始化操作,在初始化操作中将建立一个空栈。

2) 判断栈空,判断栈中的数据元素个数是否为0。

3) 入栈,在栈中加入一个数据元素。

4) 出栈,在栈中删除一个数据元素。

5) 取栈顶元素,将栈顶元素取出,但并不在栈中删除该元素。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义栈

在C语言中:

1)定义一个一维数组来表示栈的顺序存储空间。

2)定义一个变量来指出栈顶的位置。

3)这两方面的信息共同描述一个栈,可将它们用结构体封装在一起。

代码如下:


    
    
  1. #define LISTSIZE 10
  2. typedef int DataType;
  3. struct Stack {
  4. DataType data[LISTSIZE];
  5. int top; //除了记录大小 还可以记录栈顶位置
  6. };

上述代码中,以下代码:


    
    
  1. #define LISTSIZE 100

是用一个宏常量来定义顺序表的容量,这样定义的好处是当需要修改顺序表的容量的时候,只需要修改该宏常量即可。

上述代码中,以下代码:


    
    
  1. typedef int DataType;

是将数据类型int起了一个别名叫做DataType,并在后面的程序中只使用DataType,而不使用int。这样做的好处是当堆栈中的数据类型发生变化时,只需要修改此句中的int为要改变的数据类型,即可将程序中所有数据变量的数据类型变成指定的类型。

步骤二:初始化操作

在主程序中,定义栈结构体的变量。

在初始化函数中将该变量中的栈顶指针初始化为0,表示为空栈。

代码如下:


    
    
  1. void init(struct Stack *stack)
  2. {
  3. stack->top = 0;
  4. }
  5. int main(int argc, const char * argv[])
  6. {
  7. struct Stack stack;
  8. init(&stack);
  9. }

步骤三:判断栈空

判断栈空实际上是判断栈顶指针是否为0,因为当栈顶指针为0时,代表栈中没有数据元素。

代码如下:


    
    
  1. bool empty(struct Stack* stack) {
  2. return stack->top == 0;
  3. }

步骤四:入栈

入栈是在栈中加入一个数据元素,在入栈时,首先需要判断栈是否为满,如果栈满了,则就不能在向其中添加元素了,判断栈是否满的操作只有在顺序存储结构才会出现,因为采用顺序存储结构的栈是要事先定义栈的容量的。然后将数据元素放入栈中,并使栈顶指针加1,指向下一个位置。

代码如下:


    
    
  1. void push(struct Stack* stack, DataType d) {
  2. if (stack->top == LISTSIZE)
  3. return;
  4. stack->data[stack->top++] = d;
  5. }

上述代码中,以下代码:


    
    
  1. if (stack->top == LISTSIZE)

是判断栈是否满,判断栈顶指针是否与栈的容量相等,如果是,则表示栈已经满了。

步骤五:出栈

出栈操作实际上是将栈中的栈顶元素删除,在出栈时,首先判断栈是否为空,如果栈为空则代表栈中已经没有数据元素了,此时是不可能进行出栈操作的。然后,将栈顶指针减1。

代码如下:


    
    
  1. void pop(struct Stack* stack) {
  2. if (empty(stack))
  3. return;
  4. stack->top--;
  5. }

步骤六:取栈顶元素

取栈顶元素操作实际上是仅返回栈顶元素,而栈顶指针并不变动。在取栈顶元素时,首先也要判断栈是否为空,因为空栈同样是不可能有数据元素的。

代码如下:


    
    
  1. DataType topData(struct Stack* stack) {
  2. return stack->data[stack->top - 1];
  3. }

1.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdbool.h>
  3. #define LISTSIZE 10
  4. typedef int DataType;
  5. struct Stack {
  6. DataType data[LISTSIZE];
  7. int top; //处了记录大小 还可以记录栈顶位置
  8. };
  9. void init(struct Stack* stack)
  10. {
  11. stack->top = 0;
  12. }
  13. bool empty(struct Stack* stack) {
  14. return stack->top == 0;
  15. }
  16. void push(struct Stack* stack, DataType d) {
  17. if (stack->top == LISTSIZE)
  18. return;
  19. stack->data[stack->top++] = d;
  20. }
  21. void pop(struct Stack* stack) {
  22. if (empty(stack))
  23. return;
  24. stack->top--;
  25. }
  26. DataType topData(struct Stack* stack) {
  27. return stack->data[stack->top - 1];
  28. }
  29. int main()
  30. {
  31. struct Stack stack;
  32. init(&stack);
  33. push(&stack, 30);
  34. push(&stack, 60);
  35. push(&stack, 80);
  36. while (!empty(&stack)) {
  37. printf("%d ", topData(&stack));
  38. pop(&stack);
  39. }
  40. printf("\n");
  41. return 0;
  42. }

2 基于链式表的堆栈

2.1 问题

链栈是采用链式存储结构的栈,即使用一组不要求连续的存储空间来模拟栈。每个栈元素为一个节点, 每个节点中包含两个域,一个是数据域,用于存储栈元素的数据;另一个是指针域,用于存储下一个栈元素的地址。

2.2 方案

链栈的基本操作包括:

1) 初始化操作,在初始化操作中将栈顶指针置空。

2) 判断栈空,判断栈顶指针是否为空。

3) 入栈,在栈中加入一个数据元素。

4) 出栈,在栈中删除一个数据元素。

5) 取栈顶元素,将栈顶元素取出,但并不在栈中删除该元素。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义栈元素节点

在C语言中:

1)定义一个变量来表示栈元素的数据。

2)定义一个指针来指向下一个栈元素的位置。

3)这两方面的信息共同描述一个栈元素节点,可将它们用结构体封装在一起。

代码如下:


    
    
  1. typedef int DataType;
  2. struct Stack {
  3. DataType data;
  4. struct Stack *next;
  5. };

上述代码中,以下代码:


    
    
  1. struct Stack *next;

是定义了一个指向下一个栈元素的指针,由于下一个栈元素的数据类型与当前栈元素的数据类型相同,所以指针的数据类型就是栈节点的指针数据类型。

步骤二:初始化操作

在主程序中,定义栈顶指针。

在初始化函数中将栈顶指针初始化为NULL,表示为空栈。

代码如下:


    
    
  1. void init(struct Stack** top)
  2. {
  3. *top = NULL;
  4. }
  5. int main()
  6. {
  7. struct Stack *top;
  8. init(&top);
  9. return 0;
  10. }

上述代码中,以下代码:


    
    
  1. void init(struct Stack** top)

定义了初始化函数的函数头,该函数有一个形参,是栈元素结构体指针的指针。之所以使用指针的指针,是因为栈顶指针在函数执行过程中会被置空,而这种变化需要带回主函数。

步骤三:判断栈空

判断栈空操作实际上就是判断栈顶指针是否为空。

代码如下所示:


    
    
  1. bool empty(struct Stack* top)
  2. {
  3. return top == NULL;
  4. }

步骤四:入栈

入栈操作实际上就是在栈中加入一个数据元素,对于链栈,入栈操作本质上就是在栈中加入一个结点。

代码如下所示:


    
    
  1. void push(struct Stack** top, DataType d)
  2. {
  3. struct Stack *newNode = (struct Stack*)malloc(sizeof(struct Stack));
  4. newNode->data = d;
  5. newNode->next = *top;
  6. *top = newNode;
  7. }

上述代码中,以下代码:


    
    
  1. void push(struct Stack** top, DataType d)

定义了入栈函数的函数头,该函数有两个形参,第一个是栈结构的指针的指针,在这里使用指针的指针,还是因为需要将栈顶指针的变化带回主函数。第二个形参是需要入栈的数据。

上述代码中,以下代码:


    
    
  1. struct Stack *newNode = (struct Stack*)malloc(sizeof(struct Stack));

是构造一个新的栈元素节点。

上述代码中,以下代码:


    
    
  1. newNode->data = d;

是将需要入栈的元素放入新的栈元素节点中。

上述代码中,以下代码:


    
    
  1. newNode->next = *top;

是将新创建的栈元素节点加入到栈中原栈顶元素的前面,成为新的栈顶元素。

上述代码中,以下代码:


    
    
  1. *top = newNode;

是栈顶指针指向新创建的栈顶元素。正是由于这行代码,使栈顶指针发生了变化,而这种变化需要带回到主程序,所以函数的第一个形参必须是指针的指针。

步骤五:出栈

出栈操作实际上就是从栈中删除栈顶位置的元素,对于链栈,出栈操作本质上就是在栈中删除一个结点。

代码如下所示:


    
    
  1. void pop(struct Stack** top)
  2. {
  3. if (empty(*top))
  4. return;
  5. struct Stack *tempNode = *top;
  6. *top = (*top)->next;
  7. free(tempNode);
  8. }

上述代码中,以下代码:


    
    
  1. if (empty(*top))
  2. return;

是判断链栈是否为空,如果栈为空是不能从栈中删除元素的。

上述代码中,以下代码:


    
    
  1. struct Stack *tempNode = *top;
  2. *top = (*top)->next;
  3. free(tempNode);

首先用一个临时指针保存原栈顶指针指向的栈元素地址,即出栈元素的地址,然后将栈顶指针指向下一个元素,这样栈中就减少了一个元素。最后释放临时指针指向的元素,即释放出栈元素所占的存储空间。

步骤六:取栈顶元素

取栈顶元素实际上就是将栈顶元素的值返回,对于链栈,本质上是将栈顶节点的数据返回。注意,在取栈顶元素时,首先要判断栈是否为空,空栈是没有数据元素的。

代码如下:


    
    
  1. void topData(struct Stack* top, DataType* data)
  2. {
  3. if (empty(top))
  4. return;
  5. *data = top->data;
  6. }

2.4 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdbool.h>
  4. typedef int DataType;
  5. struct Stack {
  6. DataType data;
  7. struct Stack *next;
  8. };
  9. void init(struct Stack** top)
  10. {
  11. *top = NULL;
  12. }
  13. bool empty(struct Stack* top)
  14. {
  15. return top == NULL;
  16. }
  17. void push(struct Stack** top, DataType d)
  18. {
  19. struct Stack *newNode = (struct Stack*)malloc(sizeof(struct Stack));
  20. newNode->data = d;
  21. newNode->next = *top;
  22. *top = newNode;
  23. }
  24. void pop(struct Stack** top)
  25. {
  26. if (empty(*top))
  27. return;
  28. struct Stack *tempNode = *top;
  29. *top = (*top)->next;
  30. free(tempNode);
  31. }
  32. void topData(struct Stack* top, DataType* data)
  33. {
  34. if (empty(top))
  35. return;
  36. *data = top->data;
  37. }
  38. int main()
  39. {
  40. struct Stack *top;
  41. init(&top);
  42. push(&top, 30);
  43. push(&top, 60);
  44. push(&top, 80);
  45. while (!empty(top)) {
  46. int data;
  47. topData(top, &data);
  48. printf("%d ", data);
  49. pop(&top);
  50. }
  51. printf("\n");
  52. return 0;
  53. }

1 基于顺序表的队列

1.1 问题

队列是一种特殊的线性表,是限定在线性表表尾进行插入操作,而在线性表表头进行删除的线性表。由队列的概念衍生出几个子概念,它们是队首:允许进行删除的一端,又称为队头,用队首指针(front)来指示要删除的元素;队尾:允许进行插入的一端,用队尾指针(rear)来指示要插入元素的位置;空队,队列中没有元素时称为空队列。

顺序队是采用顺序存储结构的队,即使用一组连续的存储单元(一般使用数组)来模拟队,依次存放队中的数据元素。

顺序队中存在一个“假溢出”的现象,“假溢出”是指随着入队、出队的进行,使整个队列在数组中整体向后移动,队尾指针已经移到了数组的最后一个元素,再有元素入队就会出现溢出,而事实上,此时队列中并未真的“满员”的现象,因为出队会使位置空出,所以数组的前部仍然有空间。

为充分利用数组空间,克服“假溢出”现象,将队列分配的向量空间看成为一个首尾相接的圆环,并称这种队列为循环队列。

1.2 方案

顺序队的基本操作包括:

1) 初始化操作,在初始化操作中将建立一个空队。

2) 判断队空,判断队中的数据元素个数是否为0。

3) 入队,在队中加入一个数据元素。

4) 出队,在队中删除一个数据元素。

5) 取队首元素,将队首元素取出,但并不在队中删除该元素。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义队

在C语言中:

1)定义一个一维数组data,来表示队的顺序存储空间。

2)定义一个变量front,来存储队首的位置。

3)定义一个变量rear,来存储队尾的位置。

4)这三方面的信息共同描述一个队,可将它们封装在一起。

代码如下:


    
    
  1. typedef int DataType;
  2. struct Queue2 {
  3. DataType data[6];
  4. int front; //队首指针
  5. int rear; //队尾指针
  6. };

上述代码中,以下代码:


    
    
  1. typedef int DataType;

是将数据类型int起了一个别名叫做DataType,并在后面的程序中只使用DataType,而不使用int。这样做的好处是当队列中的数据类型发生变化时,只需要修改此句中的int为要改变的数据类型,即可将程序中所有数据变量的数据类型变成指定的类型。

步骤二:初始化操作

在主程序中,定义队结构体的变量。

在初始化函数中将该变量中的队首、队尾指针初始化为0,表示为空队。

代码如下:


    
    
  1. void init(struct Queue2 *q)
  2. {
  3. q->front = q->rear = 0;
  4. }
  5. int main(int argc, const char * argv[])
  6. {
  7. Queue2 q2;
  8. init(&q2);
  9. }

步骤三:判断队空

判断队空实际上是判断队首指针与队尾指针是否相同,当队首指针与队尾指针相同时,代表队中没有数据元素。

代码如下:


    
    
  1. bool empty(struct Queue2 *q) {
  2. return q->rear - q->front == 0;
  3. }

步骤四:入队

入队是在队中加入一个数据元素,在入队时,首先需要判断队是否为满,如果队满了,则就不能在向其中添加元素了,判断队是否满的操作只有在顺序存储结构才会出现,因为采用顺序存储结构的队是要事先定义队的容量的。然后将数据元素放入栈中,并使队首指针加1,指向下一个位置。

代码如下:


    
    
  1. bool push(struct Queue2 *q, DataType d) {
  2. if (q->rear - q->front == 6) return false;
  3. q->data[q->rear++ % 6] = d;
  4. return true;
  5. }

上述代码中,以下代码:


    
    
  1. if (q->rear - q->front == 6) return false;

是判断队是否满,当队尾指针与队首指针的差值为6时,则表示队已经满了。6是步骤一定义队时定义的队容量。

上述代码中,以下代码:


    
    
  1. q->data[q->rear++ % 6] = d;

是将入队元素d放置在队尾指针指向的位置。其中q->rear++ % 6是形成循环队列的关键,队尾指针对6求余数,则保证了数组q->data[]的下标永远在0~5之间变化,因为任何正整数对6求余数,余数只能是0~5,这样就保证数组不会越界访问,即防止了“假溢出”现象的出现。

步骤五:出队

出队操作实际上是将队中的队首元素删除,在出队时,首先判断队是否为空,如果队为空则代表队中已经没有数据元素了,此时是不可能进行出队操作的。然后,将队首指针加1。

代码如下:


    
    
  1. bool pop(struct Queue2 *q) {
  2. if (empty(q)) return false;
  3. q->front++;
  4. return true;
  5. }

步骤六:取队首元素

取队首元素操作实际上是仅返回队首元素,而队首指针并不变动。在取队首元素时,首先也要判断队是否为空,因为空队同样是不可能有数据元素的。

代码如下:


    
    
  1. DataType getFront(struct Queue2 *q) {
  2. return q->data[q->front % 6];
  3. }

上述代码中q->front % 6同样是形成循环队列的关键,队首指针对6求余数,则保证了数组q->data[]的下标永远在0~5之间变化,因为任何正整数对6求余数,余数只能是0~5,这样就保证数组不会越界访问,即防止了“假溢出”现象的出现。

1.4 完整代码

本案例的完整代码如下所示:


    
    
  1. //循环队列
  2. #include <stdio.h>
  3. #include <stdbool.h>
  4. typedef int DataType;
  5. struct Queue2 {
  6. DataType data[6];
  7. int front; //开始位置
  8. int rear; //结束位置
  9. };
  10. void init(struct Queue2 *q)
  11. {
  12. q->front = q->rear = 0;
  13. }
  14. bool empty(struct Queue2 *q) {
  15. return q->rear - q->front == 0;
  16. }
  17. bool push(struct Queue2 *q, DataType d) {
  18. if (q->rear - q->front == 6) return false;
  19. q->data[q->rear++ % 6] = d;
  20. return true;
  21. }
  22. bool pop(struct Queue2 *q) {
  23. if (empty(q)) return false;
  24. q->front++;
  25. return true;
  26. }
  27. DataType getFront(struct Queue2 *q) {
  28. return q->data[q->front % 6];
  29. }
  30. int main() {
  31. struct Queue2 q2;
  32. init(&q2);
  33. for (int i = 0; i < 6; i++) {
  34. push(&q2, i);
  35. }
  36. pop(&q2);
  37. push(&q2, 300);
  38. pop(&q2);
  39. pop(&q2);
  40. push(&q2, 200);
  41. while (!empty(&q2)) {
  42. printf("%d ", getFront(&q2));
  43. pop(&q2);
  44. }
  45. printf("\n");
  46. return 0;
  47. }

2 基于链式表的队列

2.1 问题

链队是采用链式存储结构的队,即使用一组不要求连续的存储空间来模拟队。每个队元素为一个节点, 每个节点中包含两个域,一个是数据域,用于存储队元素的数据;另一个是指针域,用于存储下一个队元素的地址。

2.2 方案

链队的基本操作包括:

1) 初始化操作,在初始化操作中将队头、队尾指针置空。

2) 判断队空,判断队首指针(front)是否为空。

3) 入队,在队中加入一个数据元素。

4) 出队,在队中删除一个数据元素。

5) 取队首元素,将队首元素取出,但并不在队中删除该元素。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义队元素节点

在C语言中:

1)定义一个变量来表示队元素的数据。

2)定义一个指针来指向下一个队元素的位置。

3)这两方面的信息共同描述一个队元素节点,可将它们用结构体封装在一起。

代码如下:


    
    
  1. typedef int DataType;
  2. struct Queue {
  3. DataType data;
  4. struct Queue *next;
  5. };

上述代码中,以下代码:


    
    
  1. struct Queue *next;

是定义了一个指向下一个队元素的指针,由于下一个队元素的数据类型与当前队元素的数据类型相同,所以指针的数据类型就是队节点的指针数据类型。

步骤二:初始化操作

在主程序中,定义队头、队尾指针。

在初始化函数中将队头、队尾指针初始化为NULL,表示为空栈。

代码如下:


    
    
  1. void init(struct Queue** front, struct Queue** rear)
  2. {
  3. *front = NULL;
  4. *rear = NULL;
  5. }
  6. int main()
  7. {
  8. struct Queue *front, *rear;
  9. init(&front, &rear);
  10. return 0;
  11. }

上述代码中,以下代码:


    
    
  1. void init(struct Queue** front, struct Queue** rear)

定义了初始化函数的函数头,该函数有两个形参,都是队元素结构体指针的指针。之所以使用指针的指针,是因为队头、队尾指针在函数执行过程中会被置空,而这种变化需要带回主函数。

步骤三:判断队空

判断队空操作实际上就是判断队头或队尾是否为空。

代码如下所示:


    
    
  1. bool empty(struct Queue* node)
  2. {
  3. return node == NULL;
  4. }

步骤四:入队

入队操作实际上就是在队中加入一个数据元素,对于链队,入队操作本质上就是在队中加入一个结点。

代码如下所示:


    
    
  1. void push(struct Queue** front, struct Queue** rear, DataType d)
  2. {
  3. struct Queue *newNode = (struct Queue*)malloc(sizeof(struct Queue));
  4. newNode->data = d;
  5. newNode->next = NULL;
  6. if (*rear != NULL)
  7. (*rear)->next = newNode;
  8. *rear = newNode;
  9. if (*front == NULL)
  10. *front = *rear;
  11. }

上述代码中,以下代码:


    
    
  1. void push(struct Queue** front, struct Queue** rear, DataType d)

定义了入队函数的函数头,该函数有三个形参,前两个是队结构的指针的指针,在这里使用指针的指针,还是因为需要将队头、队尾指针的变化带回主函数。第三个形参是需要入栈的数据。

上述代码中,以下代码:


    
    
  1. struct Queue *newNode = (struct Queue*)malloc(sizeof(struct Queue));

是构造一个新的队元素节点。

上述代码中,以下代码:


    
    
  1. newNode->data = d;
  2. newNode->next = NULL;

是将需要入队的元素放入新的队元素节点中。

上述代码中,以下代码:


    
    
  1. if (*rear != NULL)
  2. (*rear)->next = newNode;
  3. *rear = newNode;

首先判断队尾指针是否为空,如果不为空,则将新创建的队元素节点加入到队中原队尾元素的后面,成为新的队尾元素,并将队尾指针指向新创建的队元素节点。如果队尾指针为空,则表示此时队中没有元素,所以直接将队尾指针指向新创建的队元素节点即可。正是由于第三行代码,使队尾指针发生了变化,而这种变化需要带回到主程序,所以函数的第二个形参必须是指针的指针。

上述代码中,以下代码:


    
    
  1. if (*front == NULL)
  2. *front = *rear;

是判断队头指针是否为空,如果为空,则表示队中没有元素,此时入队的元素为队中的唯一一个元素,所以此时队头、队尾指针均指向它。由于第二行代码,使队头指针发生了变化,而这种变化需要带回到主程序,所以函数的第一个形参必须是指针的指针。

步骤五:出队

出队操作实际上就是从队中删除队尾位置的元素,对于链队,出队操作本质上就是在队中删除一个结点。

代码如下所示:


    
    
  1. void pop(struct Queue** front, struct Queue** rear)
  2. {
  3. if (empty(*rear))
  4. return;
  5. struct Queue *tempNode = *front;
  6. *front = (*front)->next;
  7. free(tempNode);
  8. if (*front == NULL)
  9. *rear = NULL;
  10. }

上述代码中,以下代码:


    
    
  1. if (empty(*rear))
  2. return;

是判断链队是否为空,如果队为空是不能从队中删除元素的。

上述代码中,以下代码:


    
    
  1. struct Queue *tempNode = *front;
  2. *front = (*front)->next;
  3. free(tempNode);

首先用一个临时指针保存原队头指针指向的队元素地址,即出队元素的地址,然后将队头指针指向下一个元素,这样队中就减少了一个元素。最后释放临时指针指向的元素,即释放出队元素所占的存储空间。

上述代码中,以下代码:


    
    
  1. if (*front == NULL)
  2. *rear = NULL;

是判断删除一个队元素后,队是否为空,如果队中没有元素了,也应该将队尾指针置为空,否则队尾指针将是野指针。

步骤六:取队首元素

取队首元素实际上就是将队头元素的值返回,对于链队,本质上是将队头节点的数据返回。注意,在取队首元素时,首先要判断队是否为空,空队是没有数据元素的。

代码如下:


    
    
  1. void topData(struct Queue* front, DataType* data)
  2. {
  3. if (empty(front))
  4. return;
  5. *data = front->data;
  6. }

2.4 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdbool.h>
  4. typedef int DataType;
  5. struct Queue {
  6. DataType data;
  7. struct Queue *next;
  8. };
  9. void init(struct Queue** front, struct Queue** rear)
  10. {
  11. *front = NULL;
  12. *rear = NULL;
  13. }
  14. bool empty(struct Queue* node)
  15. {
  16. return node == NULL;
  17. }
  18. void push(struct Queue** front, struct Queue** rear, DataType d)
  19. {
  20. struct Queue *newNode = (struct Queue*)malloc(sizeof(struct Queue));
  21. newNode->data = d;
  22. newNode->next = NULL;
  23. if (*rear != NULL)
  24. (*rear)->next = newNode;
  25. *rear = newNode;
  26. if (*front == NULL)
  27. *front = *rear;
  28. }
  29. void pop(struct Queue** front, struct Queue** rear)
  30. {
  31. if (empty(*rear))
  32. return;
  33. struct Queue *tempNode = *front;
  34. *front = (*front)->next;
  35. free(tempNode);
  36. if (*front == NULL)
  37. *rear = NULL;
  38. }
  39. void topData(struct Queue* front, DataType* data)
  40. {
  41. if (empty(front))
  42. return;
  43. *data = front->data;
  44. }
  45. int main()
  46. {
  47. struct Queue *front, *rear;
  48. init(&front, &rear);
  49. push(&front, &rear, 30);
  50. push(&front, &rear, 60);
  51. push(&front, &rear, 80);
  52. while (!empty(front)) {
  53. int data;
  54. topData(front, &data);
  55. printf("%d ", data);
  56. pop(&front, &rear);
  57. }
  58. printf("\n");
  59. return 0;
  60. }



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值