当前位置:   article > 正文

栈和队列定义、意义、区别,实现_c语言中队列和栈的区别

c语言中队列和栈的区别

栈和队列

一、栈和队列的定义、区别,存在的意义

1.栈的定义

(1)栈:栈实际上是一种线性表,它只允许在固定的一段进行插入或者删除元素,在进行数据插入或者删除的一段称之为栈顶,剩下的一端称之为栈顶。其遵循的原则是后进先出。
(2)栈的核心操作:三大核心操作,入栈,出栈,取栈顶元素
(3)对于栈的形象理解:子弹的弹夹我们一定见过,子弹在被压入的时候就相当于是一个个元素,而弹夹就相当于是栈。先被压入的子弹是最后被打出的,先压入的元素是最后出来的,也就是后进先出。

2.队列的定义

(1)队列:首先队列也是一种特殊的线性表,它允许在一端进行插入数据,在另一端进行删除数据的。队列里边有队首,队尾,队首元素。其遵循的原则是先进先出。
(2)队列的核心操作:三大核心操作分别是入队列,出队列,取队首元素。
(3)对于队列的形象理解:火车穿越隧道,火车的头相当于是队列的首,火车的尾相当于是队列的尾部。火车在穿越隧道的时候,头部先进入隧道头部也先出隧道,尾部后进入尾部后出隧道。队列也就是先入的元素先出队列,后进入的元素后出队列。

3.栈和队列的区别

(1)栈和队列的出入方式不同:栈是后进先出、队列是先进先出。
(2)栈和队列在具体实现的时候操作的位置不同:因为栈是后进先出,它在一段进行操作;而队列是先进先出,实现的时候在两端进行。在Java标准库中实现队列时是按照链表实现的。

4.栈和队列存在的意义

上边我们提到过:栈和队列都是一种典型的线性表,都是基于线性表(顺序表和链表)来实现的,那么我们研究栈和队列的目的何在?因为在栈和队列定义后,只有那三种操作,而那三种操作都是最常用的,它支持的操作越少,我们在使用的时候关心的点也就越少,用起来就越不容易出错。在计算机中“少即是多”,少意味着功能比较少、比较呆板。多意味着功能很多,用的时候要操的心就越多,就越容易出错。综上:栈和队列存在的意义就是减少线性表的基本操作,提取常用操作,让人们使用起来更方便,更不容易出错。

二、栈的实现

1.基于顺序表来实现栈

栈的操作是在一端进行的,所以我们选择在顺序表的尾部进行操作:入栈用尾插来实现、出栈用尾删来实现、取栈顶元素就是取尾部元素。
注意:我们在用顺序表来实现栈的时候选取的是在顺序表的尾部来进行的,但这并不是说在顺序表的头部就不能实现。只是在头部实现的时候,不管是头插还是头删都要进行元素的搬运,时间复杂度太高,所以不选取。

下边是基于顺序表来实现的栈:
 

  1. public class MyStackForArrayList {
  2. // 用顺序表来实现栈
  3. // 栈的特点:后进先出,所以在顺序表中入栈用尾插,出栈用尾删,取栈顶元素用[]操作
  4. // 创建数组用来表示顺序表,栈的容量是100,要是不够后边可以进行扩容
  5. private int[] data = new int[100];
  6. // 记录顺序表的当前值
  7. private int size = 0;
  8. // 一,栈的实现:顺序表实现
  9. // 1.入栈,就是顺序表中的尾插,头插也能实现,但是要进行搬运,效率太低
  10. public void push(int val) {
  11. // 特殊情况的考虑,这里也可以进行扩容
  12. if (size >= data.length) {
  13. return;
  14. }
  15. // 一般情况的处理
  16. data[size] = val;
  17. size++;// 入栈要让size++
  18. }
  19. // 2.出栈,是有返回值的,用Integer来接收,它可以返回null
  20. public Integer pop() {
  21. // 特殊情况
  22. if (size == 0) {
  23. return null;
  24. }
  25. // 一般情况
  26. int ret = data[size-1];// 保存那个出栈的元素,后边进行返回
  27. size--;// 出栈要让size--
  28. return ret;
  29. }
  30. // 3.取栈顶元素
  31. public Integer peek() {
  32. // 特殊情况
  33. if (size == 0) {
  34. return null;
  35. }
  36. return data[size - 1];
  37. }
  38. }

2.基于链表来实现栈

用链表来实现栈:在用链表实现栈的时候,我们操作的一段选取头端。用头插表示入栈,用头删来表示出栈,取栈顶元素就是取链表的head的值。
注意:选取在链表的头部实现并不是说在链表的尾部不能进行,而是在尾部进行操作的时候,因为每一次那个tail都会进行更新,所以要用一个引用来记录tail的位置,这样就占据了一块内存空间,因此不选它。

下边是基于链表实现的栈:
 

  1. class Node {
  2. int val;
  3. Node next;
  4. public Node(int val) {
  5. this.val = val;
  6. }
  7. }
  8. // 链表实现栈,头插表示入栈,头删表示出栈,取栈顶元素。链表头插,头删还是为了减少内存的开销
  9. public class MyStackForLinkedList {
  10. // 先搞一个链表
  11. private Node head = null;
  12. // 1.入栈,头插,不需要返回要插入的值
  13. public void push(int val) {
  14. // 将要插入的元素放在链表里边
  15. Node newNode = new Node(val);
  16. // 特殊情况的处理,空链表
  17. if (head == null) {// 链表本来是空的
  18. head = newNode;
  19. return;
  20. }
  21. // 处理一般情况
  22. newNode.next = head;
  23. head = head.next;
  24. }
  25. // 2.出栈,就是头删,出栈要返回一个值
  26. public Integer pop() {// 用Integer来为了可以返回那个null
  27. // 特殊情况处理,链表是空
  28. if (head == null) {
  29. return null;
  30. }
  31. // 链表之中只有一个元素,删除就没有元素了
  32. if (head.next == null) {
  33. int ret = head.val;
  34. head = null;
  35. return ret;
  36. }
  37. // 一般情况的处理
  38. int ret = head.val;
  39. head = head.next;
  40. return ret;
  41. }
  42. // 3.取栈顶元素
  43. public Integer peek() {
  44. // 特殊情况:要是链表是空的,没得取,返回null
  45. if (head == null) {
  46. return null;
  47. }
  48. return head.val;
  49. }
  50. }

三、队列的实现

1.基于链表来实现队列

队列的操作在两端进行,所以我们选择在链表的尾部进行插入表示入队列,头删来表示出队列,用 head.val 操作来表示取队首元素。
注意:这里的头删和尾插的顺序是可以进行交换的,头和尾只是选的两个引用罢了。咋样选取都是一样的。

下边是基于链表来实现的队列:

  1. // 基于链表来实现队列
  2. // 因为队列是先进先出的,所以用尾插表示入队列。头删表示出队列,.操作表示取队列元素
  3. public class MyQueueForLinkedList {
  4. // 弄一个Node内部类,要带上static,为了和外部的实例无关
  5. static class Node {
  6. int val;
  7. Node next;
  8. public Node(int val) {
  9. this.val = val;
  10. }
  11. }
  12. // 先创建一个链表,并记录其头节点和尾节点,方便后边的进行尾插
  13. Node newHead = null;
  14. Node newTail = null;
  15. // 1.入队列,就是尾插,为了和Java库中的队列保持一致,用boolean返回值
  16. public boolean offer(int val) {
  17. // 将要插入的值放在Node 节点里面
  18. Node newNode = new Node(val);
  19. // 情况特殊的处理
  20. if (newHead == null) {
  21. // 头节点和尾节点都是要插入的值
  22. newHead = newNode;
  23. newTail = newNode;
  24. return true;
  25. }
  26. // 一般情况的处理
  27. newTail.next = newNode;
  28. newTail = newTail.next;
  29. return true;
  30. }
  31. // 2.出队列,就是头删,注意头删也是要返回那个要删除的值
  32. public Integer poll() {
  33. // 特殊情况的处理,链表为空没得删
  34. if (newHead == null) {
  35. return null;
  36. }
  37. // 链表只要一个元素
  38. if (newHead.next == null) {
  39. int ret = newHead.val;
  40. // 新头节点就没有了,就是null
  41. newHead = null;
  42. return ret;
  43. }
  44. // 处理一般情况的处理
  45. int ret = newHead.val;
  46. newHead = newHead.next;
  47. return ret;
  48. }
  49. // 3.取队列首元素
  50. public Integer peek() {
  51. // 特殊情况的处理
  52. // 链表为空,没得取
  53. if (newHead == null) {
  54. return null;
  55. }
  56. // 一般情况的处理
  57. return newHead.val;
  58. }
  59. }

(1)基本思想:创建两个引用head和tail表示队列的头部和尾部,开始的时候head和tail都表示null,此时head == tail表示的是队列是空的 。
(2)出现的问题:每入一次队列给tail进行++,要是tail已经满了的时候就让tail == head,此时head和tail也是相等的,所以就不能区别队列到底是空的还是满的。
(3)解决方法:
方法1:是空上让出一个内存空间,不让tail和head重合。这种方法浪费了一块内存空间,直接让那个空间是空的,也就是用这种方法后:head == tail表示空, tail == head - 1表示队列是满的。看起来非常不好看。
方法2:创建一个变量size,时刻记录队列里边元素的多少,没进行一次入队操作进行size++,每进行一次出队操作让size–,最后用size的值来区别队列是空的还是满的。

下边是基于顺序表实现的队列:
 

  1. public class MyQueueForArrayList {
  2. // 用顺序表来实现队列
  3. // 基本思路:让head = 0,tail = 0;队列的长度是[head,tail),包含head不包含tail。
  4. // 入队:tail++,入完判断tail的值,要是 == data.length,让tail从0又开始
  5. // 出队列:让head++
  6. // 这样操作当head == tail时,有两种结果:1,是队列是空的时候 2,是队列是满的时候
  7. // 所以用size来记录一下顺序表的具体的大小,根据size来判断队列是否为满。
  8. // 创建数组
  9. public int[] data = new int[100];
  10. public int head = 0;
  11. private int tail = 0;
  12. // 这个用来判断队列到底是空的还是满的
  13. private int size = 0;
  14. // 1.入队操作,tail++,然后判断size的值
  15. public boolean offer(int val) {
  16. // 特殊情况的处理,先判断size的值的大小,每一次都是以size来判断队列是否是空的
  17. if (size == data.length) {// 队列已经满了
  18. return false;
  19. }
  20. // 一般情况的处理
  21. data[tail] = val;
  22. // 完成插入之后,判断一下tail的值的
  23. if (tail == data.length) {// 要是让tail从0开始
  24. tail = 0;
  25. }
  26. // 否则更新size的值
  27. size++;
  28. return true;
  29. }
  30. // 2.出队列,核心操作,头删,head--
  31. public Integer poll() {
  32. // 特殊情况的处理
  33. // 队列为空,没得取
  34. if (size == 0) {
  35. return null;
  36. }
  37. // 一般情况的处理
  38. int ret = data[head];
  39. head++;
  40. // 每一次要判断head的值是否到达了末尾
  41. // 要是到达了末尾,让head也是0
  42. if (head == data.length) {
  43. head = 0;
  44. }
  45. size--;
  46. return ret;
  47. }
  48. // 3.取队首元素
  49. public Integer peek() {
  50. // 特殊情况的处理
  51. if (size == 0) {// 为空,没得取
  52. return null;
  53. }
  54. return data[head];
  55. }
  56. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/557557
推荐阅读
相关标签
  

闽ICP备14008679号