赞
踩
线性存储指的是顺序表,其底层原理通过数组来实现。数组中的元素是有序的,通过下标访问。对于需要频繁访问使用的元素,使用顺序表的效率非常之高。
优点:
顺序表有序,访问顺序表中的数据很快。
缺点:
由于底层由数组实现,每当分配的空间用完后都需要扩容,而对于未使用的空间,也是一种浪费。
进行插入和删除操作的效率很低,因为可能需要挪动大量的元素。
- public class MyArraylist {
-
- public int[] elem;
- public int usedSize;//0
- //默认容量
- private static final int DEFAULT_SIZE = 10;
-
- public MyArraylist() {
- this.elem = new int[DEFAULT_SIZE];
- }
-
- /**
- * 打印顺序表:
- * 根据usedSize判断即可
- */
- public void display() {
- for (int i = 0; i < this.usedSize; i++) {
- System.out.print(this.elem[i] + " ");
- }
- System.out.println();
- }
-
- // 新增元素,默认在数组最后新增
- public void add(int data) {
- //判断
- if (isFull()) {
- //扩容
- this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
- }
- this.elem[this.usedSize] = data;
- this.usedSize++;
- }
-
- /**
- * 判断当前的顺序表是不是满的!
- *
- * @return true:满 false代表空
- */
- public boolean isFull() {
- return this.usedSize == this.elem.length;
- }
-
-
- private boolean checkPosInAdd(int pos) {
- //判断
- if (pos < 0 || pos > this.usedSize)
- return false;
- return true;//合法
- }
-
- // 在 pos 位置新增元素
- public void add(int pos, int data) {
- if (checkPosInAdd(pos)) {
- if (isFull()) {
- //扩容
- this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
- }
- // 1 2 3 4 5 pos: 2 -> 99
- // 1 2 99 3 4 5
- for (int i = this.usedSize; i > pos; i--) {
- this.elem[i] = this.elem[i - 1];
- }
- this.elem[pos] = data;
- this.usedSize++;
- }
-
- }
-
- // 判定是否包含某个元素
- public boolean contains(int toFind) {
- for (int i = 0; i < this.usedSize; i++) {
- if (this.elem[i] == toFind) {
- return true;
- }
- }
- return false;
- }
-
- // 查找某个元素对应的位置
- public int indexOf(int toFind) {
- for (int i = 0; i < this.usedSize; i++) {
- if (this.elem[i] == toFind) {
- return i;
- }
- }
- return -1;
- }
-
- private void checkIndex(int pos) {
- if (pos < 0 || pos >= this.usedSize) {
- //该异常需要自定义
- throw new IndexOutOfException("位置输入不合法!");
- }
- }
-
- // 获取 pos 位置的元素
- public int get(int pos) {
- //判断
- try {
- checkIndex(pos);
- } catch (IndexOutOfException e) {
- e.printStackTrace();
- }
- return this.elem[pos];
- }
-
- private boolean isEmpty() {
- if (this.usedSize == 0) {
- return true;
- }
- return false;
- }
-
- // 给 pos 位置的元素设为【更新为】 value
- public void set(int pos, int value) {
- //判断
- try {
- checkIndex(pos);
- } catch (IndexOutOfException e) {
- e.printStackTrace();
- }
- this.elem[pos] = value;
- }
-
- /**
- * 删除第一次出现的关键字key
- *
- * @param key
- */
- public void remove(int key) {
- //查找位置
- int index = indexOf(key);
- if (index == -1) {
- System.out.println("没有找到这个数据!");
- return;
- }
- for (int i = index; i < this.usedSize - 1; i++) {
- this.elem[i] = this.elem[i + 1];
- }
- this.usedSize--;
- }
-
- // 获取顺序表长度
- public int size() {
- return this.usedSize;
- }
-
- // 清空顺序表
- public void clear() {
- this.usedSize = 0;
- }
-
- }
链式存储主要指链表,链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表有一个个结点连接而成,每个结点都存储着一定的信息,它可以包含存放的数据信息,下一个结点的地址和上一个结点的地址。通过这些地址可以访问到下一个结点,从而实现链式访问。
根据链表中结点所包含的信息不同,链表可以是单向或者双向,可以是循环或者非循环。除此之外,链表可以带头或者不带头。因此链表可以分为8种,其中,以单向不带头非循环链表和双向不带头非循环链表为主。
优点:
链表不需要初始化,直接添加元素即可。
对于增删操作,只需更改前一个结点和后一个结点的指向即可。
缺点:
查找元素只能通过遍历来实现,耗时较多。对于单链表,不能后退,如果必须得到该结点和上一个结点,必须在遍历到上一个结点时记录下来。
- // 1、无头单向非循环链表实现
- public class SingleLinkedList {
- static class ListNode {
- public int val;
- public ListNode next;
-
- public ListNode() {
- }
-
- public ListNode(int val) {
- this.val = val;
- }
-
- public ListNode(int val, ListNode next) {
- this.val = val;
- this.next = next;
- }
- }
- public ListNode head;
-
- //头插法
- public void addFirst(int data) {
- ListNode node = new ListNode(data);
- node.next = head;
- head = node;
- }
- //尾插法
- public void addLast(int data) {
- ListNode node = new ListNode(data);
- if (head == null) {
- head = node;
- return;
- }
- ListNode cur = head;
- while (cur.next != null) {
- cur = cur.next;
- }
- cur.next = node;
- }
- private Boolean checkIndex(int index) {
- return index >= 0 && index <= size();
- }
- private ListNode findIndex(int index) {
- ListNode cur = head;
- int count = 0;
- while (count < index - 1) {
- cur = cur.next;
- count++;
- }
- return cur;
- }
- //任意位置插入,第一个数据节点为0号下标
- public boolean addIndex(int index,int data) {
- if (!checkIndex(index)) {
- return false;
- }
- if (index == 0) {
- addFirst(data);
- return true;
- }else if (index == size()) {
- addLast(data);
- return true;
- }
- ListNode cur = findIndex(index);
- ListNode node = new ListNode(data);
- node.next = cur.next;
- cur.next = node;
- return true;
- }
- //查找是否包含关键字key是否在单链表当中
- public boolean contains(int key) {
- ListNode cur = head;
- while (cur != null) {
- if (cur.val == key) {
- return true;
- }
- cur = cur.next;
- }
- return false;
- }
- private ListNode findPrevNode(int key) {
- ListNode cur = head;
- while (cur.next != null) {
- if (cur.next.val == key) {
- return cur;
- }
- cur = cur.next;
- }
- return null;
- }
- //删除第一次出现关键字为key的节点
- public void remove(int key) {
- if (head == null) {
- return;
- }
- if (head.val == key) {
- head = head.next;
- return;
- }
- ListNode cur = findPrevNode(key);
- if (cur == null) {
- return;
- }
- cur.next = cur.next.next;
- }
-
- //删除所有值为key的节点
- public void removeAllKey(int key) {
- if (head == null) {
- return;
- }
- ListNode p = head;
- ListNode q = head.next;
- while (q != null) {
- if (q.val == key) {
- p.next = q.next;
- }else {
- p = q;
- }
- q = q.next;
- }
- if (head.val == key) {
- head = head.next;
- }
- }
- //得到单链表的长度
- public int size() {
- int count = 0;
- ListNode cur = head;
- while (cur != null) {
- cur = cur.next;
- count++;
- }
- return count;
- }
- public void display() {
- ListNode cur = head;
- while (cur != null) {
- System.out.print(cur.val + " ");
- cur = cur.next;
- }
- System.out.println();
- }
- public void display(ListNode head) {
- ListNode cur = head;
- while (cur != null) {
- System.out.print(cur.val + " ");
- cur = cur.next;
- }
- System.out.println();
- }
-
- public void clear() {
- head = null;
- }
- }
Java的数据结构题一般只涉及到单链表,而Java底层实现的链表是双向链表。
栈和队列底层由顺序表实现,栈和队列大体上是相似的,都具有以下特点:
增删操作都在栈顶(队头)
每次增删或者访问都只能操作一个元素。
二者的区别在于:
栈的逻辑是“先进后出“,而队列是”先进先出“;
栈的实现就像是往箱子里放东西,最先放进去的东西,等到要拿出来的时候,总是最后一个拿出来。
而队列的实现就像去食堂排队打饭,只有排你前面的人都打好饭才能轮到你。
栈和队列具有的功能较少,栈的功能有:push()->入栈,pop()->出栈,peek()->查看栈顶元素,empty()->判空,search->查找等,队列功能有:offer()->入队列,poll()->出队列,peek->查看队头元素等。
当然,除了普通队列以外,还存在双端队列Deque,双端队列不局限于先进先出,它可以在队头或者队尾进,也可以在队头或者队尾出,没有限制。
栈和队列的实现不仅仅可以通过顺序表,也可以通过链表实现,也就是所谓的链式栈和链式队列。而实际上,Java底层自己实现的链表中就包含了栈和队列的所有方法,也就是说链表不单单是链表,它也可以是栈,也可以是队列。
当然,为了方便识别所创建的链表是普通的链表,还是栈或者队列,通常需要更改变量名。
栈和队列有着相似的特点,因此栈可以模拟实现队列,队列也可以模拟实现栈:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。