一、双向链表
双向链表也叫双向表,是链表的一种,它由多个节点组成,每个节点都由一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继节点,另一个指针用来指向前驱节点。链表的头节点的数据域不存储数据,指向前驱节点的指针域值为 null ,指向后继节点的指针域指向第一个真正存储数据的节点,如下图:
1.1、节点 API 设计
类名 |
Node<T> |
构造方法 |
Node(T t,Node next) :创建 Node 对象 |
成员变量 |
T item :存储数据 Node next :指向下一个节点 Node pre :指向上一个节点 |
1.2、双向链表 API 设计
类名 |
TwoWayLinkList<T> |
构造方法 |
TwoWayLinkList() :创建 TwoWayLinkList 对象 |
成员方法 |
1、public void clean() :清空线性表 2、public boolean isEmpty() :线性表是否为空 3、public int length() :获取线性表元素的个数 4、public T get(int i) :获取线性表中第 i 个位置元素的值 5、public void insert(int i,T t) :在第 i 个元素之前插入一个值为 t 的数据元素 6、public void insert(T t) :向线性表中添加一个元素 t 7、public T remove(int i) :删除并返回线性表中第 i 个位置的数据元素 8、public int indexOf(T t) :返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回 -1 9、public T getFirst() :获取第一个元素 10、public T getLast() :获取最后一个元素 |
成员内部类 |
private class Node<T> :节点类 |
成员变量 |
1、private Node first :记录头节点 2、private Node last :记录尾节点 3、private int N :记录链表的长度 |
1.3、双向链表代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| public class TwoWayLinkList<T>{
private Node first; private Node last; private int N;
private class Node{ private T item; private Node pre; private Node next;
public Node(T item,Node pre,Node next) { this.item = item; this.pre = pre; this.next = next; } }
public TwoWayLinkList() { first = new Node(null, null, null); last = null; N = 0; }
public void clear(){ first.next = null; last = null; N = 0; }
public boolean isEmpty(){ return N == 0; }
public int length(){ return N; }
public T getFirst(){ if(isEmpty()) return null; return first.next.item; }
public T getLast(){ if(isEmpty())return null; return last.item; }
public T get(int i){ Node temp = first.next; for (int j = 0; j < i; j++) { temp = temp.next; } return temp.item; }
public void insert(T t){ if(isEmpty()){ Node newNode = new Node(t, first, null); last = newNode; first.next = last; }else { Node oldLast = last; Node newNode = new Node(t, oldLast, null); oldLast.next = newNode; last = newNode; } N++; }
public void insert(int index,T t){ if(index >= N)return; Node pre = first; for (int i = 0; i < index; i++) { pre = pre.next; } Node current = pre.next; Node newNode = new Node(t, pre, current); pre.next = newNode; current.pre = newNode; N++; }
public int indexOf(T t){ Node temp = first; for (int i = 0;temp.next != null;i++){ temp = temp.next; if(temp.next.item.equals(t)){ return i; } } return -1; }
public T remove(int index){ if(index >= N) return null; Node pre = first; for (int i = 0; i < index; i++) { pre = pre.next; } Node current = pre.next; Node nextNode = current.next; pre.next = nextNode; nextNode.pre = pre; N--; return current.item; } }
|
1.4、双向链表遍历
我们也让 TwoWayLinkList 支持增强 for 循环:
1、让 TwoWayLinkList 实现 Iterable 接口,重写 iterator 接口
2、在 TwoWayLinkList 内部提供一个内部类 TIterator,实现 Iterator 接口,重写 hasNext() 方法和 next() 方法
具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
| public class TwoWayLinkList<T> implements Iterable<T>{
private Node first; private Node last; private int N;
private class Node{ private T item; private Node pre; private Node next;
public Node(T item,Node pre,Node next) { this.item = item; this.pre = pre; this.next = next; } }
public TwoWayLinkList() { first = new Node(null, null, null); last = null; N = 0; }
public void clear(){ first.next = null; last = null; N = 0; }
public boolean isEmpty(){ return N == 0; }
public int length(){ return N; }
public T getFirst(){ if(isEmpty()) return null; return first.next.item; }
public T getLast(){ if(isEmpty())return null; return last.item; }
public T get(int i){ Node temp = first.next; for (int j = 0; j < i; j++) { temp = temp.next; } return temp.item; }
public void insert(T t){ if(isEmpty()){ Node newNode = new Node(t, first, null); last = newNode; first.next = last; }else { Node oldLast = last; Node newNode = new Node(t, oldLast, null); oldLast.next = newNode; last = newNode; } N++; }
public void insert(int index,T t){ if(index >= N)return; Node pre = first; for (int i = 0; i < index; i++) { pre = pre.next; } Node current = pre.next; Node newNode = new Node(t, pre, current); pre.next = newNode; current.pre = newNode; N++; }
public int indexOf(T t){ Node temp = first; for (int i = 0;temp.next != null;i++){ temp = temp.next; if(temp.next.item.equals(t)){ return i; } } return -1; }
public T remove(int index){ if(index >= N) return null; Node pre = first; for (int i = 0; i < index; i++) { pre = pre.next; } Node current = pre.next; Node nextNode = current.next; pre.next = nextNode; nextNode.pre = pre; N--; return current.item; }
@NonNull @Override public Iterator<T> iterator() { return new TIterator(); }
private class TIterator implements Iterator<T>{
private Node temp;
public TIterator() { temp = first; }
@Override public boolean hasNext() { return temp.next != null; }
@Override public T next() { temp = temp.next; return temp.item; } } }
|
1.5、双向链表测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Test { public static void main(String[] args) { TwoWayLinkList<String> strList = new TwoWayLinkList<>(); strList.insert("姚明"); strList.insert("科比"); strList.insert("麦迪"); strList.insert(1,"詹姆斯"); for (String s : strList) { System.out.println(s); } System.out.println("获取头节点元素:" + strList.getFirst()); System.out.println("获取尾节点元素:" + strList.getLast()); String result = strList.get(1); System.out.println(result); String removeElement = strList.remove(0); System.out.println(removeElement); strList.clear(); System.out.println(strList.length()); } }
|
二、双向链表复杂度分析
2.1、get(i) 方法时间复杂度
get(i) 每一次查询,都要从链表的头开始,依次向后查询,随着数据元素 N 的增加,比较的元素也越多,时间复杂度为 O(n)
2.2、insert(int i,T t) 方法时间复杂度
insert(int i,T t) 每一次插入,需要先找到 i 位置的前一个元素,然后完成插入操作,随着数据元素 N 的增多,查找的元素越多,时间复杂度为 O(n)
2.3、remove(int i) 方法时间复杂度
remove(int i) 每一次删除,需要先找到 i 位置的前一个元素,然后完成移除操作,随着数据元素 N 的增多,查找的元素越多,时间复杂度为 O(n)
结合我们对单向链表的时间复杂度分析,可以发现双向链表和单向链表的时间复杂度是一样的
三、总结
本篇文章我们介绍了:
1、单向链表 API 设计,节点 API 设计,具体实现,以及添加了支持增强 for 循环,最后进行了测试用例测试
2、双向链表时间复杂度分析,结合前面讲的单向链表的时间复杂度,可发现,它们两时间复杂度是一样的
好了,本篇文章到这里就结束了,感谢你的阅读🤝