五种List集合的简单实现
- 一、数组形式
- 二、单向链表形式
- 三、含哨兵节点的单向链表形式
- 四、含哨兵节点的双向链表形式
- 五、含哨兵节点的环形链表形式
本文是对不同形式List集合的增删改查实现,仅是对学习过程进行记录
一、数组形式
关键点:
- 有三个成员变量:集合对象array、元素个数size、数组长度capacity
- 在扩容时,将旧array中的数据拷贝到新array对象中
- 新增先处理add(int index, int data)方法,考虑扩容、后移元素、赋值三个步骤
public class SelfArrayList {/*** 真实的数据*/private int[] array = {};/*** 集合真实大小*/private int size = 0;/*** 集合容量*/private int capacity = 8;public void add(int data) {initAndGroup();add(size, data);}public void add(int index, int data) {initAndGroup();// 仅支持将元素添加到当前集合最后位置if (index <= size) {for (int i = size; i > index; i--) {array[i] = array[i - 1];}array[index] = data;size++;}}public int delete() {return array[--size];}public void update(int index, int data) {if (index > 0 && index < size) {array[index] = data;}}public int get(int index) {return array[index];}public void forEach(Consumer<Integer> consumer) {for (int i = 0; i < size; i++) {consumer.accept(array[i]);}}private void initAndGroup() {if (size == 0) {array = new int[capacity];} else if (size == capacity) {capacity += capacity >> 1;int[] newArray = new int[capacity];System.arraycopy(array, 0, newArray, 0, size);array = newArray;}}
}
二、单向链表形式
关键点:
- 成员变量就一个Node head节点
- 主要是三个关键方法add(int index, int value)、delete(int index)、findNode(int index),三个方法里面findNode(int index)又是关键
public class SelfLinkList {private Node head = null;public void addHead(int value) {head = new Node(value, head);}public void addTail(int value) {Node tail = findTail();// 加入的节点为第一个节点if (tail == null) {addHead(value);} else {tail.next = new Node(value, null);}}/*** 核心方法 1 of 3*/public void add(int index, int value) {if (index <= 0) {addHead(value);return;}// 找到对应索引的上一个节点Node pre = findNode(index - 1);if (pre == null) {return;}pre.next = new Node(value, pre.next);}public Node deleteFirst() {Node deleteNode = head;head = head.next;return deleteNode;}/*** 核心方法 2 of 3*/public Node delete(int index) {Node pre = findNode(index - 1);// 删除头节点数据if (pre == null) {return deleteFirst();}Node cur = pre.next;// 删除末尾不存在数据if (cur == null) {return null;}pre.next = cur.next;return cur;}/*** 核心方法 3 of 3* index 小于0或者大于链表长度 返回null*/public Node findNode(int index) {Node temp = head;int nodeSize = 0;// 循环遍历 找到对应的节点while (temp != null) {if (index == nodeSize) {return temp;}temp = temp.next;nodeSize++;}return null;}public void forEach(Consumer<Integer> consumer) {Node temp = head;while (temp != null) {consumer.accept(temp.value);temp = temp.next;}}private Node findTail() {if (head == null) {return head;}Node tail = head;while (tail.next != null) {tail = tail.next;}return tail;}@AllArgsConstructorpublic static class Node {public int value;public Node next;}
}
三、含哨兵节点的单向链表形式
关键点:
- 自带一个head节点,其目的是为了简化后续的操作流程,避免出现null的场景
- 主要是三个关键方法add(int index, int value)、delete(int index)、findNode(int index),三个方法里面findNode(int index)又是关键,特别注意哨兵节点和普通参数节点返回下标的差异
public class SelfSentinelLinkList {private final SelfSentinelLinkList.Node head = new SelfSentinelLinkList.Node(0, null);public void addHead(int value) {add(0, value);}public void addTail(int value) {SelfSentinelLinkList.Node tail = findTail();tail.next = new SelfSentinelLinkList.Node(value, null);}/*** 核心方法 1 of 3*/public void add(int index, int value) {SelfSentinelLinkList.Node pre = findNode(index - 1);// 添加元素超过链表长度if (pre == null) {return;}pre.next = new SelfSentinelLinkList.Node(value, pre.next);}public SelfSentinelLinkList.Node deleteFirst() {return delete(0);}/*** 核心方法 2 of 3*/public SelfSentinelLinkList.Node delete(int index) {SelfSentinelLinkList.Node pre = findNode(Math.max(-1, index - 1));// 删除索引下标超过链表长度if (pre == null) {return null;}SelfSentinelLinkList.Node deleted = pre.next;// 删除索引下标超过链表长度if (deleted == null) {return null;}pre.next = deleted.next;return deleted;}/*** 核心方法 3 of 3* 哨兵节点 index=-1* 数据节点 index>=0*/public SelfSentinelLinkList.Node findNode(int index) {SelfSentinelLinkList.Node temp = head;int nodeSize = -1;while (temp != null) {if (index == nodeSize) {return temp;}temp = temp.next;nodeSize++;}return null;}public void forEach(Consumer<Integer> consumer) {SelfSentinelLinkList.Node temp = head.next;while (temp != null) {consumer.accept(temp.value);temp = temp.next;}}private SelfSentinelLinkList.Node findTail() {SelfSentinelLinkList.Node tail = head;while (tail.next != null) {tail = tail.next;}return tail;}@AllArgsConstructorpublic static class Node {public int value;public SelfSentinelLinkList.Node next;}
}
四、含哨兵节点的双向链表形式
关键点:
- 默认初始化两个节点head、tail,每增加一个数据就new一个Node,并且同时维护前后4条关系链(new Node的时候已经维护了两条),删除同理
- 主要是三个关键方法add(int index, int value)、delete(int index)、findNode(int index),三个方法里面findNode(int index)又是关键
public class SelfSentinelDoublyLinkedList {/*** 头哨兵*/private final Node head = new Node(null, 0, null);/*** 尾哨兵*/private final Node tail = new Node(null, 0, null);public SelfSentinelDoublyLinkedList() {head.next = tail;tail.prev = head;}public void addFirst(int value) {add(0, value);}/*** 双向链表的特色地方 就是在操作last的时候*/public void addLast(int value) {Node last = tail.prev;Node added = new Node(last, value, tail);last.next = added;tail.prev = added;}/*** 新增数据 核心方法 1 of 3*/public void add(int index, int value) {// 找到要插入数据的上一个节点Node prev = findNode(index - 1);if (prev == null) {return;}Node next = prev.next;Node inserted = new Node(prev, value, next);prev.next = inserted;next.prev = inserted;}public void deleteFirst() {delete(0);}public void deleteLast() {Node removed = tail.prev;if (removed == head) {return;}Node prev = removed.prev;prev.next = tail;tail.prev = prev;}/*** 删除数据 核心方法 2 of 3*/public void delete(int index) {Node prev = findNode(index - 1);if (prev == null) {return;}Node removed = prev.next;if (removed == tail) {return;}Node next = removed.next;prev.next = next;next.prev = prev;}/*** 根据索引查找数据 核心方法 3 of 3*/private Node findNode(int index) {int i = -1;for (Node p = head; p != tail; p = p.next, i++) {if (i == index) {return p;}}return null;}public void forEach(Consumer<Integer> consumer) {Node p = head.next;while (p != tail) {consumer.accept(p.value);p = p.next;}}static class Node {/*** 上一个节点指针*/private Node prev;/*** 下一个节点指针*/private Node next;/*** 具体数据*/private final int value;public Node(Node prev, int value, Node next) {this.prev = prev;this.value = value;this.next = next;}}
}
五、含哨兵节点的环形链表形式
关键点:
- 默认只初始化一个节点sentinel,由于是一个环,所以该节点既可以是头哨兵,也可以是尾哨兵。增加一个数据就new一个Node,并且同时维护前后4条关系链(new Node的时候已经维护了两条)
- 在初始化SelfRingLinkedListSentinel这个链表的时候,构建好环的关联关系
- 新增、删除数据时,都需要维护好四条关系链
public class SelfRingLinkedListSentinel {/*** 哨兵节点*/private final Node sentinel = new Node(null, 0, null);public SelfRingLinkedListSentinel() {sentinel.next = sentinel;sentinel.prev = sentinel;}/*** 添加到第一个*/public void addFirst(int value) {Node next = sentinel.next;Node prev = sentinel;Node added = new Node(prev, value, next);prev.next = added;next.prev = added;}/*** 添加到最后一个*/public void addLast(int value) {Node prev = sentinel.prev;Node next = sentinel;Node added = new Node(prev, value, next);prev.next = added;next.prev = added;}/*** 删除第一个*/public void removeFirst() {Node removed = sentinel.next;if (removed == sentinel) {return;}Node a = sentinel;Node b = removed.next;a.next = b;b.prev = a;}/*** 删除最后一个*/public void removeLast() {Node removed = sentinel.prev;if (removed == sentinel) {return;}Node a = removed.prev;Node b = sentinel;a.next = b;b.prev = a;}/*** 根据值删除节点*/public void removeByValue(int value) {Node removed = findNodeByValue(value);if (removed != null) {Node prev = removed.prev;Node next = removed.next;prev.next = next;next.prev = prev;}}/*** 遍历找到对应的元素*/private Node findNodeByValue(int value) {Node p = sentinel.next;while (p != sentinel) {if (p.value == value) {return p;}p = p.next;}return null;}public void forEach(Consumer<Integer> consumer) {Node p = sentinel.next;while (p != sentinel) {consumer.accept(p.value);p = p.next;}}static class Node {int value;Node prev;Node next;public Node(Node prev, int value, Node next) {this.prev = prev;this.value = value;this.next = next;}}
}