【数据结构与算法之线性结构】数组

梦里梦外; 2024-03-25 12:35 190阅读 0赞

【数据结构与算法之线性结构】数组

文章目录

      • 【数据结构与算法之线性结构】数组
          • 1 数组的基本概念
          • 2 数组的定义
          • 3 数组的基本操作
            • 向数组中插入元素
            • 从数组中删除元素
          • 4 数组性能分析
          • 5 其他案例
            • 数组元素逆置
            • 删除数组中的重复元素
1 数组的基本概念

数组Array是最简单的数据结构,是由有限个相同类型的变量(或对象)组成的有序集合。

在这里插入图片描述

【数组的特征】

  • 数组用唯一的名字标识,通过数组名可以数组中的元素进行引用,例如array[0] 表示数组array 中的第一个元素 1。
  • 数组中的元素类型必须相同。
  • 数组的内存单元是连续的。
  • 数组中的数据元素都是顺序存放的。有先后关系,且元素之间没有空隙

总之,数组是一种物理空间和逻辑形式都连续的线性数据结构。

2 数组的定义
  1. package 数组的定义;
  2. /**
  3. * @Projectname: JavaData_StructureAndAlgorithm
  4. * @Classname: ArrayDefine
  5. * @Author: Ding Jiaxiong
  6. * @Data:2023/3/12 17:58
  7. */
  8. public class ArrayDefine {
  9. public static void main(String[] args) {
  10. int[] array1; // 【推荐】
  11. int array2[];
  12. }
  13. }

当然这样只是声明了一个引用变量,其本质是一个指针,要使用该数组,需要进行初始化。

  1. package 数组的定义;
  2. /**
  3. * @Projectname: JavaData_StructureAndAlgorithm
  4. * @Classname: ArrayInitialization
  5. * @Author: Ding Jiaxiong
  6. * @Data:2023/3/12 18:01
  7. */
  8. public class ArrayInitialization {
  9. public static void main(String[] args) {
  10. // 静态初始化
  11. // ①
  12. int[] array1 = {
  13. 1, 2, 3, 4, 5, 6};
  14. // ②
  15. int[] array2;
  16. array2 = new int[]{
  17. 1, 2, 3, 4, 5, 6};
  18. // 动态初始化
  19. // ①
  20. int[] array3 = new int[6]; // 不指定元素值
  21. // ②
  22. int[] array4;
  23. array4 = new int[6];
  24. }
  25. }

【定义我们自己的数组类】对数组的属性和操作进行封装。

  1. package 数组的定义;
  2. /**
  3. * @Projectname: JavaData_StructureAndAlgorithm
  4. * @Classname: MyArray
  5. * @Author: Ding Jiaxiong
  6. * @Data:2023/3/12 18:05
  7. */
  8. public class MyArray {
  9. int[] array; // 数组本身
  10. int elemNumber; // 记录数组中的元素个数
  11. public MyArray(int capacity) {
  12. array = new int[capacity]; // 动态初始化数组,长度为capacity
  13. elemNumber = capacity;
  14. }
  15. public boolean insertElem(int elem, int index) {
  16. // 在数组的第index索引处插入elem元素
  17. }
  18. public boolean deleteElem(int index) {
  19. // 删除数组index索引处的元素
  20. }
  21. }
3 数组的基本操作
向数组中插入元素

在数组的index 索引位置插入elem元素,意为原index 索引以及之后位置上的元素都要向后移动一个位置。

主要就分为两步:

① 将数组中index 索引上以及之后的所有元素都向后移动一个位置,把数组的index 位置空出。

② 将新的元素插到数组的index 索引位置。

在这里插入图片描述

  1. package 数组的基本操作;
  2. /**
  3. * @Projectname: JavaData_StructureAndAlgorithm
  4. * @Classname: MyArray
  5. * @Author: Ding Jiaxiong
  6. * @Data:2023/3/12 18:14
  7. */
  8. public class MyArray {
  9. int[] array; // 数组本身
  10. int elemNumber; // 记录数组中的元素个数
  11. public MyArray(int capacity) {
  12. array = new int[capacity]; // 动态初始化数组,长度为capacity
  13. elemNumber = 0;
  14. }
  15. public boolean insertElem(int elem, int index) {
  16. // 在数组的第index个位置处插入elem元素【不是索引位置】
  17. // 判断index是否合法
  18. if (index < 1 || index > elemNumber + 1) {
  19. System.out.println("插入位置错误");
  20. return false;
  21. }
  22. // 将index上元素及之后所有元素全部往后移动
  23. for (int i = elemNumber - 1; i >= index - 1; i--) {
  24. array[i + 1] = array[i];
  25. }
  26. // 将新元素插入空的array[index]
  27. array[index - 1] = elem; // array[index - 1] 就是数组的第index 个元素
  28. elemNumber++; // 数组元素数量 + 1
  29. return true;
  30. }
  31. public void printArray() {
  32. // 打印数组内容
  33. for (int i = 0; i < elemNumber; i++) {
  34. System.out.print(array[i] + " ");
  35. }
  36. System.out.println(); // 换行
  37. }
  38. public static void main(String[] args) {
  39. MyArray array = new MyArray(8);
  40. array.insertElem(3, 1);
  41. array.insertElem(5, 2);
  42. array.insertElem(2, 3);
  43. array.insertElem(7, 4);
  44. array.insertElem(8, 5);
  45. array.printArray();
  46. array.insertElem(0, 3);
  47. array.printArray();
  48. }
  49. }

运行结果

在这里插入图片描述

这里没有严格的写清楚index,它不是索引,它不是索引,它不是索引。

模拟一下数组越界。

在这里插入图片描述

因为容量只有5,想再插入第6个元素,就不行了。

来一个扩容机制。

  1. package 数组的基本操作;
  2. /**
  3. * @Projectname: JavaData_StructureAndAlgorithm
  4. * @Classname: MyArray
  5. * @Author: Ding Jiaxiong
  6. * @Data:2023/3/12 18:14
  7. */
  8. public class MyArray {
  9. int[] array; // 数组本身
  10. int elemNumber; // 记录数组中的元素个数
  11. public MyArray(int capacity) {
  12. array = new int[capacity]; // 动态初始化数组,长度为capacity
  13. elemNumber = 0;
  14. }
  15. public boolean insertElem(int elem, int index) {
  16. // 在数组的第index个位置处插入elem元素【不是索引位置】
  17. // 判断index是否合法
  18. if (index < 1 || index > elemNumber + 1) {
  19. System.out.println("插入位置错误");
  20. return false;
  21. }
  22. // 当数组元素数量等于数组容量时,对其进行扩容
  23. if (elemNumber == array.length) {
  24. increaseCapacity();
  25. }
  26. // 将index上元素及之后所有元素全部往后移动
  27. for (int i = elemNumber - 1; i >= index - 1; i--) {
  28. array[i + 1] = array[i];
  29. }
  30. // 将新元素插入空的array[index]
  31. array[index - 1] = elem; // array[index - 1] 就是数组的第index 个元素
  32. elemNumber++; // 数组元素数量 + 1
  33. return true;
  34. }
  35. private void increaseCapacity() {
  36. // 初始化一个新数组,容量是array容量的2倍
  37. int[] arrayTmp = new int[array.length * 2];
  38. // 将原数组array的内容拷贝到新数组
  39. System.arraycopy(array, 0, arrayTmp, 0, array.length);
  40. array = arrayTmp; // 让array指向这个数组
  41. }
  42. public void printArray() {
  43. // 打印数组内容
  44. for (int i = 0; i < elemNumber; i++) {
  45. System.out.print(array[i] + " ");
  46. }
  47. System.out.println(); // 换行
  48. }
  49. public static void main(String[] args) {
  50. MyArray array = new MyArray(5);
  51. array.insertElem(3, 1);
  52. array.insertElem(5, 2);
  53. array.insertElem(2, 3);
  54. array.insertElem(7, 4);
  55. array.insertElem(8, 5);
  56. array.printArray();
  57. array.insertElem(0, 3);
  58. array.printArray();
  59. }
  60. }

再试一次【再说一次,index 为 3,是第3个元素,不是索引为3的元素。】

在这里插入图片描述

这次就过去了。

从数组中删除元素

与插入相反,只需要将index 位置之后的元素,不含index本身,顺序向前移动一个位置,并将数组元素的数量 - 1,即可完成删除操作。

在这里插入图片描述

  1. package 数组的基本操作;
  2. /**
  3. * @Projectname: JavaData_StructureAndAlgorithm
  4. * @Classname: MyArray
  5. * @Author: Ding Jiaxiong
  6. * @Data:2023/3/12 18:14
  7. */
  8. public class MyArray {
  9. int[] array; // 数组本身
  10. int elemNumber; // 记录数组中的元素个数
  11. public MyArray(int capacity) {
  12. array = new int[capacity]; // 动态初始化数组,长度为capacity
  13. elemNumber = 0;
  14. }
  15. public boolean insertElem(int elem, int index) {
  16. // 在数组的第index个位置处插入elem元素【不是索引位置】
  17. // 判断index是否合法
  18. if (index < 1 || index > elemNumber + 1) {
  19. System.out.println("插入位置错误");
  20. return false;
  21. }
  22. // 当数组元素数量等于数组容量时,对其进行扩容
  23. if (elemNumber == array.length) {
  24. increaseCapacity();
  25. }
  26. // 将index上元素及之后所有元素全部往后移动
  27. for (int i = elemNumber - 1; i >= index - 1; i--) {
  28. array[i + 1] = array[i];
  29. }
  30. // 将新元素插入空的array[index]
  31. array[index - 1] = elem; // array[index - 1] 就是数组的第index 个元素
  32. elemNumber++; // 数组元素数量 + 1
  33. return true;
  34. }
  35. private void increaseCapacity() {
  36. // 初始化一个新数组,容量是array容量的2倍
  37. int[] arrayTmp = new int[array.length * 2];
  38. // 将原数组array的内容拷贝到新数组
  39. System.arraycopy(array, 0, arrayTmp, 0, array.length);
  40. array = arrayTmp; // 让array指向这个数组
  41. }
  42. // 删除第index个位置上的元素【index非索引下标】
  43. public boolean deleteElem(int index) {
  44. // 判断index是否合法
  45. if (index < 1 || index > elemNumber) {
  46. System.out.println("删除元素位置错误");
  47. return false;
  48. }
  49. // 将index 位置之后的元素向前移动
  50. for (int i = index; i < elemNumber; i++) {
  51. array[i - 1] = array[i];
  52. }
  53. // 数组元素数量 - 1
  54. elemNumber--;
  55. return true;
  56. }
  57. public void printArray() {
  58. // 打印数组内容
  59. for (int i = 0; i < elemNumber; i++) {
  60. System.out.print(array[i] + " ");
  61. }
  62. System.out.println(); // 换行
  63. }
  64. public static void main(String[] args) {
  65. MyArray array = new MyArray(5);
  66. array.insertElem(3, 1);
  67. array.insertElem(5, 2);
  68. array.insertElem(2, 3);
  69. array.insertElem(7, 4);
  70. array.insertElem(8, 5);
  71. array.printArray();
  72. array.deleteElem(3);
  73. array.printArray();
  74. }
  75. }

运行结果【注意是删第3个元素,不是索引为3的元素】

在这里插入图片描述

4 数组性能分析
  • O ( 1 ) O(1) O(1) 时间复杂度直接定位元素
  • 插入、删除元素时间复杂度都是 O ( n ) O(n) O(n)
5 其他案例
数组元素逆置

当然Arrays 工具类谁都会用,搞算法直接调方法就没必要了

编写函数reverseArray(),将数组中的元素逆置。

  1. // 逆置
  2. public void reverseArray() {
  3. int temElem; // 数据缓冲【实现数据交换】
  4. int low = 0, high = elemNumber - 1; //low指向数组第一个元素arr[0], high指向最后一个元素arr[elemNumber - 1]
  5. while (low < high) {
  6. temElem = array[low];
  7. array[low] = array[high];
  8. array[high] = temElem;
  9. low++;
  10. high--;
  11. }
  12. }

测试

  1. public static void main(String[] args) {
  2. MyArray array = new MyArray(5);
  3. for (int i = 0; i < 5; i++) {
  4. array.insertElem(i + 1, i + 1);
  5. }
  6. array.printArray();
  7. array.reverseArray();
  8. array.printArray();
  9. }

运行结果

在这里插入图片描述

删除数组中的重复元素

实现有很多种方法:

  1. 定位一个数组元素,从前往后扫描,如果发现与定位元素相同的元素,就删除它。不断调整定位,直到删除所有重复元素。

    1. void purge() {
    2. int i = 0, j;
    3. while (i < elemNumber) {
    4. j = i + 1; // 从arr[i + 1]开始逐个比较
    5. while (j < elemNumber) {
    6. if (array[i] == array[j]) {
    7. deleteElem(j + 1);
    8. } else {
    9. j++;
    10. }
    11. }
    12. i++; // 定位下一个元素
    13. }
    14. }
    15. public static void main(String[] args) {
    16. MyArray array = new MyArray(10);
    17. // [1,1,3,5,2,3,1,5,6,8]
    18. array.insertElem(1, 1);
    19. array.insertElem(1, 2);
    20. array.insertElem(3, 3);
    21. array.insertElem(5, 4);
    22. array.insertElem(2, 5);
    23. array.insertElem(3, 6);
    24. array.insertElem(1, 7);
    25. array.insertElem(5, 8);
    26. array.insertElem(6, 9);
    27. array.insertElem(8, 10);
    28. array.printArray();
    29. array.purge();
    30. array.printArray();
    31. }

    运行结果

    在这里插入图片描述

    功能是实现了,但是时间复杂度已经来到了 O ( n 3 ) O(n^3) O(n3) 【 两次 w h i l e 循环 ∗ 删除元素 两次while循环 * 删除元素 两次while循环∗删除元素】

  2. 稍微优化一下第一种方法,当找到重复元素后,不立马进行删除,而是做个标记,扫描完毕后,整体删除,这样就只需要执行一次二重循环找到重复元素,再加上一次循环删除就OK了,时间复杂度 O ( n 2 ) + O ( n ) → O ( n 2 ) O(n^2) + O(n) → O(n^2) O(n2)+O(n)→O(n2)

    1. void purge2() {
    2. int i, j;
    3. int FLAG = -111;
    4. int number = elemNumber;
    5. // 将重复元素用FLAG覆盖
    6. for (i = 0; i < number; i++) {
    7. if (array[i] != FLAG) {
    8. for (j = i + 1; j < elemNumber; j++) {
    9. if (array[i] == array[j]) {
    10. array[j] = FLAG;
    11. }
    12. }
    13. }
    14. }
    15. // 找到第一个FLAG标记
    16. for (i = 0; array[i] != FLAG; i++) ;
    17. for (j = i + 1; j < number; ) {
    18. if (array[j] != FLAG) {
    19. // 若array[j] 不是FLAG,则用array[j] 覆盖array[i],并且向后走
    20. array[i++] = array[j++];
    21. } else {
    22. // 若array[j] 是FLAG,则j 直接向后走,寻找下一个非FLAG的有效值
    23. j++;
    24. }
    25. elemNumber = i;
    26. }
    27. }

    测试

    在这里插入图片描述

  3. 使用哈希表。

    1. void purge3() {
    2. int i, j;
    3. int FLAG = -111;
    4. int number = elemNumber;
    5. HashSet<Integer> set = new HashSet<Integer>();
    6. for (i = 0; i < number; i++) {
    7. if (!set.add(array[i])) {
    8. // 如果不能存储到集合中,则说明array[i]与set 中的元素有重复
    9. array[i] = FLAG;
    10. }
    11. }
    12. for (i = 0; array[i] != FLAG; i++) ; // 找到第一个FLAG
    13. for (j = i + 1; j < number; ) {
    14. if (array[j] != FLAG) {
    15. array[i++] = array[j++];
    16. } else {
    17. j++;
    18. }
    19. elemNumber = i;
    20. }
    21. }

    测试

    在这里插入图片描述

    利用HashSet 查找重复元素时间复杂度为 O ( n ) O(n) O(n),将数组中的FLAG全部删除 O ( n ) O(n) O(n),所以整个算法时间复杂度 O ( n ) O(n) O(n),代价就是需要一个HashSet 额外开了空间。

    之所以标记FLAG其实是为了维持原本数组的顺序(稳定),如果不在意数组元素顺序,只考虑删除重复元素就完事儿的话,可以直接将HashSet 中的元素读出,但也就是因为HashSet 中数据无序,所以从其中读出的元素可能与原数组元素顺序不一致。

发表评论

表情:
评论列表 (有 0 条评论,190人围观)

还没有评论,来说两句吧...

相关阅读