目录
- 1. 双向不带头非循环链表的介绍
- 2. 相关功能的实现
- 2.1 基本框架
- 2.2 size
- 2.3 addFirst
- 2.4 addLast
- 2.5 addIndex
- 2.6 contains
- 2.7 remove
- 2.8 removeAllKey
- 2.9 clear
- 3. 全部代码
前面我们学习了最简单的链表:单链表,今天我们学习双向不带头非循环链表,这也是Java的集合框架中LinkedList的底层实现
1. 双向不带头非循环链表的介绍
双向指的是:每个链表结点有三个域,一个是数据域,另外两个保存了前驱结点的地址和后继结点的地址,如图,第一个结点的前驱为null,最后一个结点的后继也为null
2. 相关功能的实现
2.1 基本框架
同样定义泛型类myLinkedList,每个结点定义为静态内部类ListNode,提供构造方法给val初始化,定义结点first表示链表的第一个结点,定义last表示链表的最后一个结点
public class myLinkedList<T> {//结点类static class ListNode {public ListNode prev;//前驱public Object val;//数据public ListNode next;//后继//构造方法public ListNode(Object val) {this.val = val;}}public ListNode first;//第一个结点public ListNode last;//最后一个结点//....其他方法
}
2.2 size
size的功能是获取链表长度,定义计数器size,遍历链表每个结点,每遍历一个结点size+1,直至遍历完链表,返回size
//得到单链表的长度public int size() {int size = 0;ListNode cur = first;while (cur != null) {cur = cur.next;size++;}return size;}
2.3 addFirst
addFirst意为头插,在链表的头部插入数据,如果链表没有数据,first等于null,则让first和last等于新的结点,然后return;如果链表已有数据,先让新的结点绑定链表,再让原来的头部的prev指向新的结点,接着更新first
//头插法public void addFirst(T val) {ListNode newNode = new ListNode(val);//如果没有元素if (first == null) {first = last = newNode;return;}newNode.next = first;first.prev = newNode;first = newNode;}
2.4 addLast
addLast表示尾插,在链表尾部插入数据,如果链表没有数据,让first和last等于新的结点,然后return;如果链表已有数据,让新的结点的prev指向链表的last,接着让last的next指向新的结点,然后更新last的值
//尾插法public void addLast(T val) {ListNode newNode = new ListNode(val);//如果没有元素if (last == null) {first = last = newNode;return;}newNode.prev = last;last.next = newNode;last = newNode;}
2.5 addIndex
addIndex表示在Index位置插入数据,插入前需要判断Index的合法性(Index不能小于0,不能大于链表的长度),定义IndexNotLegalException异常类,如果Index不合法就抛出这个异常
public class IndexNotLegalException extends RuntimeException {public IndexNotLegalException(String s) {super(s);}public IndexNotLegalException() {super();}
}
如果Index等于0,表示头插,如果Index等于链表长度,表示尾插,此时直接调用方法;在插入之前先找到Index位置的结点,接着先绑定后面的数据
//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, T val) {ListNode newNode = new ListNode(val);//index合法性检查try {if (index < 0 || index > size()) {throw new IndexNotLegalException("Index不合法");}} catch (IndexNotLegalException e) {e.printStackTrace();}//index=0 头插if (index == 0) {addFirst(val);return;}//index=size 尾插if (index == size()) {addLast(val);return;}//ListNode cur = first;for (int i = 0; i < index; i++) {cur = cur.next;}ListNode prev = cur.prev;//开始绑定newNode.next = cur;newNode.prev = prev; prev.next = newNode;cur.prev = newNode;}
2.6 contains
查找链表中是否包含某个数据(key),如果包含返回true否则,返回false
//查找是否包含关键字key是否在单链表当中public boolean contains(T key) {if (first == null) {return false;}ListNode cur = first;while (cur != null) {if (cur.val.equals(key)) {return true;}cur = cur.next;}return false;}
2.7 remove
删除链表中第一个值为key的结点,如果链表为空,直接返回。定义del来遍历链表,如果del的值与key相等,分情况讨论:
1.del为第一个结点,此时只需将first指向下一个结点即可
2.del为最后一个结点,此时将last指向前一个结点,然后将last的next置为空
3.一般情况:要删除的结点在中间,定义两个变量:prevNode,表示要删除的结点的前一个结点;nextNode,表示要删除的结点的后一个结点,让prevNode的next指向nextNode,让nextNode的prev指向prevNode,此时就跳过了要删除的结点,没人引用它也就意味着删除了它
//删除第一次出现关键字为key的结点public void remove(T key) {if (first == null) {return;}ListNode del = first;while (del != null) {if (del.val.equals(key)) {//删除ListNode preNode = del.prev;ListNode nextNode = del.next;//pre==null?if (pre == null) {//第一个是keyfirst = first.next;return;}if (next == null) {//最后一个是keylast = last.prev;last.next = null;return;}preNode.next = nextNode;nextNode.prev = preNode;return;}del = del.next;}}
2.8 removeAllKey
删除链表中所有值为key的结点,分情况讨论:
1.前面部分全是要删除的结点:first往后走,如果first为空了,说明整个链表的值都是key,这个时候直接return
2.后面部分全是要删除的结点:让last往前走
3.一般情况:情况1处理了链表前部分都是key的情况,情况2处理了链表后部分都是key的情况,此时的key只能出现在中间部分,删除的逻辑remove一样,只不过remove删除完一个之后return了,removeAllKey则是删除之后继续删除
//删除所有值为key的结点public void removeAllKey(T key) {if (first == null) {return;}//处理前面的keywhile (first.val.equals(key)) {first = first.next;if (first == null) {return;}}//处理后面的keywhile (last.val.equals(key)) {last = last.prev;last.next = null;}//ListNode del = first;while (del != null) {if (del.val.equals(key)) {//删除ListNode preNode = del.prev;ListNode nextNode = del.next;//preNode.next = next;nextNode.prev = preNode;}del = del.next;}}
2.9 clear
clear表示将链表置空,只需循环遍历链表,将每一个结点都置空
public void clear() {while (first != null) {ListNode next = first.next;first.prev = first.next = null;first = next;}last = null;}
3. 全部代码
//不带头 双向 非循环 链表
public class myLinkedList<T> {//结点类static class ListNode {public ListNode prev;//前驱public Object val;//数据public ListNode next;//后继//构造方法public ListNode(Object val) {this.val = val;}}public ListNode first;//第一个结点public ListNode last;//最后一个结点//头插法public void addFirst(T val) {ListNode newNode = new ListNode(val);//如果没有元素if (first == null) {first = last = newNode;return;}newNode.next = first;first.prev = newNode;first = newNode;}//尾插法public void addLast(T val) {ListNode newNode = new ListNode(val);//如果没有元素if (last == null) {first = last = newNode;return;}newNode.prev = last;last.next = newNode;last = newNode;}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, T val) {ListNode newNode = new ListNode(val);//index合法性检查try {if (index < 0 || index > size()) {throw new IndexNotLegalException("Index不合法");}} catch (IndexNotLegalException e) {e.printStackTrace();}//index=0 头插if (index == 0) {addFirst(val);return;}//index=size 尾插if (index == size()) {addLast(val);return;}//ListNode cur = first;for (int i = 0; i < index; i++) {cur = cur.next;}ListNode prev = cur.prev;//开始绑定newNode.next = cur;newNode.prev = prev;prev.next = newNode;cur.prev = newNode;}//查找是否包含关键字key是否在单链表当中public boolean contains(T key) {if (first == null) {return false;}ListNode cur = first;while (cur != null) {if (cur.val.equals(key)) {return true;}cur = cur.next;}return false;}//删除第一次出现关键字为key的节点public void remove(T key) {if (first == null) {return;}//ListNode del = first;while (del != null) {if (del.val.equals(key)) {//删除ListNode pre = del.prev;ListNode next = del.next;//pre==null?if (pre == null) {//第一个是keyfirst = first.next;return;}if (next == null) {//最后一个是keylast = last.prev;last.next = null;return;}pre.next = next;next.prev = pre;return;}del = del.next;}}//删除所有值为key的节点public void removeAllKey(T key) {if (first == null) {return;}//处理前面的keywhile (first.val.equals(key)) {first = first.next;if (first == null) {return;}}while (last.val.equals(key)) {last = last.prev;last.next = null;}//ListNode del = first;while (del != null) {if (del.val.equals(key)) {//删除ListNode pre = del.prev;ListNode next = del.next;//pre.next = next;next.prev = pre;}del = del.next;}}//得到单链表的长度public int size() {int size = 0;ListNode cur = first;while (cur != null) {cur = cur.next;size++;}return size;}public void clear() {while (first != null) {ListNode next = first.next;first.prev = first.next = null;first = next;}last = null;}
}
今天的内容就到这里,感谢老铁们的点赞、收藏、评论~❤