完整代码向下拉java
链表是一种经常使用的数据结构,在插入和移除操做中有着优秀的表现,同为数据结构的数组哭晕,其实数组的访问效率比链表高多了有木有。数组
咱们先看一下链表的样子数据结构
有同窗可能要说了,这不就是咱们生活中的交通工具——火车,没错链表的结构和下图简直就是一个模子刻出来的。(咳咳,忽略这灵魂的画法)工具
经过火车示意图能够观察到,火车由火车头和n节车箱组成,每节车箱都与下一节车箱相连,能理解这句话,链表你就掌握一半了。性能
以小学掌握的物品分类知识来对上图进行面向对象抽象,火车总体能够认为是链表,火车又由车箱组成的,一样能够理解链表是由节点组成的,链表起到了组合节点的做用。this
车箱都有哪些特色呢?车箱能存放物品,车箱与下一节车箱相连。spa
public class Node {
public Object data;//存放的数据
public Node next;//指向下一个节点
public Node(Object value) {
this.data = value;
}
//展现节点数据
public void display() {
System.out.print(data+" ");
}
}
复制代码
在现实中咱们要想查看整个火车由一个十分暴力的方法,那就是找到火车头,找到火车头后沿着每节车箱向后查找就能够完整的查看整辆火车。code
public class LinkList {
private Node first;//第一个节点
}
复制代码
在代码中 Node 类已经声明了指向下一个节点的属性,因此只须要找到第一个节点,调用next属性便可无限向后查找。cdn
第一个节点为空即为链表为空对象
public boolean isEmpty() {
return first == null;
}
复制代码
添加数据到链表头部,就是将新节点的next指向原来的首节点,再将新节点标记为first便可。
public void addFirst(Object value) {
Node newNode = new Node(value);//建立新节点
if (isEmpty()) {
first = newNode;//没有节点时直接标记为首节点
} else {
newNode.next = first;//新节点next指向旧的首节点
first = newNode;
}
}
复制代码
并不是真正意义上的移除,而是将first指向first.next的内存地址,而原来的first会被垃圾回收器回收。
public Node removeFirst() {
if (isEmpty()) {
return null;
}
Node tmp = first;
first = first.next;
return tmp;
}
复制代码
因为链表中的每一个节点都有变量指向下一个节点,全部可使用循环递进获取下一个节点实现遍历的效果。
public void display() {
Node current = first;//先从首节点开始
while (current != null) {
current.display();//节点不为空,则打印该节点信息
current = current.next;//获取当前节点的下个节点从新放入current中
System.out.println();
}
}
复制代码
须要遍历节点,将每一个节点的值都与要查找的值进行比对,若是值不相等就一直循环,直到最后一个节点为空时表示没有查到。
public Node find(Object value) {
if (isEmpty()) {
return null;
}
Node current = first;
while (current.data != value) {
if (current.next==null){
return null;
}
current = current.next;
}
return current;
}
复制代码
移除时一样遍历全部节点,但要保存查到节点的以前节点(previous),若是查到的节点是第一个节点,直接移除第一个,不然就将前一个节点指向要移除节点的下一个。
public Node remove(Object value){
if (isEmpty()) {
return null;
}
Node current = first;
Node previous = first;
while (current.data != value) {
if (current.next==null){
return null;
}
previous = current;
current = current.next;
}
if (current==first){
removeFirst();
}else{
previous.next=current.next;
}
return current;
}
复制代码
public class LinkList {
private Node first;//第一个节点
public void addFirst(Object value) {
Node newNode = new Node(value);//建立新节点
if (isEmpty()) {
first = newNode;//没有节点时直接标记为首节点
} else {
newNode.next = first;//新节点next指向旧的首节点
first = newNode;
}
}
public Node removeFirst() {
if (isEmpty()) {
return null;
}
Node tmp = first;
first = first.next;
return tmp;
}
public void display() {
Node current = first;//先从首节点开始
while (current != null) {
current.display();//节点不为空,则打印该节点信息
current = current.next;//获取当前节点的下个节点从新放入current中
System.out.println();
}
}
public Node find(Object value) {
if (isEmpty()) {
return null;
}
Node current = first;
while (current.data != value) {
if (current.next==null){
return null;
}
current = current.next;
}
return current;
}
public Node remove(Object value){
if (isEmpty()) {
return null;
}
Node current = first;
Node previous = first;
while (current.data != value) {
if (current.next==null){
return null;
}
previous = current;
current = current.next;
}
if (current==first){
removeFirst();
}else{
previous.next=current.next;
}
return current;
}
public boolean isEmpty() {
return first == null;
}
public static void main(String[] args) {
LinkList linkList = new LinkList();
linkList.addFirst("a");
linkList.addFirst("b");
System.out.println("-0---");
linkList.remove("a");
linkList.display();
}
public class Node {
public Object data;//存放的数据
public Node next;//指向下一个节点
public Node(Object value) {
this.data = value;
}
//展现节点数据
public void display() {
System.out.print(data+" ");
}
}
}
复制代码
单端链表仍是有一些不足,好比咱们要操做最后一个节点须要遍历整个链表,下一节我们实现双端链表,提升操做最后一个节点的效率。
上节文章中我们了解了链表的结构及原理,可是还有些美中不足的地方,就是没法快速的访问链表中的最后一个节点。 在这节文章中我们来解决这个问题,一块儿来吧。
首先先看如何快速访问尾节点,其实这个能够经过一个变量指向尾节点,在作插入、删除时更新尾节点便可。
节点用于存储数据和下一个节点相连
public class Node {
public Object data;//存放的数据
public Node next;//指向下一个节点
public Node(Object value) {
this.data = value;
}
//展现节点数据
public void display() {
System.out.print(data + " ");
}
}
复制代码
操做节点和指向首尾两个节点
public class DoubleEndLinkList {
private Node first;//第一个节点
private Node last;//最后一个节点
}
复制代码
若是加入的节点是第一个节点,这个节点是首节点同时也是尾节点。若是已经有节点则让新节点指向原来的首节点,让首节点指向新节点。
public void addFirst(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
//若是为空,尾节点指向此节点
last = newNode;
}
//更替新节点
newNode.next = first;
first = newNode;
}
复制代码
和向前添加相反,由于链表中last始终指向最后一个节点,因此操做last节点。
public void addLast(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
//若是为空,首节点和尾节点指向此节点
first = newNode;
last = newNode;
return;
}
//更替尾节点
last.next = newNode;
last = newNode;
}
复制代码
移除首节点时将首节点的下一个节点标记为首节点便可,直到首节点与尾节点相同时(这时也意味这只剩一个节点)再也不须要轮替,直接将首尾节点设置为null。
public Node removeFirst() {
if (isEmpty()) {
return null;
}
Node tmp = first;
//仅剩一个节点时
if (first == last) {
first = null;
last = null;
return tmp;
}
//不管有多少个节点都会轮替到first==last
first = first.next;
return tmp;
}
复制代码
package com.jikedaquan.datastruct;
public class DoubleEndLinkList {
private Node first;//第一个节点
private Node last;//最后一个节点
public void addFirst(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
//若是为空,尾节点指向此节点
last = newNode;
}
//更替新节点
newNode.next = first;
first = newNode;
}
public void addLast(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
//若是为空,首节点和尾节点指向此节点
first = newNode;
last = newNode;
return;
}
//更替尾节点
last.next = newNode;
last = newNode;
}
public Node removeFirst() {
if (isEmpty()) {
return null;
}
Node tmp = first;
//仅剩一个节点时
if (first == last) {
first = null;
last = null;
return tmp;
}
//不管有多少个节点都会轮替到first==last
first = first.next;
return tmp;
}
public void display() {
Node current = first;//先从首节点开始
while (current != null) {
current.display();//节点不为空,则打印该节点信息
current = current.next;//获取当前节点的下个节点从新放入current中
System.out.println();
}
}
public boolean isEmpty() {
return first == null;
}
public static void main(String[] args) {
DoubleEndLinkList linkList = new DoubleEndLinkList();
linkList.addLast("e");
linkList.addFirst("a");
linkList.addFirst("b");
linkList.removeFirst();
linkList.removeFirst();
linkList.removeFirst();
linkList.display();
System.out.println("-0---");
linkList.addLast("c");
linkList.addFirst("1");
linkList.display();
System.out.println("-0---");
linkList.removeFirst();
linkList.removeFirst();
linkList.addLast(9);
linkList.display();
System.out.println("-0---");
linkList.display();
}
public class Node {
public Object data;//存放的数据
public Node next;//指向下一个节点
public Node(Object value) {
this.data = value;
}
//展现节点数据
public void display() {
System.out.print(data + " ");
}
}
}
复制代码
双端链表能同时向首尾添加新元素,用双端链表实现队列也成为了可能(比用数组实现效率更高),可是如何快速移除最后一个元素(由于不能快速的追溯以前的元素)成为了一个新难题,请看下一节 双向链表!
单向链表每一个节点指向下一个节点,而双向链表是指每一个节点还指向了前一个节点。这样作的好处是能够快速的移除最后一个元素。
节点除了指向下一个节点还要指向前一个节点
public class Node {
public Object data;//存放的数据
public Node previous;//指向前一个节点
public Node next;//指向下一个节点
public Node(Object value) {
this.data = value;
}
//展现节点数据
public void display() {
System.out.print(data + " ");
}
}
复制代码
public class TwoWayLinkList {
private Node first;
private Node last;
}
复制代码
考虑到链表为空时,首元素和尾元素均指向新元素。
public void addFirst(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
last = newNode;
first = newNode;
return;
}
//新首元素指向旧首元素
newNode.next = first;
//旧首元素的前一个指向新首元素
first.previous = newNode;
//更替首元素
first = newNode;
}
复制代码
因为是双向的,因此以前的引用和以后的引用都要更新
public void addLast(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
first = newNode;
last = newNode;
return;
}
//更替尾元素
newNode.previous = last;
last.next = newNode;
last = newNode;
}
复制代码
若是过已经是最后一个元素,直接将首尾设置为null,其余状况进行轮替。
public Node removeFirst() {
Node removeNode = first;
//若是当前已经是最后一个元素
if (first.next == null) {
first = null;
last = null;
return removeNode;
}
//首元素指向下一个元素
first = first.next;
//将新首元素指向的以前的元素设为null
first.previous = null;
return removeNode;
}
复制代码
和移除首元素相似
public Node removeLast() {
Node removeNode = last;
if (last.previous == null) {
first = null;
last = null;
return null;
}
//尾元素指向旧尾元素以前的元素
last = last.previous;
//将新尾元素的下一个元素设为null
last.next = null;
return removeNode;
}
复制代码
从第一个元素开始遍历,若是值不相同就一直遍历,没有元素匹配返回null,有元素相同时将以前的元素指向当前元素的下一个,将当前元素下一个指向前一个。
public Node remove(Object value) {
if (isEmpty()){
return null;
}
Node current = first;
while (current.data != value) {
if (current.next == null) {
return null;
}
current = current.next;
}
current.previous.next = current.next;
current.next.previous = current.previous;
return current;
}
复制代码
package com.jikedaquan.datastruct;
public class TwoWayLinkList {
private Node first;
private Node last;
public void addFirst(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
last = newNode;
first = newNode;
return;
}
//新首元素指向旧首元素
newNode.next = first;
//旧首元素的前一个指向新首元素
first.previous = newNode;
//更替首元素
first = newNode;
}
public void addLast(Object value) {
Node newNode = new Node(value);
if (isEmpty()) {
first = newNode;
last = newNode;
return;
}
//更替尾元素
newNode.previous = last;
last.next = newNode;
last = newNode;
}
public Node removeFirst() {
Node removeNode = first;
//若是当前已经是最后一个元素
if (first.next == null) {
first = null;
last = null;
return removeNode;
}
//首元素指向下一个元素
first = first.next;
//将新首元素指向的以前的元素设为null
first.previous = null;
return removeNode;
}
public Node removeLast() {
Node removeNode = last;
if (last.previous == null) {
first = null;
last = null;
return null;
}
//尾元素指向旧尾元素以前的元素
last = last.previous;
//将新尾元素的下一个元素设为null
last.next = null;
return removeNode;
}
public Node remove(Object value) {
if (isEmpty()){
return null;
}
Node current = first;
while (current.data != value) {
if (current.next == null) {
return null;
}
current = current.next;
}
current.previous.next = current.next;
current.next.previous = current.previous;
return current;
}
public boolean isEmpty() {
return first == null;
}
public void display() {
Node current = first;//先从首节点开始
while (current != null) {
current.display();//节点不为空,则打印该节点信息
current = current.next;//获取当前节点的下个节点从新放入current中
}
System.out.println();
}
public static void main(String[] args) {
TwoWayLinkList linkList = new TwoWayLinkList();
linkList.addFirst("b");
linkList.addFirst("a");
linkList.display();
System.out.println("======");
while (!linkList.isEmpty()) {
linkList.removeFirst();
linkList.display();
}
System.out.println("======");
linkList.addLast("c");
linkList.addLast("d");
linkList.display();
System.out.println("======");
while (!linkList.isEmpty()) {
linkList.removeLast();
linkList.display();
}
System.out.println("======");
linkList.addFirst("e");
linkList.addLast("f");
linkList.addLast("g");
linkList.display();
System.out.println("======");
linkList.remove("f");
System.out.println("======");
linkList.display();
}
public class Node {
public Object data;//存放的数据
public Node previous;//指向前一个节点
public Node next;//指向下一个节点
public Node(Object value) {
this.data = value;
}
//展现节点数据
public void display() {
System.out.print(data + " ");
}
}
}
复制代码
链表是节点中经过变量指向前一个节点和下一个节点实现了相连,链表在移除节点、首尾添加节点有着优越的性能,时间复杂度O(1)。数组在作同等操做须要O(n),但在访问元素上优于链表,空间复杂度也小于链表,应在不一样的场景选用不一样的结构。 固然这些数据结构也不须要咱们去实现的,java语言中已经有对应的实现。