稀疏数组

基本介绍

image.png

  1. 稀疏数组的列数固定为3列
  2. 稀疏数组的行数=有效数据个数+1

稀疏数组的意义:

  1. 原数组中存在大量的无效数据,占据了大量的存储空间,有效数据很少。
  2. 压缩存储可以节省存储空间,避免资源不必要的浪费,在数据序列化到磁盘时,压缩存储可以提高IO效率。

处理步骤:

  1. 记录原数组的行数,列数,有效数据(不为0的数)。
  2. 把有效数据的的行数,列数和值记录在一个小规模的数组中,从而缩小程序的规模。

    实际应用

  3. 使用稀疏数组来保留图中的棋盘(二维数组)

  4. 将稀疏数组重新恢复为二维数组

image.png
代码:

  1. package ch03_sparseArray;
  2. /**
  3. * 稀疏数组
  4. */
  5. public class SparseArray {
  6. public static void main(String[] args) {
  7. /**
  8. *使用二维数组,模拟棋盘数据
  9. */
  10. int[][] array = new int[11][11];
  11. array[1][2] = 1;
  12. array[2][3] = 2;
  13. //打印棋盘,查看效果
  14. for (int i = 0; i < array.length; i++) {
  15. for (int j = 0; j < array[i].length; j++) {
  16. System.out.print(array[i][j] + " ");
  17. }
  18. System.out.println();
  19. }
  20. /**
  21. * 使用稀疏数组来保存以上二维数组
  22. */
  23. //计算二维数组中有效数据(不为0)的个数。
  24. int sum = 0;
  25. for (int i = 0; i < array.length; i++) {
  26. for (int j = 0; j < array[i].length; j++) {
  27. if (array[i][j] != 0) {
  28. sum++;
  29. }
  30. }
  31. }
  32. /*
  33. 定义稀疏数组
  34. 1.稀疏数组的列数固定为3列
  35. 2.稀疏数组的行数=有效数据个数+1
  36. */
  37. int[][] sparseArray = new int[sum + 1][3];
  38. //原数组行数
  39. sparseArray[0][0] = 11;
  40. //原数组列数
  41. sparseArray[0][1] = 11;
  42. //有效数据个数
  43. sparseArray[0][2] = sum;
  44. //将有效数据存放至稀疏数组中
  45. int count = 0;
  46. for (int i = 0; i < array.length; i++) {
  47. for (int j = 0; j < array[i].length; j++) {
  48. if (array[i][j] != 0) {
  49. count++;
  50. sparseArray[count][0] = i;
  51. sparseArray[count][1] = j;
  52. sparseArray[count][2] = array[i][j];
  53. }
  54. }
  55. }
  56. //打印稀疏数组
  57. System.out.println("-----------------------------------");
  58. for (int[] row : sparseArray) {
  59. for (int val : row) {
  60. System.out.print(val + "\t");
  61. }
  62. System.out.println();
  63. }
  64. /**
  65. * 将稀疏数组重新恢复为二维数组
  66. */
  67. //创建二维数组
  68. int[][] oldArray = new int[sparseArray[0][0]][sparseArray[0][1]];
  69. //赋值
  70. for (int i = 1; i <= count; i++) {
  71. oldArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
  72. }
  73. //打印二维数组
  74. System.out.println("-------------------------------");
  75. for (int[] row : oldArray) {
  76. for (int i : row) {
  77. System.out.print(i+"\t");
  78. }
  79. System.out.println();
  80. }
  81. }
  82. }

队列

基本介绍

image.png

  • 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。
  • 进行插入操作的一端叫做队尾(rear),进行删除操作的一端称为队头(front)。
  • 队列中的元素只能 先进先出(FIFIO)。
  • 队列是一个有序列表,可以用数组和链表来实现。

    循环队列

    数据结构与算法(中) - 图4
    循环队列的出现就是为了解决队列的假溢出问题。
    真溢出:我们运用数组实现队列时,数组长度为 5,我们放入了[1,2,3,4,5],此时如果继续加入 6 时,是真的满了,放不了。
    假溢出:我们运用数组实现队列时,数组长度为 5,我们放入了[1,2,3,4,5],我们将 1,2 出队,此时如果继续加入 6 时,因为数组末尾元素已经被占用,再向后加则会溢出,但是我们的下标 0,和下标 1 还是空闲的。所以我们把这种现象叫做“假溢出”。
    我们用来解决假溢出的方法就是后面满了,就再从头开始,也就是头尾相接的循环,我们把队列的这种头尾相接的顺序存储结构成为循环队列。
    队尾指针指向的位置永远空出1位, 所以队列最大容量比数组长度小1。

    基本操作

    我们发现队列为空时 front == rear,队列满时也是 front == rear,如何区分队列是空还是满?
  1. 设置标记变量 flag。
  2. 当队列为空时,front==rear;

当队列满时,我们保留一个元素空间,也就是说,队列满时,数组内还有一个空间
数据结构与算法(中) - 图5
数据结构与算法(中) - 图6

  • 公式(判断队列是否满了):(rear+1)%queuesize==front
    • rear:尾部指针指向的数组下标
    • front:头部指针指向的数组下标
    • queuesize:队列的长度

代码:

  1. package ch04_queue;
  2. public class ArrayQueue {
  3. public static void main(String[] args) throws Exception {
  4. ArrayQueue arrayQueue = new ArrayQueue(5);
  5. arrayQueue.addQueue(1);
  6. arrayQueue.addQueue(2);
  7. arrayQueue.addQueue(3);
  8. arrayQueue.addQueue(4);
  9. arrayQueue.getQueue();
  10. arrayQueue.getQueue();
  11. arrayQueue.headQueue();
  12. //arrayQueue.output();
  13. }
  14. private int[] array;
  15. private int front;
  16. private int rear;
  17. public ArrayQueue(int capacity) {
  18. this.array = new int[capacity];
  19. }
  20. /**
  21. * 判断队列是否已满
  22. *
  23. * @return:true-满了,false-没满
  24. */
  25. public boolean isFull() {
  26. return (rear + 1) % array.length == front;
  27. }
  28. /**
  29. * 判断队列是否为空
  30. *
  31. * @return:true-空,false-非空
  32. */
  33. public boolean isEmpty() {
  34. return front == rear;
  35. }
  36. /**
  37. * 入队操作
  38. *
  39. * @param element:入队元素
  40. */
  41. public void addQueue(int element) throws Exception {
  42. if (isFull()) {
  43. throw new Exception("队列已满!");
  44. }
  45. array[rear] = element;
  46. rear = (rear + 1) % array.length;
  47. }
  48. /**
  49. * 出队操作
  50. *
  51. * @return
  52. */
  53. public int getQueue() throws Exception {
  54. if (isEmpty()) {
  55. throw new Exception("队列已空!");
  56. }
  57. int getElement = array[front];
  58. front = (front + 1) % array.length;
  59. return getElement;
  60. }
  61. /**
  62. * 输出队列
  63. */
  64. public void output() {
  65. for (int i = front; i != rear; i = (i + 1) % array.length) {
  66. System.out.println(array[i]);
  67. }
  68. }
  69. /**
  70. * 输出队列的第一个数据
  71. */
  72. public void headQueue(){
  73. System.out.println(array[front]);
  74. }
  75. }

递归

基本介绍

概念:
递归就是方法自己调用自己,每次调用时传入的参数是不同的,递归有助于解决程序中复杂的问题,同时可以让代码更为简洁。
规则:

  • 执行一个方法时,就创建一个新的受保护的独立栈空间
  • 方法的局部变量是独立的,不会相互影响
  • 如果方法中使用的是引用型类型变量,比如数组,则会共享引用型的数据
  • 递归必须向退出递归的条件接近,否则就是无限递归,会出现StackOverflowError
  • 一个方法执行完毕后,或者遇到return,会就返回数据,遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕

    递归(迷宫问题)

    image.png