数据分散的存储在物理空间中,通过一根线保存着它们之间的逻辑关系,这种存储结构称为链式存储结构
实际上,就是每一个结点存放一个元素和一个指向下一个结点的引用(C语言里面是指针,Java中就是对象的引用,代表下一个结点对象)

利用这种思想,我们再来尝试实现上面的抽象类,从实际的代码中感受!
比较:顺序表和链表的优异?
顺序表优缺点:
- 访问速度快,随机访问性能高
- 插入和删除的效率低下,极端情况下需要变更整个表
- 不易扩充,需要复制并重新创建数组
链表优缺点:
- 插入和删除效率高,只需要改变连接点的指向即可
- 动态扩充容量,无需担心容量问题
- 访问元素需要依次寻找,随机访问元素效率低下
链表只能指向后面,能不能指向前面呢?双向链表!
栈和队列实际上就是对线性表加以约束的一种数据结构,如果前面的线性表的掌握已经ok,那么栈和队列就非常轻松了!
手动实现一个int链表
package com.linkedlist;public class Node {private int data;// 结点数据private Node next;// 下一个结点public Node(int data) {this.data = data;}public int getData() {return data;}public void setData(int data) {this.data = data;}public Node getNext() {return next;}public void setNext(Node next) {this.next = next;}}
package com.linkedlist;import java.util.Hashtable;public class LinkedListOperator {private Node head = null;// 头结点// 在链表的末尾增加一个结点private void addNode(int data) {Node newNode = new Node(data);if (head == null) {head = newNode;return;}Node temp = head;while (temp.getNext() != null) {temp = temp.getNext();}temp.setNext(newNode);}// 打印链表结点private void printLink() {Node curNode = head;while (curNode != null) {System.out.println(curNode.getData());curNode = curNode.getNext();}System.out.println("===========");}// 求链表长度private int getLength() {int len = 0;Node curNode = head;while (curNode != null) {len++;curNode = curNode.getNext();}return len;}// 删除某一个结点private boolean delNode(int index) {if (index < 1) {return false;}if (index == 1) {head = head.getNext();return true;}Node preNode = head;Node curNode = head.getNext();int n = 1;while (curNode.getNext() != null) {if (n == index) {preNode.setData(curNode.getData());preNode.setNext(curNode.getNext());return true;}preNode = preNode.getNext();curNode = curNode.getNext();n++;}if (curNode.getNext() == null) {preNode.setNext(null);}return false;}// 链表排序:选择排序法,从小到大private void sortList() {Node curNode = head;while (curNode != null) {Node nextNode = curNode.getNext();while (nextNode != null) {if (curNode.getData() > nextNode.getData()) {int temp = curNode.getData();curNode.setData(nextNode.getData());nextNode.setData(temp);}nextNode = nextNode.getNext();}curNode = curNode.getNext();}}// 去掉重复元素private void distinctLink() {Hashtable<Integer, Integer> map = new Hashtable<Integer, Integer>();Node curNode = head;Node preNode = null;while (curNode != null) {if (map.containsKey(curNode.getData())) {preNode.setData(curNode.getData());preNode.setNext(curNode.getNext());} else {map.put(curNode.getData(), 1);preNode = curNode;}curNode = curNode.getNext();}}// 返回倒数第k个结点,定义两个指针,第一个指针向前移动K-1次,之后两个指针同时前进,// 当第一个指针到达末尾时,第二个指针所在的位置即为倒数第k个结点private Node getReverNode(int k) {if (k < 1) {return null;}Node first = head;Node second = head;for (int i = 0; i < k - 1; i++) {first = first.getNext();}while (first.getNext() != null) {first = first.getNext();second = second.getNext();}return second;}// 反转链表private void reserveLink() {Node preNode = null;Node curNode = head;Node tempNode = null;while (curNode != null) {tempNode = curNode.getNext();curNode.setNext(preNode);preNode = curNode;curNode = tempNode;}head = preNode;}// 寻找链表的中间结点private Node getMiddleNode() {Node slowNode = head;Node quickNode = head;while (slowNode.getNext() != null && quickNode.getNext() != null) {slowNode = slowNode.getNext();quickNode = quickNode.getNext().getNext();}return slowNode;}// 判断链表是否有环private boolean isRinged() {if (head == null) {return false;}Node slowNode = head;Node quickNode = head;while (slowNode.getNext() != null && quickNode.getNext() != null) {slowNode = slowNode.getNext();quickNode = quickNode.getNext().getNext();if (slowNode.getData() == quickNode.getData()) {return true;}}return false;}// 删除指定结点private boolean delNode(Node node) {if (node.getNext() == null) {return false;// 在不知道头结点的情况下,没法删除单链表的尾结点}node.setData(node.getNext().getData());node.setNext(node.getNext().getNext());return true;}// 判断两个链表是否相交:相交的链表的尾结点相同private boolean isCross(Node n1, Node n2) {while (n1.getNext() != null) {n1 = n1.getNext();}while (n2.getNext() != null) {n2 = n2.getNext();}if (n1.getData() == n2.getData()) {return true;}return false;}// 求相交链表的起始点private Node getFirstCrossNode(LinkedListOperator l1, LinkedListOperator l2) {int len = l1.getLength() - l2.getLength();Node n1 = l1.head;Node n2 = l2.head;if (len > 0) {for (int i = 0; i < len; i++) {n1 = n1.getNext();}} else {for (int i = 0; i < len; i++) {n2 = n2.getNext();}}while (n1.getData() != n2.getData()) {n1 = n1.getNext();n2 = n2.getNext();}return n1;}public static void main(String[] args) {LinkedListOperator llo = new LinkedListOperator();llo.addNode(10);llo.addNode(4);llo.addNode(6);llo.addNode(8);llo.printLink();// llo.delNode(4);// llo.sortList();// llo.distinctLink();// System.out.println(llo.getReverNode(3).getData());// llo.reserveLink();// System.out.println(llo.getMiddleNode().getData());// System.out.println(llo.isRinged());llo.delNode(llo.head.getNext().getNext());llo.printLink();}}
