一、概念
链表(linked list):是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的.
链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
二、代码实现
1、首先我们创建节点类
//双链表节点
private static class Node{
E val;
Node next;
Node prev;
Node(E val){
this.val = val;
}
}
2、然后设置调用链表时 初始化头尾节点长度
//构造函数初始化头尾节点
public MyLinkedList_tow(){
this.head = new Node<>(null);
this.tail = new Node<>(null);
head.next = tail;
tail.prev = head;
this.size = 0;
}
3、设置头、尾节点和长度变量
private final Nodehead,tail;
private int size;
4、我们在写一个数据结构时无非就是 增、删、查、改,还有一些像获取长度,判断是否为空的功能函数,我们在这添加两个检查索引是否越界函数。后续做判读就方便多了。
private boolean isElementIndex(int index){
return index>=0 && index= 0 && index <= size;
}
/** 检查 index 索引位置是否可以存在元素 */
private void checkElementIndex(int index){
if(!isElementIndex(index)){
throw new IndexOutOfBoundsException();
}
}
/** 检查 index 索引位置是否可以添加元素 **/
private void checkPositionIndex(int index){
if(!isPositionIndex(index)){
throw new IndexOutOfBoundsException();
}
}
5、在链表中访问一个index 位置的节点值会很困难,必须遍历链表,我们将或取index位置的节点设置一个函数。
private Node getNode(int index){
Node p = head.next;
for (int i = 0; i < index; i++) {
p = p.next;
}
return p;
}
6、增:①public void addFirst();
②public void addLast();
③public void add(int index,E element);
/***** 增 *****/
public void addFirst(E e){
Node x = new Node<>(e);
Node temp = head.next;
//head temp
x.next = temp;
x.prev = head;
head.next = x;
temp.prev = x;
size++;
}
public void addLast(E e){
Node x = new Node<>(e);
Node temp = tail.prev;
//temp tail
x.prev = temp;
x.next = tail;
temp.next = x;
tail.prev = x;
size++;
}
public void add(int index,E element){
checkPositionIndex(index);
Node p = getNode(index);
Node temp = p.prev;
Node x = new Node<>(element);//新节点
//temp p
x.next = p;
x.prev = temp;
temp.next = x;
p.prev = x;
size++;
}
注意:在添加节点时,就是将两边(前后)的两个节点的prev 和 next 指向添加的那个节点,
而添加的节点的 prev 指向前面节点,next指向后面节点。
7、删:①public E removeFirst();
②public E removeLast();
③public E remove(int index,E val);
/***** 删 *****/
public E removeFirst(E e){
if(isEmpty()){
throw new NoSuchElementException();
}
Node x = head.next;
Node temp = x.next;
head.next = temp;
temp.prev = head;
x.next = x.prev = null;
size--;
return x.val;
}
public E removeLast(E e){
if(isEmpty()){
throw new NoSuchElementException();
}
Node x = tail.prev;
Node temp = x.prev;
tail.prev = temp;
temp.next = tail;
x.next = x.prev = null;
size--;
return x.val;
}
public E remove(int index){
checkElementIndex(index);
Node p = getNode(index);
Node perv = p.prev;
Node next = p.next;
perv.next = next;
next.prev = perv;
p.prev = p.next = null;
size--;
return p.val;
}
8、查:①public E getFirst();
②public E getLast();
③public E get(int index);
/***** 查 *****/
public E getFirst(){
if(isEmpty()){
throw new NoSuchElementException();
}
return head.next.val;
}
public E getLast(){
if(isEmpty()){
throw new NoSuchElementException();
}
return tail.prev.val;
}
public E get(int index){
checkElementIndex(index);
Node p = getNode(index);
return p.val;
}
9、改:①public E set(int index,E val);
/***** 改 *****/
public E set(int index,E element){
checkElementIndex(index);
Node p = getNode(index);
E oldVal = p.val;
p.val = element;
return oldVal;
}
10、大小,是否为空
public int size(){
return size;
}
public boolean isEmpty(){
return size==0;
}
总结:①注意 size 在add或remove 后记得++或--;
②能先判断边界问题先判断,最后写方法
package pgs0204.mylinkedlist;
import java.util.NoSuchElementException;
public class MyLinkedList_tow {
//双链表节点
private static class Node{
E val;
Node next;
Node prev;
Node(E val){
this.val = val;
}
}
private final Nodehead,tail;
private int size;
//构造函数初始化头尾节点
public MyLinkedList_tow(){
this.head = new Node<>(null);
this.tail = new Node<>(null);
head.next = tail;
tail.prev = head;
this.size = 0;
}
/***** 增 *****/
public void addFirst(E e){
Node x = new Node<>(e);
Node temp = head.next;
//head temp
x.next = temp;
x.prev = head;
head.next = x;
temp.prev = x;
size++;
}
public void addLast(E e){
Node x = new Node<>(e);
Node temp = tail.prev;
//temp tail
x.prev = temp;
x.next = tail;
temp.next = x;
tail.prev = x;
size++;
}
public void add(int index,E element){
checkPositionIndex(index);
Node p = getNode(index);
Node temp = p.prev;
Node x = new Node<>(element);//新节点
//temp p
x.next = p;
x.prev = temp;
temp.next = x;
p.prev = x;
size++;
}
/***** 删 *****/
public E removeFirst(E e){
if(isEmpty()){
throw new NoSuchElementException();
}
Node x = head.next;
Node temp = x.next;
head.next = temp;
temp.prev = head;
x.next = x.prev = null;
size--;
return x.val;
}
public E removeLast(E e){
if(isEmpty()){
throw new NoSuchElementException();
}
Node x = tail.prev;
Node temp = x.prev;
tail.prev = temp;
temp.next = tail;
x.next = x.prev = null;
size--;
return x.val;
}
public E remove(int index){
checkElementIndex(index);
Node p = getNode(index);
Node perv = p.prev;
Node next = p.next;
perv.next = next;
next.prev = perv;
p.prev = p.next = null;
size--;
return p.val;
}
/***** 查 *****/
public E getFirst(){
if(isEmpty()){
throw new NoSuchElementException();
}
return head.next.val;
}
public E getLast(){
if(isEmpty()){
throw new NoSuchElementException();
}
return tail.prev.val;
}
public E get(int index){
checkElementIndex(index);
Node p = getNode(index);
return p.val;
}
/***** 改 *****/
public E set(int index,E element){
checkElementIndex(index);
Node p = getNode(index);
E oldVal = p.val;
p.val = element;
return oldVal;
}
/***** 其他工具函数 *****/
public int size(){
return size;
}
public boolean isEmpty(){
return size==0;
}
private boolean isElementIndex(int index){
return index>=0 && index= 0 && index <= size;
}
/** 检查 index 索引位置是否可以存在元素 */
private void checkElementIndex(int index){
if(!isElementIndex(index)){
throw new IndexOutOfBoundsException();
}
}
/** 检查 index 索引位置是否可以添加元素 **/
private void checkPositionIndex(int index){
if(!isPositionIndex(index)){
throw new IndexOutOfBoundsException();
}
}
private Node getNode(int index){
Node p = head.next;
for (int i = 0; i < index; i++) {
p = p.next;
}
return p;
}
}