栈的实现

  • 栈的基本功能:
  1. public interface Stack<E> {
  2. int getSize();
  3. boolean isEmpty();
  4. void push(E e);
  5. E pop();
  6. E peek();
  7. }
  • 基于动态数组的栈的实现
  1. public class ArrayStack<E> implements Stack<E>{
  2. Array<E> array;
  3. public ArrayStack(int capacity){
  4. array=new Array<>(capacity);
  5. }
  6. public ArrayStack(){
  7. array=new Array<>();
  8. }
  9. @Override
  10. public int getSize() {
  11. return array.getSize();
  12. }
  13. @Override
  14. public boolean isEmpty() {
  15. return array.isEmpty();
  16. }
  17. @Override
  18. public void push(E e) {
  19. array.addLast(e);
  20. }
  21. @Override
  22. public E pop() {
  23. return array.removeLast();
  24. }
  25. @Override
  26. public E peek() {
  27. E ele=array.get(array.getSize()-1);
  28. return ele;
  29. }
  30. public int getCapacity(){
  31. return array.getCapacity();
  32. }
  33. @Override
  34. public String toString() {
  35. StringBuilder ret=new StringBuilder();
  36. ret.append("Stack: ");
  37. ret.append("[");
  38. for(int i=0;i<array.getSize();i++){
  39. ret.append(array.get(i));
  40. if(i!=array.getSize()-1){
  41. ret.append(", ");
  42. }
  43. }
  44. ret.append("] top");
  45. return ret.toString();
  46. }
  47. }

时间复杂度分析

操作 时间复杂度
push(E) 均摊时间复杂度:O(1)
pop() 均摊时间复杂度:O(1)
peek() O(1)
getSize() O(1)
isEmpty() O(1)

队列

  • 队列是一种线性结构
  • 相比数组,队列对应的操作是数组的子集
  • 只能从一端(队尾)添加元素,只能从另一端(队首)取出元素

队列的基本功能

  1. public interface Queue<E> {
  2. void enqueue(E e);
  3. E dequeue();
  4. E getFront();
  5. int getSize();
  6. boolean isEmpty();
  7. }

数组队列

实现

  1. /**
  2. * 基于动态数组的队列的实现
  3. */
  4. public class ArrayQueue<E> implements Queue<E>{
  5. Array<E> array;
  6. public ArrayQueue(int capacity){
  7. array=new Array<>(capacity);
  8. }
  9. public ArrayQueue(){
  10. array=new Array<>();
  11. }
  12. @Override
  13. public int getSize() {
  14. return array.getSize();
  15. }
  16. @Override
  17. public boolean isEmpty() {
  18. return array.isEmpty();
  19. }
  20. @Override
  21. public void enqueue(E e) {
  22. array.addLast(e);
  23. }
  24. @Override
  25. public E dequeue() {
  26. return array.removeFirst();
  27. }
  28. @Override
  29. public E getFront() {
  30. return array.get(0);
  31. }
  32. @Override
  33. public String toString() {
  34. StringBuilder ret=new StringBuilder();
  35. ret.append("Queue: ");
  36. ret.append("front [");
  37. for(int i=0;i<array.getSize();i++){
  38. ret.append(array.get(i));
  39. if(i!=array.getSize()-1){
  40. ret.append(", ");
  41. }
  42. }
  43. ret.append("] tail");
  44. return ret.toString();
  45. }
  46. }

时间复杂度分析

操作 时间复杂度
enqueue(e) 均摊时间复杂度:O(1)
dequeue() O(n)
front() O(1)
getSize() O(1)
isEmpty() O(1)

循环队列

  • 数组队列的问题
    出队操作,要移动数据,时间复杂度是O(n)
    image.png
  • 循环队列

image.png

队列为空判断条件:

  1. front == tail

队列为满判断条件:

  1. (tail + 1) % data.length == front

动态调整数组大小的resize函数:

  1. private void resize(int newCapacity) {
  2. //预留一个位置,用来判断队列是否已满
  3. E[] newData=(E[])new Object[newCapacity+1];
  4. for(int i=0;i<size;i++){
  5. //将原来循环队列中数据复制到新数组,原来的data数据是从 font开始的
  6. //复制到数组,新数组是从0小标开始的
  7. newData[i]=data[(i+front)%data.length];
  8. }
  9. data=newData;
  10. front=0;
  11. tail=size;
  12. }

实现

  1. public class LoopQueue<E> implements Queue<E>{
  2. private E[] data;
  3. private int front,tail;
  4. private int size;
  5. public LoopQueue(int capacity){
  6. //循环队列会浪费一个单位空间
  7. data=(E[])new Object[capacity+1];
  8. front=0;
  9. tail=0;
  10. size=0;
  11. }
  12. public LoopQueue(){
  13. this(10);
  14. }
  15. public int getCapacity(){
  16. return data.length-1;
  17. }
  18. @Override
  19. public int getSize() {
  20. return size;
  21. }
  22. @Override
  23. public boolean isEmpty() {
  24. return front==tail;
  25. }
  26. @Override
  27. public void enqueue(E e) {
  28. //入队操作,先判断队列是否满了
  29. if((tail+1)%data.length==front){
  30. resize(getCapacity()*2);
  31. }
  32. data[tail]=e;
  33. tail=(tail+1)%data.length;
  34. size++;
  35. }
  36. @Override
  37. public E dequeue() {
  38. if(isEmpty()){
  39. throw new IllegalArgumentException("con not dequeue from empty queue");
  40. }
  41. E ret=data[front];
  42. data[front]=null;
  43. front=(front+1)%data.length;
  44. size--;
  45. if(size==getCapacity()/4 && getCapacity()/2!=0){
  46. resize(getCapacity()/2);
  47. }
  48. return ret;
  49. }
  50. @Override
  51. public E getFront() {
  52. if(isEmpty()){
  53. throw new IllegalArgumentException("con not dequeue from empty queue");
  54. }
  55. return data[front];
  56. }
  57. private void resize(int newCapacity) {
  58. E[] newData=(E[])new Object[newCapacity+1];
  59. for(int i=0;i<size;i++){
  60. newData[i]=data[(i+front)%data.length];
  61. }
  62. data=newData;
  63. front=0;
  64. tail=size;
  65. }
  66. @Override
  67. public String toString() {
  68. StringBuilder ret=new StringBuilder();
  69. ret.append(String.format("LooPQueue: size=%d,capacity=%d\n",size,getCapacity()));
  70. ret.append("font [");
  71. for(int i=front;i!=tail;i=(i+1)%data.length){
  72. ret.append(data[i]);
  73. if((i+1)%data.length!=tail){
  74. ret.append(", ");
  75. }
  76. }
  77. ret.append("] tail");
  78. return ret.toString();
  79. }
  80. }

时间复杂度分析

操作 时间复杂度
enqueue(e) 均摊时间复杂度:O(1)
dequeue() 均摊时间复杂度:O(1)
front() O(1)
getSize() O(1)
isEmpty() O(1)

比较

  1. public class Main {
  2. public static void main(String[] args) {
  3. int opCount=100000;
  4. ArrayQueue<Integer> arrayQueue=new ArrayQueue<>();
  5. double t1=testQueue(arrayQueue,opCount);
  6. System.out.println("Array Queue time:"+t1+"s");
  7. LoopQueue<Integer> loopQueue=new LoopQueue<>();
  8. double t2=testQueue(loopQueue,opCount);
  9. System.out.println("Loop Queue time:"+t2+"s");
  10. }
  11. //测试使用q运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
  12. private static double testQueue(Queue<Integer> q,int opCount){
  13. long startTime=System.nanoTime();
  14. Random random=new Random();
  15. for(int i=0;i<opCount;i++){
  16. q.enqueue(random.nextInt(Integer.MAX_VALUE));
  17. }
  18. for(int i=0;i<opCount;i++){
  19. q.dequeue();
  20. }
  21. long endTime=System.nanoTime();
  22. return (endTime-startTime)/1000000000.0;
  23. }
  24. }