数组

二次封装属于自己的数组

  1. public class Array<E> {
  2. private E[] data;
  3. private int size; //用于记录数组中元素的数量
  4. /**
  5. * 构造函数,传入数组的容量capacity构造Array
  6. * @param capacity 容量
  7. */
  8. public Array(int capacity) {
  9. data = (E[])new Object[capacity];
  10. size = 0;
  11. }
  12. /**
  13. * 默认给10个空间
  14. */
  15. public Array() {
  16. this(10);
  17. }

java泛型类

  • java语言不支持直接new一个泛型数组E[] data = new E[capacity];
  • 实际应该E[] data = (E[])new Object[capacity];
  • 泛型类的申明中Array<Integer> array = new Array<>();不需要在后面再添加Integer

数组的增删改查

  1. ---------------------------------------------------增-------------------------------------------------------
  2. /**
  3. * 往数组后面添加一个元素
  4. * @param e
  5. */
  6. public void addLast(E e){
  7. //if (data.length == size){
  8. // throw new IllegalArgumentException("AddLast failed . Array is full");
  9. //}
  10. //
  11. //data[size] = e;
  12. //size++;
  13. add(size,e);
  14. }
  15. /**
  16. * 往数组前面添加一个元素
  17. * @param e
  18. */
  19. public void addFirst(E e){
  20. add(0,e);
  21. }
  22. /**
  23. * 在index的位置插入e
  24. * @param index
  25. * @param e
  26. */
  27. public void add(int index,E e){
  28. if (data.length == size){
  29. //扩容两倍
  30. resize(2*data.length);
  31. }
  32. if (index < 0 || index>size){
  33. throw new IllegalArgumentException("Add failed . Require index>0 or index>size");
  34. }
  35. for (int i = size-1 ; i>=index ; i--){
  36. data[i+1] = data[i];
  37. }
  38. data[index] = e;
  39. size++;
  40. }
  41. ---------------------------------------------------增-------------------------------------------------------
  42. ---------------------------------------------------查-------------------------------------------------------
  43. /**
  44. * 获取index位置的元素
  45. * @param index
  46. * @return
  47. */
  48. E get(int index){
  49. if (index < 0 || index > size){
  50. throw new IllegalArgumentException("Get failed. Index is Illegal");
  51. }
  52. return data[index];
  53. }
  54. /**
  55. * 修改index的索引位置的元素为e
  56. * @param index
  57. * @param e
  58. */
  59. E set(int index,E e){
  60. if (index < 0 || index > size){
  61. throw new IllegalArgumentException("Set failed. Index is Illegal");
  62. }
  63. return data[index] = e;
  64. }
  65. /**
  66. * 查找数组是否有元素e
  67. * @param e
  68. * @return
  69. */
  70. public boolean contains(E e){
  71. for (int i = 0 ; i<size ; i++){
  72. if (data[i].equals(e)){
  73. return true;
  74. }
  75. }
  76. return false;
  77. }
  78. /**
  79. * 查找数组是否有元素e所在的索引,不存在则返回-1
  80. * @param e
  81. * @return
  82. */
  83. public int find(E e){
  84. for (int i = 0 ; i<size ; i++){
  85. if (data[i].equals(e)){
  86. return i;
  87. }
  88. }
  89. return -1;
  90. }
  91. ---------------------------------------------------查-------------------------------------------------------
  92. ---------------------------------------------------删-------------------------------------------------------
  93. /**
  94. * 删除指定位置的元素,并返回当前值
  95. * @param index
  96. * @return
  97. */
  98. public E remove(int index){
  99. if (index < 0 || index > size){
  100. throw new IllegalArgumentException("Remove failed. Index is Illegal");
  101. }
  102. E ret = data[index];
  103. for (int i = index+1 ; i<size ; i++){
  104. data[i-1] = data[i];
  105. }
  106. size--;
  107. //如果数元素个数是当前数组大小的四分之一(原因是复杂度震荡),则当前数组容量也减半
  108. if (size == data.length/4 && data.length/2 !=0){
  109. resize(data.length/2);
  110. }
  111. return ret;
  112. }
  113. /**
  114. * 删除第一个
  115. * @return
  116. */
  117. public E removeFirst(){
  118. return remove(0);
  119. }
  120. /**
  121. * 删除最后一个
  122. * @return
  123. */
  124. public E removeLast(){
  125. return remove(size-1);
  126. }
  127. /**
  128. * 检验数组中有没有e,如果存在就删除他
  129. *
  130. * 但只能删除一个,因为元素会重复
  131. *
  132. */
  133. public void removeElement(E e){
  134. int index = find(e);
  135. if (index != -1){
  136. remove(index);
  137. }
  138. }
  139. ---------------------------------------------------删-------------------------------------------------------

动态数组

  • 简单来说就是要满的时候扩充两倍,只装了一半的时候缩小两倍
    1. 看一下这个resize函数就行
    2. /**
    3. * 如果插入时数组已经满了,那就扩容
    4. * @param newCapacity
    5. */
    6. private void resize(int newCapacity){
    7. E[] newData = (E[])new Object[newCapacity];
    8. for (int i = 0; i < size; i++) {
    9. newData[i] = data[i];
    10. }
    11. data = newData;
    12. }

分析动态数组的复杂度

image.png

复杂度震荡

比如说当前数组的容量是10,当大小要达到10的时候扩容(capacity*2),此时数组大小是11,然后又减去1,数组大小变为10,此时又要resize,执行capacity/2,这样就执行了两次的resize

  • 由于resize的时间复杂度是O(n),而上述又执行了两次O(n),所以耗费了更多的时间

  • 解决方案:Lazy

当size == capacity/4 时,才将capacity减半

栈的基本实现

  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. return array.getLast();
  28. }
  29. @Override
  30. public String toString() {
  31. final StringBuffer sb = new StringBuffer("Queue: ");
  32. sb.append("front[");
  33. for (int i = 0; i < array.getSize(); i++) {
  34. sb.append(array.get(i));
  35. if (i != array.getSize()-1)
  36. sb.append(", ");
  37. }
  38. sb.append("] tail");
  39. return sb.toString();
  40. }
  41. }

栈的一个应用

  1. /**
  2. * Valid-Parenttheses
  3. * 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
  4. * 使用的是java.util包里面的stack
  5. *
  6. * @Author Li Cheng
  7. * @Date 2021/9/17 14:10
  8. * @Version 1.0
  9. */
  10. public class Solution {
  11. public boolean isValid(String s) {
  12. Stack<Character> stack = new Stack<>();
  13. for (int i = 0; i < s.length(); i++) {
  14. char c = s.charAt(i);
  15. if (c == '(' || c=='[' || c=='{')
  16. stack.push(c);
  17. else {
  18. if (stack.isEmpty()){
  19. return false;
  20. }
  21. char topChar = stack.pop();
  22. if (c==')' && topChar != '(')
  23. return false;
  24. if (c==']' && topChar != '[')
  25. return false;
  26. if (c=='}' && topChar != '{')
  27. return false;
  28. }
  29. }
  30. return stack.isEmpty();
  31. }
  32. public static void main(String[] args) {
  33. Solution solution = new Solution();
  34. System.out.println(solution.isValid("({})"));
  35. System.out.println(solution.isValid("({}"));
  36. }
  37. }

队列

队列的实现

2-5 数组队列【一手微信itit11223344】.mp4_20210917_145427507.jpg

数组队列

  • 相对简单,就是用数组实现一个队列,但有点浪费空间,出列之后front就不能往前移了

2-5 数组队列【一手微信itit11223344】.mp4_20210917_150937044.jpg

  1. public class ArrayQueue<E> implements Queue<E> {
  2. Array<E> array;
  3. public ArrayQueue(int capacity) {
  4. array = new Array<>(capacity);
  5. }
  6. public ArrayQueue() {
  7. array = new Array<>(10);
  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 enqueue(E e) {
  19. array.addLast(e);
  20. }
  21. @Override
  22. public E dequeue(E e) {
  23. return array.removeFirst();
  24. }
  25. @Override
  26. public E getFront() {
  27. return array.getFirst();
  28. }
  29. @Override
  30. public String toString() {
  31. final StringBuffer sb = new StringBuffer("Queue: ");
  32. sb.append("front[");
  33. for (int i = 0; i < array.getSize(); i++) {
  34. sb.append(array.get(i));
  35. if (i != array.getSize()-1)
  36. sb.append(", ");
  37. }
  38. sb.append("] tail");
  39. return sb.toString();
  40. }
  41. }

循环队列

  • 有点难理解,可以把这个队列想象成一个圆,在不停的循环直到满列
  • 队列为空:front == tail
  • 队列已经满了: (tail + 1)%c == front,c表示的是队列的长度
  • 出队,front在数组队列中是直接+1,但这里是(front+1)%c
  • 入队,tail在数组队列中是直接+1,但这里是(tail+1)%c
  • 扩容 : 要将原队列中的数据从front~tail依次复制给新队列

    2-6 循环队列【一手微信itit11223344】.mp4_20210917_151722888.jpg ```java public class LoopQueue implements Queue {

    private E[] data; private int front,tail; private int size;

    public LoopQueue(int capacity) {

    1. //因为是泛型,所以要强制类型转换
    2. data = (E[])new Object[capacity+1];
    3. front = 0;
    4. tail = 0;
    5. size = 0;

    }

    public LoopQueue() {

    1. this(10);

    }

    public int getCapacity(){

    1. return data.length-1;

    }

    @Override public int getSize() {

    1. return size;

    }

    @Override public boolean isEmpty() {

    1. return front == tail;

    }

    //入队 @Override public void enqueue(E e) {

    1. //队列满了,就扩容
    2. if((tail+1)%data.length == front )
    3. resize(getCapacity()*2);
    4. data[tail] = e;
    5. tail = (tail+1) % data.length;
    6. size++;

    }

    private void resize(int capacity){

    1. E[] newData = (E[])new Object[capacity+1];
    2. for (int i = 0; i < size; i++) {
    3. newData[i] = data[(i+front) % data.length];
    4. }
    5. data = newData;
    6. front = 0;
    7. tail = size;

    }

  1. //出队
  2. @Override
  3. public E dequeue(E e) {
  4. if (isEmpty())
  5. throw new IllegalArgumentException("Cannot dequeue from an empty queue");
  6. E ret = data[front];
  7. data[front] = null;
  8. front = (front+1)%data.length;
  9. size--;
  10. //如果只剩下四分之一就缩小容量
  11. if (size == getCapacity()/4 && getCapacity()/2 !=0){
  12. resize(getCapacity()/2);
  13. }
  14. return ret;
  15. }
  16. @Override
  17. public E getFront() {
  18. return data[front];
  19. }
  20. @Override
  21. public String toString() {
  22. StringBuilder res = new StringBuilder();
  23. res.append(String.format("Queue: size = %d , capacity = %d\n",size,getCapacity()));
  24. res.append("front [");
  25. for (int i=front ; i != tail ; i = (i+1)%data.length){
  26. res.append(data[i]);
  27. if ((i+1)%data.length != tail){
  28. res.append(",");
  29. }
  30. }
  31. res.append("] tail");
  32. return res.toString();
  33. }

}

  1. <a name="tFs8i"></a>
  2. # 练习题
  3. <a name="G8mpb"></a>
  4. ## 不浪费一个空间的循环队列
  5. ```java
  6. /**
  7. * 不浪费一个空间的循环队列
  8. *
  9. * @Author Li Cheng
  10. * @Date 2021/9/18 13:26
  11. * @Version 1.0
  12. */
  13. public class LoopQueueNoWaste<E> implements Queue<E>{
  14. private E[] data;
  15. private int front,tail;
  16. private int size;
  17. public LoopQueueNoWaste(int capacity){
  18. data = (E[]) new Object[capacity];
  19. front = 0;
  20. tail=0;
  21. size=0;
  22. }
  23. public LoopQueueNoWaste() {
  24. this(10);
  25. }
  26. public int getCapacity(){
  27. return data.length;
  28. }
  29. @Override
  30. public int getSize() {
  31. return size;
  32. }
  33. @Override
  34. public boolean isEmpty() {
  35. return front == tail;
  36. }
  37. /**
  38. * 入队
  39. * @param o
  40. */
  41. @Override
  42. public void enqueue(E o) {
  43. //如果满了就扩容
  44. if (size == getCapacity()){
  45. resize(data.length*2);
  46. }
  47. data[tail] = o;
  48. tail = (tail+1)% data.length;
  49. size++;
  50. }
  51. //扩容
  52. public void resize(int capacity){
  53. E[] newData = (E[]) new Object[capacity];
  54. for (int i = 0; i < size; i++) {
  55. newData[i] = data[(i+front)% data.length];
  56. }
  57. data = newData;
  58. front = 0;
  59. tail = size;
  60. }
  61. /**
  62. * 出队
  63. * @param
  64. * @return
  65. */
  66. @Override
  67. public E dequeue() {
  68. if (isEmpty())
  69. throw new IllegalArgumentException("Cannot dequeue from an empty queue");
  70. E ret = data[front];
  71. data[front] = null;
  72. front = (front+1)% data.length;
  73. size--;
  74. //如果只剩下四分之一就缩小容量
  75. if (size == getCapacity()/4 && size/2!=0){
  76. resize(getCapacity()/2);
  77. }
  78. return ret;
  79. }
  80. @Override
  81. public E getFront() {
  82. return data[front];
  83. }
  84. @Override
  85. public String toString() {
  86. StringBuilder res = new StringBuilder();
  87. res.append(String.format("Queue: size = %d , capacity = %d\n",size,getCapacity()));
  88. res.append("front [");
  89. for (int i=front ; i != tail ; i = (i+1)%data.length){
  90. res.append(data[i]);
  91. if ((i+1)%data.length != tail){
  92. res.append(",");
  93. }
  94. }
  95. res.append("] tail");
  96. return res.toString();
  97. }
  98. }

没有size成员变量的循环队列

  1. /**
  2. * 浪费一个空间但是没有size
  3. *
  4. * @Author Li Cheng
  5. * @Date 2021/9/18 13:55
  6. * @Version 1.0
  7. */
  8. public class LoopQueueNoSize<E> implements Queue<E>{
  9. private E[] data;
  10. private int front,tail;
  11. public LoopQueueNoSize(int capacity){
  12. data = (E[]) new Object[capacity+1];
  13. front = 0;
  14. tail=0;
  15. }
  16. public LoopQueueNoSize() {
  17. this(10);
  18. }
  19. public int getCapacity(){
  20. return data.length-1;
  21. }
  22. @Override
  23. public int getSize() {
  24. int size = (tail + (data.length-front))%data.length;
  25. return size;
  26. }
  27. @Override
  28. public boolean isEmpty() {
  29. return tail == front;
  30. }
  31. @Override
  32. public void enqueue(E e) {
  33. //队列满了,就扩容
  34. if ((tail+1)% data.length == front)
  35. resize(getCapacity()*2);
  36. data[tail] = e;
  37. tail = (tail+1) % data.length;
  38. }
  39. public void resize(int capacity){
  40. E[] newData = (E[]) new Object[capacity+1];
  41. for (int i = 0; i < getSize(); i++) {
  42. newData[i] = data[(i+front) % data.length];
  43. }
  44. data = newData;
  45. tail = getSize();
  46. front = 0;
  47. }
  48. @Override
  49. public E dequeue() {
  50. //队列为空就报错
  51. if (isEmpty())
  52. throw new IllegalArgumentException("Cannot dequeue an empty queue");
  53. E ret = data[front];
  54. data[front] = null;
  55. front = (front+1)% data.length;
  56. if (getSize() == getCapacity()/4 && getCapacity()/2 != 0){
  57. resize(getCapacity()/2);
  58. }
  59. return ret;
  60. }
  61. @Override
  62. public E getFront() {
  63. return data[front];
  64. }
  65. @Override
  66. public String toString() {
  67. StringBuilder res = new StringBuilder();
  68. res.append(String.format("Queue: size = %d , capacity = %d\n",getSize(),getCapacity()));
  69. res.append("front [");
  70. for (int i=front ; i != tail ; i = (i+1)%data.length){
  71. res.append(data[i]);
  72. if ((i+1)%data.length != tail){
  73. res.append(",");
  74. }
  75. }
  76. res.append("] tail");
  77. return res.toString();
  78. }
  79. }

实现双端队列

  • 双端队列图示

微信图片_20210921141918.jpg

  1. /**
  2. * @Author Li Cheng
  3. * @Date 2021/9/18 18:14
  4. * @Version 1.0
  5. */
  6. public class TwoHeadLoopQueue<E>{
  7. private E[] data;
  8. private int front,tail;
  9. private int size;
  10. public TwoHeadLoopQueue(int capacity) {
  11. data = (E[]) new Object[capacity];
  12. front = 0;
  13. tail = 0;
  14. size = 0;
  15. }
  16. public TwoHeadLoopQueue() {
  17. this(10);
  18. }
  19. public boolean isEmpty() {
  20. return size == 0;
  21. }
  22. public boolean isFull(){
  23. return size == getCapacity();
  24. }
  25. public int getCapacity(){
  26. return data.length;
  27. }
  28. public void addLast(E e){
  29. if (isFull()){
  30. resize(getCapacity()*2);
  31. }
  32. data[tail] = e;
  33. tail = (tail+1)% data.length;
  34. size++;
  35. }
  36. public void addFront(E e){
  37. if (isFull()){
  38. resize(getCapacity()*2);
  39. }
  40. front = front==0 ? data.length - 1 : front-1;
  41. data[front] = e;
  42. size++;
  43. }
  44. public E removeLast(){
  45. if (isEmpty()){
  46. throw new IllegalArgumentException("Cannot dequeue an empty queue");
  47. }
  48. E ret = data[tail];
  49. tail = tail == 0 ? data.length -1 : tail-1 ;
  50. data[tail] = null;
  51. size--;
  52. if (size == getCapacity()/4 && getCapacity()/2 != 0){
  53. resize(getCapacity()/2);
  54. }
  55. return ret;
  56. }
  57. public E removeFront(){
  58. if (isEmpty()){
  59. throw new IllegalArgumentException("Cannot dequeue an empty queue");
  60. }
  61. E ret = data[front];
  62. data[front] = null;
  63. front = (front+1)% data.length;
  64. size--;
  65. if (size == getCapacity()/4 && getCapacity()/2 != 0){
  66. resize(getCapacity()/2);
  67. }
  68. return ret;
  69. }
  70. public E getFront(){
  71. if ((isEmpty())){
  72. throw new IllegalArgumentException("Queue is Empty");
  73. }
  74. return data[front];
  75. }
  76. public E getLast(){
  77. if ((isEmpty())){
  78. throw new IllegalArgumentException("Queue is Empty");
  79. }
  80. //因为tail指向的是队尾元素的下一个位置,所以需要计算真正的位置
  81. return data[ tail==0? data.length -1 : tail-1];
  82. }
  83. private void resize(int capacity){
  84. E[] newData = (E[]) new Object[capacity];
  85. for (int i = 0; i < size; i++) {
  86. newData[i] = data[(i+front)% data.length];
  87. }
  88. data = newData;
  89. tail = size;
  90. front = 0;
  91. }
  92. @Override
  93. public String toString() {
  94. StringBuilder res = new StringBuilder();
  95. res.append(String.format("Queue: size = %d , capacity = %d\n",size,getCapacity()));
  96. res.append("front [");
  97. for (int i=front ; i != tail ; i = (i+1)%data.length){
  98. res.append(data[i]);
  99. if ((i+1)%data.length != tail){
  100. res.append(",");
  101. }
  102. }
  103. res.append("] tail");
  104. return res.toString();
  105. }
  106. }

用栈实现队列

  1. import java.util.LinkedList;
  2. import java.util.Queue;
  3. /**
  4. *
  5. * 用队列实现栈
  6. * @Author Li Cheng
  7. * @Date 2021/9/20 18:26
  8. * @Version 1.0
  9. */
  10. public class StackForQueue {
  11. private Queue<Integer> q;
  12. private int top; // 用来追踪记录栈顶元素将原先的复杂度O(n)变为O(1)
  13. public StackForQueue() {
  14. q = new LinkedList<>();
  15. }
  16. public boolean isEmpty(){
  17. return q.isEmpty();
  18. }
  19. public void push(int x){
  20. q.add(x);
  21. top = x;
  22. }
  23. public int pop(){
  24. //用q2来存储q队列n个元素之前的n-1个元素
  25. Queue<Integer> q2 = new LinkedList<>();
  26. for (int i = 0; i < q.size()-1; i++) {
  27. //每次从q中取出一个元素,都给top赋值
  28. //top中存储的就是q中除了队尾元素以外的最后一个元素
  29. //即最新的栈顶元素
  30. top = q.peek();
  31. q2.add(q.remove());
  32. }
  33. int ret = q.remove();
  34. q = q2;
  35. return ret;
  36. }
  37. public int top(){
  38. return top;
  39. }
  40. }

用队列实现栈

  1. import java.util.Stack;
  2. /**
  3. * @Author Li Cheng
  4. * @Date 2021/9/20 18:53
  5. * @Version 1.0
  6. */
  7. public class MyQueue {
  8. private Stack<Integer> stack;
  9. /** Initialize your data structure here. */
  10. public MyQueue() {
  11. stack = new Stack<>();
  12. }
  13. /** Push element x to the back of queue. */
  14. public void push(int x) {
  15. Stack<Integer> stack2 = new Stack<>();
  16. while (stack.size() > 0){
  17. stack2.push(stack.pop());
  18. }
  19. stack.push(x);
  20. while (stack2.size() > 0){
  21. stack.push(stack2.pop());
  22. }
  23. }
  24. /** Removes the element from in front of queue and returns that element. */
  25. public int pop() {
  26. return stack.pop();
  27. }
  28. /** Get the front element. */
  29. public int peek() {
  30. return stack.peek();
  31. }
  32. /** Returns whether the queue is empty. */
  33. public boolean empty() {
  34. return stack.isEmpty();
  35. }
  36. }