Java实现单链表
一、链表
1、什么是链表?
链表是离散存储线性结构。
n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一个后续节点,首节点没有前驱节点,尾节点没有后续节点。
每个链表都有一个头指针指向头节点(不是首节点),有个尾指针指向尾节点。只要知道一个链表的头节点,即可遍历该链表的所有节点。
如下图:添加一个节点
2、链表的优缺点
(1)优点:增删数据快,没有空间限制(某种程度上)。
(2)缺点:读取速度慢。
3、链表分类
(1)单向链表
一个节点指向下一个节点
(2)双向链表
一个节点有两个指针域
(3)循环链表
能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个结点指向第一个结点从而实现循环
注:
节点中指针域指向的就是一个节点!
4、Java实现单链表
(1)实现功能:
1、向链表中添加数据(末尾添加),遍历链表 2、查看当前链表中节点的个数 3、向链表中指定位置插入一个数据 4、向链表中指定位置删除一个数据 5、找到链表中倒数第K个节点 6、查询链表的中间节点(快慢节点) 7、反转链表
(2)代码实现
package linkedList; /** * 定义一个节点类,其包括数据域,指针域(即节点域) * */ class Node { private String data; // 数据域,用于保存节点的数据 private Node next; // 指针域,用于保存指针指向的下一个节点 public Node() { } public Node(String data) { this.data = data; } public Node(String data, Node next) { this.data = data; this.next = next; } public String getData() { return data; } public void setData(String data) { this.data = data; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } @Override public String toString() { return "Node [data=" + data + ", next=" + next + "]"; } } /** * 定义一个节点操作类,用于操作节点 */ class NodeUtil { private static int size = 1; // 用于保存链表的节点总长度 /** * 向单链表中的末尾增加节点 * * @param head * 头节点 * @param data * 节点数据 */ public static void addNode(Node head, String data) { Node newNode = new Node(data); // 定义一个新节点 Node tempNode = head; // 定义一个临时节点,用于存储头节点 // 遍历链表,直至链表末尾 while (tempNode.getNext() != null) { tempNode = tempNode.getNext(); } // 向链表末尾插入一个节点 tempNode.setNext(newNode); size++; } /** * 在单链表中的末尾删除节点 * * @param head * 头节点 */ public static void deleteLastNode(Node head) { Node tempNode = head; // 定义一个临时节点,用于存储头节点 // 遍历链表,直至链表末尾倒数第二位 while (tempNode.getNext().getNext() != null) { tempNode = tempNode.getNext(); } // 在链表末尾删除一个节点 tempNode.setNext(null); size--; } /** * 遍历输出整个链表 * * @param head * 头节点 */ public static void toPrintNode(Node head) { Node tempNode = head; System.out.print(tempNode.getData()); while (tempNode.getNext() != null) { tempNode = tempNode.getNext(); System.out.print(" --> " + tempNode.getData()); } System.out.println(); } /** * 返回单链表的节点总长度 */ public static int size() { return size; } /** * 在指定位置插入节点 * * @param head * 头节点 * @param index * 插入的位置 * @param data * 插入的数据 */ public static void insert(Node head, int index, String data) { // 判断插入节点的位置是否越界 if (index < 1 || index > size) { throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } // 若在末尾添加一个节点 if (index == size) { addNode(head, data); return; } // 不在末尾添加 Node tempNode = head; // 定义一个临时节点,用于存储头节点 Node newNode = new Node(data); // 定义一个新节点 int currentPoint = 0; // 保存当前节点指向的位置 // 循环遍历链表 while (tempNode.getNext() != null) { // 找到要插入的位置 if (currentPoint == (index - 1)) { // 向指定位置插入某节点,比如 A->B中插入 C, 即 A->C->B,此时,先让C指向B,再让A指向C newNode.setNext(tempNode.getNext()); tempNode.setNext(newNode); size++; return; } // 未匹配到位置,当前位置向后移 currentPoint++; tempNode = tempNode.getNext(); } } /** * 删除指定位置的节点 * * @param head * 头节点 * @param index * 删除的位置 */ public static void delete(Node head, int index) { // 判断删除节点的位置是否越界 if (index < 1 || index > size) { throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } // 若删除的是最后一个 if (index == size) { deleteLastNode(head); return; } // 若删除的不是最后一个 Node tempNode = head; // 定义一个临时节点,用于存储头节点 int currentPoint = 0; // 保存当前节点指向的位置 // 循环遍历链表 while (tempNode.getNext() != null) { // 找到要删除的位置 if (currentPoint == (index - 1)) { // 在指定位置删除某节点,比如 A->C->B 中删除 C,此时,直接让A指向B,即A->B即可。 Node deleteNode = tempNode.getNext(); tempNode.setNext(deleteNode.getNext()); deleteNode.setNext(null); size--; return; } // 未匹配到位置,当前位置向后移 currentPoint++; tempNode = tempNode.getNext(); } } /** * 找到链表中倒数第K个节点。 * * 通过两个节点,一个节点A比另一个节点B始终快k个节点,A,B同时向后遍历,当A遍历完成后,B遍历的位置即为倒数第K个节点 * * @param head * 头节点 * @param k * 查询的位置 * @return 倒数第K个节点 */ public static Node fingKNode(Node head, int k) { // 判断删除节点的位置是否越界 if (k < 1 || k > size) { throw new IndexOutOfBoundsException("Index: " + k + ", Size: " + size); } Node nodeA = head;// 保存当前节点 Node nodeB = head;// 保存提前k个节点的节点 for (int i = 0; i < k - 1; i++) { nodeB = nodeB.getNext(); // 定位到第K个节点的位置 } // 遍历完成后,返回的即为倒数第K个节点 while (nodeB.getNext() != null) { nodeA = nodeA.getNext(); nodeB = nodeB.getNext(); } return nodeA; } /** * 查询链表的中间节点。 * * 通过两个节点,一个节点A比另一个节点B始终快1个节点,A,B同时向后遍历,当A遍历完成后,B遍历的位置即为中间节点 * * @param head * 头节点 * @return 中间节点 */ public static Node findMiddleNode(Node head) { Node nodeA = head; // 保存A节点 Node nodeB = head; // 保存B节点 // 循环遍历A节点,A节点每次都比B节点快一个节点(每次多走一个节点),所以当A遍历完成后,B节点所处位置即为中间节点。 while (nodeA.getNext() != null && nodeA.getNext().getNext() != null) { nodeA = nodeA.getNext().getNext(); nodeB = nodeB.getNext(); } return nodeB; } /** * 反转链表 * * 把每个节点的指针域由原来指向下一个节点变为指向其前一个节点。但由于单链表没有指向前一个节点的指针域, * 因此我们需要增加一个指向前一个节点的指针beforeNode,用于存储每一个节点的前一个节点。此外, * 还需要定义一个保存当前节点的指针currentNode, * 以及下一个节点的afterNode。定义好这三个指针后,遍历单链表,将当前节点的指针域指向前一个节点,之后将定义三个指针往后移动, * 直至遍历到最后一个节点停止(最后一个节点作为新的头节点)。 * * @param head * 头节点 */ public static Node reserveLinkedList(Node head) { // 单链表为空或只有一个节点,直接返回原单链表 if (head == null || head.getNext() == null) { return head; } // 前一个节点指针 Node beforeNode = null; // 当前指针 Node currentNode = head; // 下一个指针 Node afterNode = null; // 遍历当前指针,遍历结束,则表示最后一个将转为头节点 while (currentNode.getNext() != null) { afterNode = currentNode.getNext(); // 获取下一个节点 currentNode.setNext(beforeNode); // 将当前节点指向上一个节点 // 将节点后移一个节点 beforeNode = currentNode; currentNode = afterNode; } currentNode.setNext(beforeNode); return currentNode; } } /** * 测试单链表功能, * * 1、向链表中添加数据(末尾添加),遍历链表 * * 2、查看当前链表中节点的个数 * * 3、向链表中指定位置插入一个数据 * * 4、向链表中指定位置删除一个数据 * * 5、找到链表中倒数第K个节点 * * 6、查询链表的中间节点(快慢节点) * * 7、反转链表 */ public class SingleLinkedListDemo { public static void main(String[] args) { Node head = new Node("start"); NodeUtil.addNode(head, "node1"); NodeUtil.addNode(head, "node2"); NodeUtil.addNode(head, "node3"); NodeUtil.addNode(head, "end"); System.out.println("当前链表为:"); NodeUtil.toPrintNode(head); System.out.println(); System.out.println("当前链表长度为:"); System.out.println(NodeUtil.size()); NodeUtil.insert(head, 1, "node4"); System.out.println(); System.out.println("向第一个位置插入一个数据后,当前链表为:"); NodeUtil.toPrintNode(head); System.out.println("当前链表长度为:"); System.out.println(NodeUtil.size()); NodeUtil.delete(head, 5); System.out.println(); System.out.println("删除最后一个位置的数据,当前链表为:"); NodeUtil.toPrintNode(head); System.out.println("当前链表长度为:"); System.out.println(NodeUtil.size()); System.out.println(); System.out.println("找到链表中倒数第2个节点,节点为:"); System.out.println(NodeUtil.fingKNode(head, 2).getData()); NodeUtil.toPrintNode(head); System.out.println("当前链表长度为:"); System.out.println(NodeUtil.size()); System.out.println(); System.out.println("找到链表中间节点,节点为:"); System.out.println(NodeUtil.findMiddleNode(head).getData()); NodeUtil.toPrintNode(head); System.out.println("当前链表长度为:"); System.out.println(NodeUtil.size()); System.out.println(); System.out.println("反转链表,当前链表为:"); NodeUtil.toPrintNode(NodeUtil.reserveLinkedList(head)); System.out.println("当前链表长度为:"); System.out.println(NodeUtil.size()); } }
(3)输出结果
当前链表为: start --> node1 --> node2 --> node3 --> end 当前链表长度为: 5 向第一个位置插入一个数据后,当前链表为: start --> node4 --> node1 --> node2 --> node3 --> end 当前链表长度为: 6 删除最后一个位置的数据,当前链表为: start --> node4 --> node1 --> node2 --> node3 当前链表长度为: 5 找到链表中倒数第2个节点,节点为: node2 start --> node4 --> node1 --> node2 --> node3 当前链表长度为: 5 找到链表中间节点,节点为: node1 start --> node4 --> node1 --> node2 --> node3 当前链表长度为: 5 反转链表,当前链表为: node3 --> node2 --> node1 --> node4 --> start 当前链表长度为: 5
别把自己太当回事,也别太把自己不当回事!Life is Fantastic!!!