当前位置:   article > 正文

基本数据结构底层原理——线性存储和链式存储_什么是线性存储

什么是线性存储

线性存储

线性存储指的是顺序表,其底层原理通过数组来实现。数组中的元素是有序的,通过下标访问。对于需要频繁访问使用的元素,使用顺序表的效率非常之高。

顺序表的优缺点

优点:

  • 顺序表有序,访问顺序表中的数据很快。

缺点:

  • 由于底层由数组实现,每当分配的空间用完后都需要扩容,而对于未使用的空间,也是一种浪费。

  • 进行插入和删除操作的效率很低,因为可能需要挪动大量的元素。

模拟实现顺序表

  1. public class MyArraylist {
  2. public int[] elem;
  3. public int usedSize;//0
  4. //默认容量
  5. private static final int DEFAULT_SIZE = 10;
  6. public MyArraylist() {
  7. this.elem = new int[DEFAULT_SIZE];
  8. }
  9. /**
  10. * 打印顺序表:
  11. * 根据usedSize判断即可
  12. */
  13. public void display() {
  14. for (int i = 0; i < this.usedSize; i++) {
  15. System.out.print(this.elem[i] + " ");
  16. }
  17. System.out.println();
  18. }
  19. // 新增元素,默认在数组最后新增
  20. public void add(int data) {
  21. //判断
  22. if (isFull()) {
  23. //扩容
  24. this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
  25. }
  26. this.elem[this.usedSize] = data;
  27. this.usedSize++;
  28. }
  29. /**
  30. * 判断当前的顺序表是不是满的!
  31. *
  32. * @return true:满 false代表空
  33. */
  34. public boolean isFull() {
  35. return this.usedSize == this.elem.length;
  36. }
  37. private boolean checkPosInAdd(int pos) {
  38. //判断
  39. if (pos < 0 || pos > this.usedSize)
  40. return false;
  41. return true;//合法
  42. }
  43. // 在 pos 位置新增元素
  44. public void add(int pos, int data) {
  45. if (checkPosInAdd(pos)) {
  46. if (isFull()) {
  47. //扩容
  48. this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
  49. }
  50. // 1 2 3 4 5 pos: 2 -> 99
  51. // 1 2 99 3 4 5
  52. for (int i = this.usedSize; i > pos; i--) {
  53. this.elem[i] = this.elem[i - 1];
  54. }
  55. this.elem[pos] = data;
  56. this.usedSize++;
  57. }
  58. }
  59. // 判定是否包含某个元素
  60. public boolean contains(int toFind) {
  61. for (int i = 0; i < this.usedSize; i++) {
  62. if (this.elem[i] == toFind) {
  63. return true;
  64. }
  65. }
  66. return false;
  67. }
  68. // 查找某个元素对应的位置
  69. public int indexOf(int toFind) {
  70. for (int i = 0; i < this.usedSize; i++) {
  71. if (this.elem[i] == toFind) {
  72. return i;
  73. }
  74. }
  75. return -1;
  76. }
  77. private void checkIndex(int pos) {
  78. if (pos < 0 || pos >= this.usedSize) {
  79.             //该异常需要自定义
  80. throw new IndexOutOfException("位置输入不合法!");
  81. }
  82. }
  83. // 获取 pos 位置的元素
  84. public int get(int pos) {
  85. //判断
  86. try {
  87. checkIndex(pos);
  88. } catch (IndexOutOfException e) {
  89. e.printStackTrace();
  90. }
  91. return this.elem[pos];
  92. }
  93. private boolean isEmpty() {
  94. if (this.usedSize == 0) {
  95. return true;
  96. }
  97. return false;
  98. }
  99. // 给 pos 位置的元素设为【更新为】 value
  100. public void set(int pos, int value) {
  101. //判断
  102. try {
  103. checkIndex(pos);
  104. } catch (IndexOutOfException e) {
  105. e.printStackTrace();
  106. }
  107. this.elem[pos] = value;
  108. }
  109. /**
  110. * 删除第一次出现的关键字key
  111. *
  112. * @param key
  113. */
  114. public void remove(int key) {
  115. //查找位置
  116. int index = indexOf(key);
  117. if (index == -1) {
  118. System.out.println("没有找到这个数据!");
  119. return;
  120. }
  121. for (int i = index; i < this.usedSize - 1; i++) {
  122. this.elem[i] = this.elem[i + 1];
  123. }
  124. this.usedSize--;
  125. }
  126. // 获取顺序表长度
  127. public int size() {
  128. return this.usedSize;
  129. }
  130. // 清空顺序表
  131. public void clear() {
  132. this.usedSize = 0;
  133. }
  134. }

链式存储

链式存储主要指链表,链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表有一个个结点连接而成,每个结点都存储着一定的信息,它可以包含存放的数据信息,下一个结点的地址和上一个结点的地址。通过这些地址可以访问到下一个结点,从而实现链式访问。

根据链表中结点所包含的信息不同,链表可以是单向或者双向,可以是循环或者非循环。除此之外,链表可以带头或者不带头。因此链表可以分为8种,其中,以单向不带头非循环链表和双向不带头非循环链表为主。

链表的优缺点

优点:

链表不需要初始化,直接添加元素即可。

对于增删操作,只需更改前一个结点和后一个结点的指向即可。

缺点:

查找元素只能通过遍历来实现,耗时较多。对于单链表,不能后退,如果必须得到该结点和上一个结点,必须在遍历到上一个结点时记录下来。

链表的模拟实现(单链表)

  1. // 1、无头单向非循环链表实现
  2. public class SingleLinkedList {
  3. static class ListNode {
  4. public int val;
  5. public ListNode next;
  6. public ListNode() {
  7. }
  8. public ListNode(int val) {
  9. this.val = val;
  10. }
  11. public ListNode(int val, ListNode next) {
  12. this.val = val;
  13. this.next = next;
  14. }
  15. }
  16. public ListNode head;
  17. //头插法
  18. public void addFirst(int data) {
  19. ListNode node = new ListNode(data);
  20. node.next = head;
  21. head = node;
  22. }
  23. //尾插法
  24. public void addLast(int data) {
  25. ListNode node = new ListNode(data);
  26. if (head == null) {
  27. head = node;
  28. return;
  29. }
  30. ListNode cur = head;
  31. while (cur.next != null) {
  32. cur = cur.next;
  33. }
  34. cur.next = node;
  35. }
  36. private Boolean checkIndex(int index) {
  37. return index >= 0 && index <= size();
  38. }
  39. private ListNode findIndex(int index) {
  40. ListNode cur = head;
  41. int count = 0;
  42. while (count < index - 1) {
  43. cur = cur.next;
  44. count++;
  45. }
  46. return cur;
  47. }
  48. //任意位置插入,第一个数据节点为0号下标
  49. public boolean addIndex(int index,int data) {
  50. if (!checkIndex(index)) {
  51. return false;
  52. }
  53. if (index == 0) {
  54. addFirst(data);
  55. return true;
  56. }else if (index == size()) {
  57. addLast(data);
  58. return true;
  59. }
  60. ListNode cur = findIndex(index);
  61. ListNode node = new ListNode(data);
  62. node.next = cur.next;
  63. cur.next = node;
  64. return true;
  65. }
  66. //查找是否包含关键字key是否在单链表当中
  67. public boolean contains(int key) {
  68. ListNode cur = head;
  69. while (cur != null) {
  70. if (cur.val == key) {
  71. return true;
  72. }
  73. cur = cur.next;
  74. }
  75. return false;
  76. }
  77. private ListNode findPrevNode(int key) {
  78. ListNode cur = head;
  79. while (cur.next != null) {
  80. if (cur.next.val == key) {
  81. return cur;
  82. }
  83. cur = cur.next;
  84. }
  85. return null;
  86. }
  87. //删除第一次出现关键字为key的节点
  88. public void remove(int key) {
  89. if (head == null) {
  90. return;
  91. }
  92. if (head.val == key) {
  93. head = head.next;
  94. return;
  95. }
  96. ListNode cur = findPrevNode(key);
  97. if (cur == null) {
  98. return;
  99. }
  100. cur.next = cur.next.next;
  101. }
  102. //删除所有值为key的节点
  103. public void removeAllKey(int key) {
  104. if (head == null) {
  105. return;
  106. }
  107. ListNode p = head;
  108. ListNode q = head.next;
  109. while (q != null) {
  110. if (q.val == key) {
  111. p.next = q.next;
  112. }else {
  113. p = q;
  114. }
  115. q = q.next;
  116. }
  117. if (head.val == key) {
  118. head = head.next;
  119. }
  120. }
  121. //得到单链表的长度
  122. public int size() {
  123. int count = 0;
  124. ListNode cur = head;
  125. while (cur != null) {
  126. cur = cur.next;
  127. count++;
  128. }
  129. return count;
  130. }
  131. public void display() {
  132. ListNode cur = head;
  133. while (cur != null) {
  134. System.out.print(cur.val + " ");
  135. cur = cur.next;
  136. }
  137. System.out.println();
  138. }
  139. public void display(ListNode head) {
  140. ListNode cur = head;
  141. while (cur != null) {
  142. System.out.print(cur.val + " ");
  143. cur = cur.next;
  144. }
  145. System.out.println();
  146. }
  147. public void clear() {
  148. head = null;
  149. }
  150. }

Java的数据结构题一般只涉及到单链表,而Java底层实现的链表是双向链表。

栈和队列

栈和队列底层由顺序表实现,栈和队列大体上是相似的,都具有以下特点:

  • 增删操作都在栈顶(队头)

  • 每次增删或者访问都只能操作一个元素。

二者的区别在于:

栈的逻辑是“先进后出“,而队列是”先进先出“;

栈的实现就像是往箱子里放东西,最先放进去的东西,等到要拿出来的时候,总是最后一个拿出来。

而队列的实现就像去食堂排队打饭,只有排你前面的人都打好饭才能轮到你。

栈和队列具有的功能较少,栈的功能有:push()->入栈,pop()->出栈,peek()->查看栈顶元素,empty()->判空,search->查找等,队列功能有:offer()->入队列,poll()->出队列,peek->查看队头元素等。

当然,除了普通队列以外,还存在双端队列Deque,双端队列不局限于先进先出,它可以在队头或者队尾进,也可以在队头或者队尾出,没有限制。

栈和队列的实现不仅仅可以通过顺序表,也可以通过链表实现,也就是所谓的链式栈和链式队列。而实际上,Java底层自己实现的链表中就包含了栈和队列的所有方法,也就是说链表不单单是链表,它也可以是栈,也可以是队列。

当然,为了方便识别所创建的链表是普通的链表,还是栈或者队列,通常需要更改变量名。

栈和队列有着相似的特点,因此栈可以模拟实现队列,队列也可以模拟实现栈:

用队列实现栈

用栈实现队列

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

闽ICP备14008679号