最近想刷leetcode,看了一些大神的经验,说可以从数据结构方面入手,选择一种数据数据然后开始刷这一数据结构的LeetCode中的题目,我的第一种数据结构为链表。以下是链表的常用方法和链表的应用,希望对大家有帮助。
链表的定义比较简单,因此就不进行赘述了,大家可以看看C语言版的数据结构或者王道的数据结构,介绍的都很清楚,下面主要看代码,其中一些应用方法也标注了简单的思路。
package structure_linkedList;
public class Node {
int value;
Node next = null;
public Node(int value) {
this.value = value;
}
}
package structure_linkedList;
/*
* 参考文档链接:
* https://www.cnblogs.com/llfy/p/9395936.html
* https://www.cnblogs.com/keeya/p/9218352.html
* https://blog.youkuaiyun.com/u010292561/article/details/80444057
*/
public class Link {
Node head = null;
// 链表基本方法(插入、删除、长度、打印)
// 插入值为val的节点(尾插法)
public void insertNode1(int val) {
Node newNode = new Node(val);
if (head == null) {
head = newNode;
return;
}
Node temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = newNode;
}
// 插入值为val的节点(头插法)
public void insertNode2(int val){
Node node = new Node(val);
if(head == null) {
head = node;
return;
}
node.next = head.next;
head.next = node;
}
// 删除第index个节点
public boolean deleteNode(int index) {
if (index < 1 || index > length())
return false;
if (index == 1) {
head = head.next;
return true;
}
Node preNode = head;
int i = 1;
while (i < index - 1) {
preNode = preNode.next;
}
preNode.next = preNode.next.next;
return true;
}
// 打印链表
public void printList() {
Node temp = head;
while (temp != null) {
System.out.print(temp.value + " ");
temp = temp.next;
}
System.out.println();
}
// 链表的长度
public int length() {
int len = 0;
Node temp = head;
while (temp != null) {
temp = temp.next;
len++;
}
return len;
}
// 常用方法:
// 1. 链表反转(遍历法)
// 思路:如果有三个结点,node为当前结点,pre为前一个结点,next为后一个
public Node reverse1(Node node) {
Node pre = null;
Node next = null;
while(node != null) {
// next为node的后一个结点
next = node.next;
// 反转,当前结点node的next为pre
node.next = pre;
// 让pre指向当前结点node(pre指向后移)
pre = node;
// 让node指向结点next(node指向后移)
node = next;
}
return pre;
}
// 2. 链表反转(递归法)
public Node reverse2(Node node) {
if(node == null || node.next == null) {
return node;
}
Node next = node.next;
Node newHead = reverse2(next);
next.next = node;
node.next = null;
return newHead;
}
// 3.逆序输出链表(递归法)
public void reversePrint(Node node) {
if (node != null) {
Node next = node.next;
reversePrint(next);
System.out.print(node.value+" ");
}
}
// 4.删除重复节点
public void deleteDuplecate(Node node) {
while (node != null) {
Node pre = node;
Node next = node.next;
while (next != null) {
if (node.value == next.value) {
pre.next = next.next;
}
next = next.next;
pre = pre.next;
}
node = node.next;
}
}
// 5.链表排序
// 思路:找到一个最小的与第一个节点交换(参考直接选择排序)
public void sortList(Node node) {
int temp;
while (node != null) {
Node next = node.next;
while (next != null) {
if (next.value < node.value) {
temp = next.value;
next.value = node.value;
node.value = temp;
}
next = next.next;
}
node = node.next;
}
}
// 6.查找单链表的中间节点(快慢指针,快指针每次走两步,慢指针每次走一步)
public Node searchMid(Node node) {
Node quick,slow;
quick = slow = node;
while(quick != null && quick.next != null) {
quick = quick.next.next;
slow = slow.next;
}
return slow;
}
// 7.查找倒数第k个元素
// 思路:从头节点开始,找到第k个节点,然后头节点和k节点同时移动,如果k节点到达尾节点,则头节点指向的尾倒数第k个节点
public Node findElem(Node head, int k) {
Node kNode;
int i = 1;
kNode = head;
while(i < k) {
kNode = kNode.next;
i++;
}
while(kNode.next != null) {
kNode = kNode.next;
head = head.next;
}
return head;
}
// 8.判断是否有环
// 思路:如果快慢指针相遇,则有环
public boolean isLoop(Node node) {
Node fast, slow;
fast = slow = node;
if(fast == null) {
return false;
}
while (fast != null || fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
// 9.求环的长度
// 思路:快慢指针相遇,慢指针再走一圈到达当前位置,走过的路径为长度
private int loopLength(Node node) {
Node fast, slow, meet = null;
fast = slow = node;
while (fast != null || fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
meet = slow;
break;
}
}
int count = 1;
slow = slow.next;
while(slow != meet) {
slow = slow.next;
count++;
}
return count;
}
// 10.求环的入口
// 思路:依据公式a = (n - 1) * r + (r - b),其中a为头节点到入口的距离,r为环的长度,b为入口到相遇节点之间的距离,n为快指针在环内走的圈数;
// 推导过程:
// (1)依据快指针走的路程为慢指针走的路径的2倍可以得到 公式一: 2(a + b) = n * r + a + b
// (2)化简后得到公式二:a = (n - 1) * r + (r - b),此公式的含义为一个节点A从头节点出发,节点B从相遇点出发,当节点A和节点B相遇时,此节点为环的入口
private Node loopPort(Node node) {
Node fast, slow;
Node nodeA, nodeB = null;
fast = slow = nodeA = node;
while (fast != null || fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
nodeB = slow;
break;
}
}
while(nodeA != nodeB) {
nodeA = nodeA.next;
nodeB = nodeB.next;
}
return nodeA;
}
public static void main(String[] args) {
Link list = new Link();
System.out.println("the length of linkList:" + list.length());
// 尾插入5和3 5,3
list.insertNode1(5);
list.insertNode1(3);
list.printList();
// 反转列表 3,5
list.head = list.reverse2(list.head);
list.printList();
// 删除第一个结点3 5
list.deleteNode(1);
list.printList();
// 头插入7 7,5
list.insertNode2(7);
list.printList();
list.insertNode1(1);
list.insertNode1(2);
list.insertNode1(3);
list.insertNode1(4);
// 查找倒数第3个元素
System.out.println(list.findElem(list.head, 3).value);
list.printList();
// 链表排序
list.sortList(list.head);
// 逆序输出链表(递归法)
list.reversePrint(list.head);
list.printList();
// 删除重复数字
list.deleteDuplecate(list.head);
list.printList();
System.out.println("the length of linkList:" + list.length());
list.printList();
// 测试是否有环、环的长度和环的入口 1->2->3->4->5->6->3 环的长度为4,入口为3
/*Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
list.head = node1;
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = node3;
System.out.println(list.isLoop(list.head));
System.out.println(list.loopLength(list.head));
System.out.println(list.loopPort(list.head).value);*/
}
}
下一篇写二叉树