1实现单链表的基本操作(插入、删除、查找、打印)。
好的,下面是一个用Java语言实现单链表基本操作(插入、删除、查找、打印)的示例代码:
class Node { int data; Node next; public Node(int data) { this.data = data; this.next = null; } } class SinglyLinkedList { private Node head; // 插入节点到链表末尾 public void insert(int data) { Node newNode = new Node(data); if (head == null) { head = newNode; } else { Node current = head; while (current.next != null) { current = current.next; } current.next = newNode; } } // 删除指定值的节点 public void delete(int data) { if (head == null) return; if (head.data == data) { head = head.next; return; } Node current = head; while (current.next != null && current.next.data != data) { current = current.next; } if (current.next != null) { current.next = current.next.next; } } // 查找指定值的节点 public boolean search(int data) { Node current = head; while (current != null) { if (current.data == data) { return true; } current = current.next; } return false; } // 打印链表中的所有节点 public void printList() { Node current = head; while (current != null) { System.out.print(current.data + " -> "); current = current.next; } System.out.println("null"); } public static void main(String[] args) { SinglyLinkedList list = new SinglyLinkedList(); list.insert(1); list.insert(2); list.insert(3); list.printList(); // 输出: 1 -> 2 -> 3 -> null list.delete(2); list.printList(); // 输出: 1 -> 3 -> null System.out.println(list.search(3)); // 输出: true System.out.println(list.search(4)); // 输出: false } }
- Node类:表示链表中的一个节点,包含数据和指向下一个节点的引用。
- SinglyLinkedList类:实现了单链表的基本操作。
insert(int data):在链表末尾插入一个新节点。delete(int data):删除链表中第一个值为data的节点。search(int data):查找链表中是否存在值为data的节点。printList():打印链表中的所有节点。
- main方法:测试上述功能。
你可以根据需要扩展这些基本操作,例如在特定位置插入或删除节点等。
2实现双向链表的基本操作(插入、删除、查找、打印)。
好的,下面是一个用Java语言实现双向链表基本操作(插入、删除、查找、打印)的示例代码:
class DoublyNode { int data; DoublyNode next; DoublyNode prev; public DoublyNode(int data) { this.data = data; this.next = null; this.prev = null; } } class DoublyLinkedList { private DoublyNode head; // 在链表末尾插入节点 public void insert(int data) { DoublyNode newNode = new DoublyNode(data); if (head == null) { head = newNode; } else { DoublyNode current = head; while (current.next != null) { current = current.next; } current.next = newNode; newNode.prev = current; } } // 删除指定值的节点 public void delete(int data) { if (head == null) return; DoublyNode current = head; while (current != null && current.data != data) { current = current.next; } if (current == null) return; // 没有找到要删除的节点 if (current.prev != null) { current.prev.next = current.next; } else { head = current.next; // 如果要删除的是头节点 } if (current.next != null) { current.next.prev = current.prev; } } // 查找指定值的节点 public boolean search(int data) { DoublyNode current = head; while (current != null) { if (current.data == data) { return true; } current = current.next; } return false; } // 打印链表中的所有节点 public void printList() { DoublyNode current = head; while (current != null) { System.out.print(current.data + " <-> "); current = current.next; } System.out.println("null"); } public static void main(String[] args) { DoublyLinkedList list = new DoublyLinkedList(); list.insert(1); list.insert(2); list.insert(3); list.printList(); // 输出: 1 <-> 2 <-> 3 <-> null list.delete(2); list.printList(); // 输出: 1 <-> 3 <-> null System.out.println(list.search(3)); // 输出: true System.out.println(list.search(4)); // 输出: false } }
- DoublyNode类:表示双向链表中的一个节点,包含数据和指向前一个节点及下一个节点的引用。
- DoublyLinkedList类:实现了双向链表的基本操作。
insert(int data):在链表末尾插入一个新节点。delete(int data):删除链表中第一个值为data的节点。search(int data):查找链表中是否存在值为data的节点。printList():打印链表中的所有节点。
- main方法:测试上述功能。
你可以根据需要扩展这些基本操作,例如在特定位置插入或删除节点等。
3反转单链表。
反转单链表是一个常见的操作,可以通过迭代或递归的方法来实现。下面分别给出这两种方法的实现:
3.1方法一:迭代法
class Node { int data; Node next; public Node(int data) { this.data = data; this.next = null; } } class SinglyLinkedList { private Node head; // 插入节点到链表末尾 public void insert(int data) { Node newNode = new Node(data); if (head == null) { head = newNode; } else { Node current = head; while (current.next != null) { current = current.next; } current.next = newNode; } } // 打印链表中的所有节点 public void printList() { Node current = head; while (current != null) { System.out.print(current.data + " -> "); current = current.next; } System.out.println("null"); } // 反转链表 public void reverse() { Node prev = null; Node current = head; Node next = null; while (current != null) { next = current.next; // 保存下一个节点 current.next = prev; // 当前节点指向前一个节点 prev = current; // 前一个节点移动到当前节点 current = next; // 当前节点移动到下一个节点 } head = prev; // 更新头节点为最后一个节点 } public static void main(String[] args) { SinglyLinkedList list = new SinglyLinkedList(); list.insert(1); list.insert(2); list.insert(3); list.printList(); // 输出: 1 -> 2 -> 3 -> null list.reverse(); list.printList(); // 输出: 3 -> 2 -> 1 -> null } }
3.2方法二:递归法
class Node { int data; Node next; public Node(int data) { this.data = data; this.next = null; } } class SinglyLinkedList { private Node head; // 插入节点到链表末尾 public void insert(int data) { Node newNode = new Node(data); if (head == null) { head = newNode; } else { Node current = head; while (current.next != null) { current = current.next; } current.next = newNode; } } // 打印链表中的所有节点 public void printList() { Node current = head; while (current != null) { System.out.print(current.data + " -> "); current = current.next; } System.out.println("null"); } // 反转链表(递归) public void reverse() { head = reverseRecursive(head); } private Node reverseRecursive(Node node) { if (node == null || node.next == null) { return node; // 基本情况:如果节点为空或只有一个节点,直接返回该节点 } Node rest = reverseRecursive(node.next); // 反转剩余部分的链表 node.next.next = node; // 将当前节点的下一个节点的next指向当前节点 node.next = null; // 将当前节点的next置为空 return rest; // 返回新的头节点 } public static void main(String[] args) { SinglyLinkedList list = new SinglyLinkedList(); list.insert(1); list.insert(2); list.insert(3); list.printList(); // 输出: 1 -> 2 -> 3 -> null list.reverse(); list.printList(); // 输出: 3 -> 2 -> 1 -> null } }
3.3代码说明:
- Node类:表示单链表中的一个节点,包含数据和指向下一个节点的引用。
- SinglyLinkedList类:实现了单链表的基本操作,包括插入、打印和反转。
insert(int data):在链表末尾插入一个新节点。printList():打印链表中的所有节点。reverse():反转链表,使用迭代法或递归法实现。
- main方法:测试上述功能。
4反转双向链表。
反转双向链表与反转单链表类似,但需要处理两个方向的指针(
next 和 prev)。下面分别给出迭代法和递归法的实现:4.1方法一:迭代法
class DoublyNode { int data; DoublyNode next; DoublyNode prev; public DoublyNode(int data) { this.data = data; this.next = null; this.prev = null; } } class DoublyLinkedList { private DoublyNode head; // 插入节点到链表末尾 public void insert(int data) { DoublyNode newNode = new DoublyNode(data); if (head == null) { head = newNode; } else { DoublyNode current = head; while (current.next != null) { current = current.next; } current.next = newNode; newNode.prev = current; } } // 打印链表中的所有节点 public void printList() { DoublyNode current = head; while (current != null) { System.out.print(current.data + " <-> "); current = current.next; } System.out.println("null"); } // 反转链表 public void reverse() { DoublyNode current = head; DoublyNode temp = null; while (current != null) { temp = current.prev; // 保存当前节点的前一个节点 current.prev = current.next; // 将当前节点的prev指向下一个节点 current.next = temp; // 将当前节点的next指向前一个节点 head = current; // 更新头节点为当前节点 current = current.prev; // 移动到下一个节点 } } public static void main(String[] args) { DoublyLinkedList list = new DoublyLinkedList(); list.insert(1); list.insert(2); list.insert(3); list.printList(); // 输出: 1 <-> 2 <-> 3 <-> null list.reverse(); list.printList(); // 输出: 3 <-> 2 <-> 1 <-> null } }
4.2方法二:递归法
class DoublyNode { int data; DoublyNode next; DoublyNode prev; public DoublyNode(int data) { this.data = data; this.next = null; this.prev = null; } } class DoublyLinkedList { private DoublyNode head; // 插入节点到链表末尾 public void insert(int data) { DoublyNode newNode = new DoublyNode(data); if (head == null) { head = newNode; } else { DoublyNode current = head; while (current.next != null) { current = current.next; } current.next = newNode; newNode.prev = current; } } // 打印链表中的所有节点 public void printList() { DoublyNode current = head; while (current != null) { System.out.print(current.data + " <-> "); current = current.next; } System.out.println("null"); } // 反转链表(递归) public void reverse() { head = reverseRecursive(head); } private DoublyNode reverseRecursive(DoublyNode node) { if (node == null || node.next == null) { return node; // 基本情况:如果节点为空或只有一个节点,直接返回该节点 } DoublyNode rest = reverseRecursive(node.next); // 反转剩余部分的链表 node.next.next = node; // 将当前节点的下一个节点的next指向当前节点 node.prev = node.next; // 将当前节点的prev指向下一个节点 node.next = null; // 将当前节点的next置为空 return rest; // 返回新的头节点 } public static void main(String[] args) { DoublyLinkedList list = new DoublyLinkedList(); list.insert(1); list.insert(2); list.insert(3); list.printList(); // 输出: 1 <-> 2 <-> 3 <-> null list.reverse(); list.printList(); // 输出: 3 <-> 2 <-> 1 <-> null } }
4.3代码说明:
- DoublyNode类:表示双向链表中的一个节点,包含数据、指向下一个节点的引用和指向前一个节点的引用。
- DoublyLinkedList类:实现了双向链表的基本操作,包括插入、打印和反转。
insert(int data):在链表末尾插入一个新节点。printList():打印链表中的所有节点。reverse():反转链表,使用迭代法或递归法实现。
- main方法:测试上述功能。
5判断一个链表是否为回文链表。
要判断一个链表是否为回文链表,可以使用以下步骤:
- 使用快慢指针找到链表的中间节点。
- 反转链表的后半部分。
- 比较前半部分和反转后的后半部分是否相同。
- 恢复链表(可选)。
- 返回结果。
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class PalindromeLinkedList { public boolean isPalindrome(ListNode head) { if (head == null || head.next == null) return true; // Step 1: Find the middle of the linked list using slow and fast pointers ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // Step 2: Reverse the second half of the linked list ListNode reversedSecondHalf = reverseList(slow); // Step 3: Compare the first half and the reversed second half ListNode firstHalf = head; while (reversedSecondHalf != null) { if (firstHalf.val != reversedSecondHalf.val) return false; firstHalf = firstHalf.next; reversedSecondHalf = reversedSecondHalf.next; } return true; } private ListNode reverseList(ListNode head) { ListNode prev = null; while (head != null) { ListNode next = head.next; head.next = prev; prev = head; head = next; } return prev; } public static void main(String[] args) { PalindromeLinkedList solution = new PalindromeLinkedList(); // Example usage: ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(2); head.next.next.next = new ListNode(1); System.out.println(solution.isPalindrome(head)); // Output: true } }
5.1解释:
- 找到中间节点:使用快慢指针法,
slow每次走一步,fast每次走两步。当fast到达链表末尾时,slow正好在链表的中间位置。 - 反转后半部分链表:从中间节点开始反转链表的后半部分。
- 比较两部分:同时遍历原链表的前半部分和反转后的后半部分,逐个节点进行比较。如果所有对应节点的值都相等,则链表是回文的。
- 恢复链表(可选):如果需要保持链表的原始结构,可以在比较完成后再次反转后半部分链表。
这个算法的时间复杂度是O(n),空间复杂度是O(1)。
6合并两个有序链表。
合并两个有序链表是一个常见的面试题。假设我们有两个升序排列的链表,我们需要将它们合并成一个新的升序链表。
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class MergeTwoSortedLists { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { // 创建一个虚拟头节点,方便处理边界情况 ListNode dummy = new ListNode(0); ListNode current = dummy; // 遍历两个链表,直到其中一个为空 while (l1 != null && l2 != null) { if (l1.val <= l2.val) { current.next = l1; l1 = l1.next; } else { current.next = l2; l2 = l2.next; } current = current.next; } // 如果有一个链表还有剩余节点,直接连接到结果链表的末尾 if (l1 != null) { current.next = l1; } else { current.next = l2; } // 返回合并后的链表(跳过虚拟头节点) return dummy.next; } public static void main(String[] args) { // 创建测试用例 ListNode l1 = new ListNode(1); l1.next = new ListNode(2); l1.next.next = new ListNode(4); ListNode l2 = new ListNode(1); l2.next = new ListNode(3); l2.next.next = new ListNode(4); MergeTwoSortedLists solution = new MergeTwoSortedLists(); ListNode mergedList = solution.mergeTwoLists(l1, l2); // 打印合并后的链表 while (mergedList != null) { System.out.print(mergedList.val + " "); mergedList = mergedList.next; } } }
6.1解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 合并函数:
mergeTwoLists方法接受两个有序链表的头节点作为参数,并返回合并后的新链表的头节点。- 使用一个虚拟头节点
dummy来简化操作,避免处理头节点为空的情况。 - 使用一个指针
current来跟踪新链表的最后一个节点。 - 遍历两个链表,比较当前节点的值,将较小的节点连接到新链表中,并移动相应的指针。
- 当其中一个链表遍历完后,将另一个链表的剩余部分直接连接到新链表的末尾。
- 使用一个虚拟头节点
- 主函数:在
main方法中创建两个测试链表,调用mergeTwoLists方法进行合并,并打印合并后的链表。
这个算法的时间复杂度是O(n + m),其中n和m分别是两个链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
7在单链表中找到中间节点。
在单链表中找到中间节点是一个常见的问题,可以使用快慢指针法(也称为龟兔赛跑算法)来高效地解决这个问题。
7.1快慢指针法
- 初始化两个指针:一个慢指针
slow和一个快指针fast,都指向链表的头节点。 - 遍历链表:快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针正好位于链表的中间节点。
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class FindMiddleNode { public ListNode findMiddleNode(ListNode head) { if (head == null) return null; ListNode slow = head; ListNode fast = head; // 快慢指针遍历链表 while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // 当快指针到达末尾时,慢指针即为中间节点 return slow; } public static void main(String[] args) { // 创建测试用例 ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); head.next.next.next = new ListNode(4); head.next.next.next.next = new ListNode(5); FindMiddleNode solution = new FindMiddleNode(); ListNode middleNode = solution.findMiddleNode(head); // 打印中间节点的值 System.out.println("The middle node value is: " + middleNode.val); // Output: 3 } }
7.2解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 找到中间节点的方法:
findMiddleNode方法接受链表的头节点作为参数,并返回中间节点。- 如果链表为空,直接返回
null。 - 使用两个指针
slow和fast,初始时都指向头节点。 - 遍历链表,快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针正好位于链表的中间节点。
- 如果链表为空,直接返回
- 主函数:在
main方法中创建一个测试链表,调用findMiddleNode方法找到中间节点,并打印中间节点的值。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
8删除单链表中的重复元素。
删除单链表中的重复元素是一个常见的问题。我们可以使用一个哈希集合(
HashSet)来跟踪已经遇到的元素,从而在遍历链表时删除重复的元素。下面是一个完整的Java实现:
import java.util.HashSet; class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class RemoveDuplicatesFromLinkedList { public ListNode deleteDuplicates(ListNode head) { if (head == null) return null; HashSet<Integer> seen = new HashSet<>(); ListNode current = head; seen.add(current.val); while (current.next != null) { if (seen.contains(current.next.val)) { // 跳过重复节点 current.next = current.next.next; } else { // 记录新值并移动到下一个节点 seen.add(current.next.val); current = current.next; } } return head; } public static void main(String[] args) { // 创建测试用例 ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); head.next.next.next = new ListNode(2); head.next.next.next.next = new ListNode(4); RemoveDuplicatesFromLinkedList solution = new RemoveDuplicatesFromLinkedList(); ListNode result = solution.deleteDuplicates(head); // 打印结果链表 while (result != null) { System.out.print(result.val + " "); result = result.next; } } }
8.1解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 删除重复元素的方法:
deleteDuplicates方法接受链表的头节点作为参数,并返回处理后的链表头节点。- 如果链表为空,直接返回
null。 - 使用一个哈希集合
seen来记录已经遇到的元素。 - 初始化当前节点
current为头节点,并将头节点的值添加到seen中。 - 遍历链表,检查当前节点的下一个节点的值是否已经在
seen中:- 如果在
seen中,说明是重复元素,跳过该节点(即设置current.next = current.next.next)。 - 如果不在
seen中,将该值添加到seen中,并移动到下一个节点。
- 如果在
- 如果链表为空,直接返回
- 主函数:在
main方法中创建一个测试链表,调用deleteDuplicates方法删除重复元素,并打印处理后的链表。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(n),因为我们需要额外的哈希集合来存储已遇到的元素。
9检测单链表中是否存在环。
检测单链表中是否存在环是一个经典的问题,可以使用快慢指针法(也称为龟兔赛跑算法)来高效地解决这个问题。
9.1快慢指针法
- 初始化两个指针:一个慢指针
slow和一个快指针fast,都指向链表的头节点。 - 遍历链表:快指针每次移动两步,慢指针每次移动一步。如果链表中存在环,快指针最终会追上慢指针;如果不存在环,快指针会到达链表末尾(即
null)。
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkedListCycleDetection { public boolean hasCycle(ListNode head) { if (head == null || head.next == null) return false; ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { return true; // 存在环 } } return false; // 不存在环 } public static void main(String[] args) { // 创建测试用例 ListNode head = new ListNode(3); ListNode node1 = new ListNode(2); ListNode node2 = new ListNode(0); ListNode node3 = new ListNode(-4); head.next = node1; node1.next = node2; node2.next = node3; node3.next = node1; // 创建一个环 LinkedListCycleDetection solution = new LinkedListCycleDetection(); boolean result = solution.hasCycle(head); // 打印结果 System.out.println("Does the linked list have a cycle? " + result); // Output: true } }
9.2解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 检测环的方法:
hasCycle方法接受链表的头节点作为参数,并返回一个布尔值,表示链表中是否存在环。- 如果链表为空或只有一个节点,直接返回
false。 - 使用两个指针
slow和fast,初始时都指向头节点。 - 遍历链表,快指针每次移动两步,慢指针每次移动一步。如果快指针和慢指针相遇,说明存在环,返回
true。如果快指针到达链表末尾(即null),说明不存在环,返回false。
- 如果链表为空或只有一个节点,直接返回
- 主函数:在
main方法中创建一个测试链表,调用hasCycle方法检测是否存在环,并打印结果。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
10找到单链表中的环的入口点。
找到单链表中环的入口点是一个经典的问题,通常与检测环的存在一起解决。我们可以使用快慢指针法来检测环的存在,然后通过数学推导找到环的入口点。
10.1步骤:
- 检测环的存在:使用快慢指针法。如果存在环,快指针最终会追上慢指针。
- 找到环的入口点:
- 当快慢指针相遇时,假设环的长度为
L,从头节点到环入口的距离为a,从相遇点到环入口的距离为b。 - 设从头节点出发的指针和从相遇点出发的指针都以相同速度前进,它们会在环入口点相遇。
- 具体地,从头节点出发的指针走
a + b步,从相遇点出发的指针也走a + b步,两者在环入口点相遇。
- 当快慢指针相遇时,假设环的长度为
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkedListCycleEntryPoint { public ListNode detectCycle(ListNode head) { if (head == null || head.next == null) return null; ListNode slow = head; ListNode fast = head; // 检测是否存在环 while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { break; // 存在环,跳出循环 } } // 如果不存在环,返回null if (fast == null || fast.next == null) return null; // 找到环的入口点 slow = head; while (slow != fast) { slow = slow.next; fast = fast.next; } return slow; // 环的入口点 } public static void main(String[] args) { // 创建测试用例 ListNode head = new ListNode(3); ListNode node1 = new ListNode(2); ListNode node2 = new ListNode(0); ListNode node3 = new ListNode(-4); head.next = node1; node1.next = node2; node2.next = node3; node3.next = node1; // 创建一个环 LinkedListCycleEntryPoint solution = new LinkedListCycleEntryPoint(); ListNode entry = solution.detectCycle(head); // 打印结果 System.out.println("The entry point of the cycle is at node with value: " + entry.val); // Output: 2 } }
10.2解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 检测环并找到入口点的方法:
detectCycle方法接受链表的头节点作为参数,并返回环的入口点(如果存在)。- 如果链表为空或只有一个节点,直接返回
null。 - 使用两个指针
slow和fast,初始时都指向头节点。 - 遍历链表,快指针每次移动两步,慢指针每次移动一步。如果快指针和慢指针相遇,说明存在环,跳出循环。
- 如果快指针到达链表末尾(即
null),说明不存在环,返回null。 - 如果存在环,将慢指针重新指向头节点,同时保持快指针在相遇点,然后两个指针以相同速度前进,直到它们再次相遇,相遇点即为环的入口点。
- 如果链表为空或只有一个节点,直接返回
- 主函数:在
main方法中创建一个测试链表,调用detectCycle方法找到环的入口点,并打印结果。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
11删除单链表中的环。
删除单链表中的环可以通过找到环的入口点,然后将其断开来实现。以下是一个完整的Java实现:
11.1步骤:
- 检测环的存在:使用快慢指针法。如果存在环,快指针最终会追上慢指针。
- 找到环的入口点:当快慢指针相遇时,从头节点出发的指针和从相遇点出发的指针都以相同速度前进,它们会在环入口点相遇。
- 删除环:将环入口点的前一个节点的
next指向null,从而断开环。
下面是完整的Java代码:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkedListCycleRemoval { public void removeCycle(ListNode head) { if (head == null || head.next == null) return; ListNode slow = head; ListNode fast = head; // 检测是否存在环 while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { break; // 存在环,跳出循环 } } // 如果不存在环,直接返回 if (fast == null || fast.next == null) return; // 找到环的入口点 slow = head; ListNode prev = null; // 用于记录环入口点的前一个节点 while (slow != fast) { prev = fast; slow = slow.next; fast = fast.next; } // 删除环 prev.next = null; } public static void main(String[] args) { // 创建测试用例 ListNode head = new ListNode(3); ListNode node1 = new ListNode(2); ListNode node2 = new ListNode(0); ListNode node3 = new ListNode(-4); head.next = node1; node1.next = node2; node2.next = node3; node3.next = node1; // 创建一个环 LinkedListCycleRemoval solution = new LinkedListCycleRemoval(); solution.removeCycle(head); // 打印结果,验证环是否被删除 ListNode current = head; for (int i = 0; i < 10; i++) { // 打印前10个节点,避免无限循环 System.out.print(current.val + " "); current = current.next; if (current == null) break; } // Output: 3 2 0 -4 null (表示环已被删除) } }
11.2解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 删除环的方法:
removeCycle方法接受链表的头节点作为参数,并删除链表中的环(如果存在)。- 如果链表为空或只有一个节点,直接返回。
- 使用两个指针
slow和fast,初始时都指向头节点。 - 遍历链表,快指针每次移动两步,慢指针每次移动一步。如果快指针和慢指针相遇,说明存在环,跳出循环。
- 如果快指针到达链表末尾(即
null),说明不存在环,直接返回。 - 如果存在环,将慢指针重新指向头节点,同时保持快指针在相遇点,然后两个指针以相同速度前进,直到它们再次相遇,相遇点即为环的入口点。
- 将环入口点的前一个节点的
next指向null,从而断开环。
- 主函数:在
main方法中创建一个测试链表,调用removeCycle方法删除环,并打印结果验证环是否被删除。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
12将单链表重新排列成奇偶交错的顺序。
将单链表重新排列成奇偶交错的顺序是一个经典的问题。我们可以使用两个指针分别指向奇数位置和偶数位置的节点,然后重新链接这些节点。
12.1步骤:
- 创建两个虚拟头节点:一个用于奇数位置的节点,另一个用于偶数位置的节点。
- 遍历链表:使用两个指针分别指向当前处理的奇数位置和偶数位置的节点。
- 重新链接节点:将奇数位置的节点连接到奇数链表,将偶数位置的节点连接到偶数链表。
- 合并两个链表:将奇数链表的末尾连接到偶数链表的头部。
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkedListOddEvenReorder { public ListNode oddEvenList(ListNode head) { if (head == null || head.next == null) return head; ListNode oddDummy = new ListNode(0); // 虚拟头节点,用于奇数链表 ListNode evenDummy = new ListNode(0); // 虚拟头节点,用于偶数链表 ListNode odd = oddDummy; ListNode even = evenDummy; ListNode current = head; int index = 1; while (current != null) { if (index % 2 != 0) { odd.next = current; odd = odd.next; } else { even.next = current; even = even.next; } current = current.next; index++; } even.next = null; // 终止偶数链表 odd.next = evenDummy.next; // 连接奇数链表和偶数链表 return oddDummy.next; // 返回新的头节点 } public static void main(String[] args) { // 创建测试用例 ListNode head = new ListNode(1); ListNode node1 = new ListNode(2); ListNode node2 = new ListNode(3); ListNode node3 = new ListNode(4); ListNode node4 = new ListNode(5); head.next = node1; node1.next = node2; node2.next = node3; node3.next = node4; LinkedListOddEvenReorder solution = new LinkedListOddEvenReorder(); ListNode newHead = solution.oddEvenList(head); // 打印结果 ListNode current = newHead; while (current != null) { System.out.print(current.val + " "); current = current.next; } // Output: 1 3 5 2 4 } }
12.2解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 重新排列链表的方法:
oddEvenList方法接受链表的头节点作为参数,并返回重新排列后的链表。- 如果链表为空或只有一个节点,直接返回头节点。
- 创建两个虚拟头节点
oddDummy和evenDummy,分别用于奇数位置和偶数位置的节点。 - 使用两个指针
odd和even分别指向当前处理的奇数位置和偶数位置的节点。 - 遍历链表,根据索引的奇偶性将节点分别链接到奇数链表和偶数链表。
- 终止偶数链表,并将奇数链表的末尾连接到偶数链表的头部。
- 返回新的头节点。
- 主函数:在
main方法中创建一个测试链表,调用oddEvenList方法重新排列链表,并打印结果验证输出是否正确。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
13将单链表重新排列成奇偶交错的顺序。
将单链表重新排列成奇偶交错的顺序是一个经典的问题。我们可以使用两个指针分别指向奇数位置和偶数位置的节点,然后重新链接这些节点。
13.1步骤:
- 创建两个虚拟头节点:一个用于奇数位置的节点,另一个用于偶数位置的节点。
- 遍历链表:使用两个指针分别指向当前处理的奇数位置和偶数位置的节点。
- 重新链接节点:将奇数位置的节点连接到奇数链表,将偶数位置的节点连接到偶数链表。
- 合并两个链表:将奇数链表的末尾连接到偶数链表的头部。
下面是一个完整的Java实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkedListOddEvenReorder { public ListNode oddEvenList(ListNode head) { if (head == null || head.next == null) return head; ListNode oddDummy = new ListNode(0); // 虚拟头节点,用于奇数链表 ListNode evenDummy = new ListNode(0); // 虚拟头节点,用于偶数链表 ListNode odd = oddDummy; ListNode even = evenDummy; ListNode current = head; int index = 1; while (current != null) { if (index % 2 != 0) { odd.next = current; odd = odd.next; } else { even.next = current; even = even.next; } current = current.next; index++; } even.next = null; // 终止偶数链表 odd.next = evenDummy.next; // 连接奇数链表和偶数链表 return oddDummy.next; // 返回新的头节点 } public static void main(String[] args) { // 创建一个测试用例 ListNode head = new ListNode(1); ListNode node1 = new ListNode(2); ListNode node2 = new ListNode(3); ListNode node3 = new ListNode(4); ListNode node4 = new ListNode(5); head.next = node1; node1.next = node2; node2.next = node3; node3.next = node4; LinkedListOddEvenReorder solution = new LinkedListOddEvenReorder(); ListNode newHead = solution.oddEvenList(head); // 打印结果 ListNode current = newHead; while (current != null) { System.out.print(current.val + " "); current = current.next; } // 输出: 1 3 5 2 4 } }
13.2解释:
- 定义链表节点类:
ListNode类表示链表的节点,每个节点包含一个整数值和一个指向下一个节点的引用。 - 重新排列链表的方法:
oddEvenList方法接受链表的头节点作为参数,并返回重新排列后的链表。- 如果链表为空或只有一个节点,直接返回头节点。
- 创建两个虚拟头节点
oddDummy和evenDummy,分别用于奇数位置和偶数位置的节点。 - 使用两个指针
odd和even分别指向当前处理的奇数位置和偶数位置的节点。 - 遍历链表,根据索引的奇偶性将节点分别链接到奇数链表和偶数链表。
- 终止偶数链表,并将奇数链表的末尾连接到偶数链表的头部。
- 返回新的头节点。
- 主函数:在
main方法中创建一个测试链表,调用oddEvenList方法重新排列链表,并打印结果验证输出是否正确。
这个算法的时间复杂度是O(n),其中n是链表的长度。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。
14实现一个函数来检测两个单链表是否相交,并找出交点。
要检测两个单链表是否相交,并找出交点,我们可以使用以下步骤:
- 计算两个链表的长度。
- 将较长的链表的头指针向前移动,直到它与较短的链表长度相同。
- 同时遍历两个链表,直到找到相同的节点或到达链表末尾。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if (headA == null || headB == null) return null; // 获取两个链表的长度 int lenA = getLength(headA); int lenB = getLength(headB); // 将较长的链表的头指针向前移动,直到它与较短的链表长度相同 while (lenA > lenB) { headA = headA.next; lenA--; } while (lenB > lenA) { headB = headB.next; lenB--; } // 同时遍历两个链表,直到找到相同的节点或到达链表末尾 while (headA != null && headB != null) { if (headA == headB) { return headA; } headA = headA.next; headB = headB.next; } return null; // 没有交点 } private int getLength(ListNode node) { int length = 0; while (node != null) { length++; node = node.next; } return length; } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个getIntersectionNode方法来找到两个链表的交点。该方法首先检查输入链表是否为空,然后计算两个链表的长度,并将较长链表的头指针向前移动,直到两个链表的长度相等。最后同时遍历两个链表,直到找到相同的节点或到达链表末尾。如果找到了相同的节点,则返回该节点;如果没有找到交点,则返回null。15编写一个函数来求单链表的倒数第K个节点。
要找到单链表的倒数第K个节点,我们可以使用两个指针的方法。首先,我们将第一个指针移动K步,然后同时移动两个指针直到第一个指针到达链表末尾。此时,第二个指针将指向倒数第K个节点。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public ListNode findKthToLast(ListNode head, int k) { if (head == null || k <= 0) return null; ListNode first = head; ListNode second = head; // 将第一个指针向前移动k步 for (int i = 0; i < k; i++) { if (first == null) return null; // 如果k大于链表长度,返回null first = first.next; } // 同时移动两个指针,直到第一个指针到达链表末尾 while (first != null) { first = first.next; second = second.next; } return second; // 此时second指向倒数第k个节点 } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个findKthToLast方法来找到链表的倒数第K个节点。该方法首先检查输入参数是否有效,然后将第一个指针向前移动K步。如果K大于链表的长度,则返回null。接着同时移动两个指针,直到第一个指针到达链表末尾。此时,第二个指针将指向倒数第K个节点。16编写一个函数来求单链表的中点。
要找到单链表的中点,我们可以使用快慢指针的方法。快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针将位于链表的中间位置。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public ListNode findMiddle(ListNode head) { if (head == null) return null; ListNode slow = head; ListNode fast = head; // 快指针每次移动两步,慢指针每次移动一步 while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; // 当快指针到达链表末尾时,慢指针位于中点 } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个findMiddle方法来找到链表的中点。该方法首先检查输入链表是否为空,如果为空则返回null。接着使用两个指针(快指针和慢指针),快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针将位于链表的中间位置。此时返回慢指针所指向的节点即为链表的中点。17实现一个函数来检测两个单链表是否相等。
要检测两个单链表是否相等,我们可以逐个比较两个链表中的节点。如果所有对应的节点都相等,并且两个链表的长度相同,那么这两个链表是相等的。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean isEqual(ListNode headA, ListNode headB) { // 遍历两个链表,同时比较每个节点的值 while (headA != null && headB != null) { if (headA.val != headB.val) { return false; // 如果发现不相等的节点,返回false } headA = headA.next; headB = headB.next; } // 如果一个链表已经到达末尾而另一个没有,则两个链表长度不同,返回false if (headA != null || headB != null) { return false; } return true; // 所有节点都相等且长度相同,返回true } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个isEqual方法来检查两个链表是否相等。该方法通过循环遍历两个链表,并逐个比较对应节点的值。如果在任何时候发现节点值不相等,或者一个链表先到达末尾而另一个没有,则返回false。如果所有节点都相等且两个链表长度相同,则返回true。18实现一个函数来比较两个单链表的大小。
要比较两个单链表的大小,我们可以逐个节点地比较两个链表中的对应元素。如果在某个位置上,一个链表的元素大于另一个链表的元素,则该链表更大;如果所有对应的元素都相等,但一个链表更长,那么较长的链表更大。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public int compareLists(ListNode headA, ListNode headB) { // 遍历两个链表,同时比较每个节点的值 while (headA != null && headB != null) { if (headA.val > headB.val) { return 1; // 如果链表A的当前节点值大于链表B的当前节点值,返回1 } else if (headA.val < headB.val) { return -1; // 如果链表A的当前节点值小于链表B的当前节点值,返回-1 } headA = headA.next; headB = headB.next; } // 如果一个链表已经到达末尾而另一个没有,则较长的链表更大 if (headA != null) { return 1; // 链表A更长,返回1 } else if (headB != null) { return -1; // 链表B更长,返回-1 } return 0; // 所有节点都相等且长度相同,返回0 } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个compareLists方法来比较两个链表的大小。该方法通过循环遍历两个链表,并逐个比较对应节点的值。如果在任何时候发现一个链表的节点值大于另一个链表的节点值,则立即返回1或-1。如果所有对应的节点都相等,但一个链表先到达末尾而另一个没有,则返回1或-1,表示较长的链表更大。如果所有节点都相等且长度相同,则返回0。19实现一个函数来计算单链表的长度。
要计算单链表的长度,我们可以遍历整个链表,同时使用一个计数器来记录节点的数量。以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public int getLength(ListNode head) { int length = 0; ListNode current = head; // 遍历链表,直到到达末尾 while (current != null) { length++; // 对每个节点进行计数 current = current.next; // 移动到下一个节点 } return length; // 返回链表的长度 } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个getLength方法来计算链表的长度。该方法初始化一个计数器length为0,并使用一个指针current来遍历链表。每访问一个节点,就将计数器加1,并将指针移动到下一个节点。当指针到达链表末尾(即current为null)时,循环结束,此时计数器的值即为链表的长度。最后返回这个长度值。20实现一个函数来判断一个单链表是否是回文链表。
要判断一个单链表是否是回文链表,我们可以使用快慢指针找到链表的中点,然后反转后半部分链表,最后比较前半部分和反转后的后半部分是否相同。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean isPalindrome(ListNode head) { if (head == null || head.next == null) return true; // 使用快慢指针找到中点 ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // 反转后半部分链表 ListNode secondHalfStart = reverse(slow); ListNode firstHalfStart = head; // 比较前半部分和反转后的后半部分 while (secondHalfStart != null) { if (firstHalfStart.val != secondHalfStart.val) return false; firstHalfStart = firstHalfStart.next; secondHalfStart = secondHalfStart.next; } return true; } private ListNode reverse(ListNode head) { ListNode prev = null; while (head != null) { ListNode next = head.next; head.next = prev; prev = head; head = next; } return prev; } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个isPalindrome方法来判断链表是否是回文链表。该方法首先检查链表是否为空或只有一个节点,如果是,则直接返回true。接着使用快慢指针找到链表的中点,并调用reverse方法反转后半部分链表。最后,比较前半部分和反转后的后半部分是否相同。如果所有对应的节点都相等,则返回true;否则返回false。21实现一个函数来检测两个单链表是否有相同的元素。
要检测两个单链表是否有相同的元素,我们可以使用一个HashSet来存储第一个链表中的元素,然后遍历第二个链表并检查每个元素是否在HashSet中。
以下是Java代码实现:
import java.util.HashSet; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean hasCommonElements(ListNode headA, ListNode headB) { HashSet<Integer> set = new HashSet<>(); // 将第一个链表的所有元素添加到HashSet中 ListNode currentA = headA; while (currentA != null) { set.add(currentA.val); currentA = currentA.next; } // 遍历第二个链表,检查是否有元素存在于HashSet中 ListNode currentB = headB; while (currentB != null) { if (set.contains(currentB.val)) { return true; // 如果找到相同元素,返回true } currentB = currentB.next; } return false; // 如果没有找到相同元素,返回false } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个hasCommonElements方法来检测两个链表是否有相同的元素。该方法使用一个HashSet来存储第一个链表的所有元素,然后遍历第二个链表并检查每个元素是否在HashSet中。如果找到相同的元素,则返回true;否则,继续检查直到第二个链表结束,最后返回false。22实现一个函数来检测两个单链表是否互为镜像。
要检测两个单链表是否互为镜像,我们可以使用一个栈来帮助反转第一个链表,然后逐个比较第二个链表的节点和栈顶元素。
以下是Java代码实现:
import java.util.Stack; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areMirrors(ListNode headA, ListNode headB) { Stack<Integer> stack = new Stack<>(); // 将第一个链表的所有元素压入栈中 ListNode currentA = headA; while (currentA != null) { stack.push(currentA.val); currentA = currentA.next; } // 遍历第二个链表,并检查每个元素是否与栈顶元素相同 ListNode currentB = headB; while (currentB != null) { if (stack.isEmpty() || stack.pop() != currentB.val) { return false; // 如果栈为空或栈顶元素不等于当前节点的值,返回false } currentB = currentB.next; } return stack.isEmpty(); // 如果栈为空,则两个链表互为镜像,返回true;否则返回false } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areMirrors方法来检测两个链表是否互为镜像。该方法使用一个栈来存储第一个链表的所有元素,然后遍历第二个链表并检查每个元素是否与栈顶元素相同。如果所有对应的元素都相同,并且栈为空,则返回true;否则返回false。23实现一个函数来检测两个单链表是否互为旋转。
要检测两个单链表是否互为旋转,我们可以将第一个链表的节点值存储在一个数组中,然后将这个数组拼接到自身,形成一个新的“扩展”数组。接着,我们检查第二个链表的节点值是否按顺序出现在这个“扩展”数组中。
以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areRotations(ListNode headA, ListNode headB) { if (headA == null || headB == null) return false; // 将第一个链表的值存入数组 List<Integer> valuesA = new ArrayList<>(); ListNode currentA = headA; while (currentA != null) { valuesA.add(currentA.val); currentA = currentA.next; } // 创建扩展数组 List<Integer> extendedValuesA = new ArrayList<>(valuesA); extendedValuesA.addAll(valuesA); // 检查第二个链表的值是否按顺序出现在扩展数组中 ListNode currentB = headB; int index = 0; while (currentB != null) { index = extendedValuesA.indexOf(currentB.val, index); if (index == -1) return false; // 如果找不到,返回false index++; // 移动到下一个位置 currentB = currentB.next; } return true; // 如果所有元素都按顺序找到,返回true } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areRotations方法来检测两个链表是否互为旋转。该方法首先检查两个链表是否为空,如果有一个为空则直接返回false。接着,它将第一个链表的所有元素值存储到一个ArrayList中,并创建一个扩展的ArrayList,该ArrayList是原ArrayList的两倍长。然后,它遍历第二个链表,检查每个元素是否按顺序出现在扩展的ArrayList中。如果所有元素都按顺序找到,则返回true;否则返回false。24实现一个函数来检测两个单链表是否互为翻转。
要检测两个单链表是否互为翻转,我们可以使用一个栈来帮助反转第一个链表,然后逐个比较第二个链表的节点和栈顶元素。
以下是Java代码实现:
import java.util.Stack; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areReverses(ListNode headA, ListNode headB) { Stack<Integer> stack = new Stack<>(); // 将第一个链表的所有元素压入栈中 ListNode currentA = headA; while (currentA != null) { stack.push(currentA.val); currentA = currentA.next; } // 遍历第二个链表,并检查每个元素是否与栈顶元素相同 ListNode currentB = headB; while (currentB != null) { if (stack.isEmpty() || stack.pop() != currentB.val) { return false; // 如果栈为空或栈顶元素不等于当前节点的值,返回false } currentB = currentB.next; } return stack.isEmpty(); // 如果栈为空,则两个链表互为翻转,返回true;否则返回false } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areReverses方法来检测两个链表是否互为翻转。该方法使用一个栈来存储第一个链表的所有元素,然后遍历第二个链表并检查每个元素是否与栈顶元素相同。如果所有对应的元素都相同,并且栈为空,则返回true;否则返回false。25实现一个函数来检测两个单链表是否互为平移。
要检测两个单链表是否互为平移,我们可以将第一个链表的节点值存储在一个数组中,然后将这个数组拼接到自身,形成一个新的“扩展”数组。接着,我们检查第二个链表的节点值是否按顺序出现在这个“扩展”数组中。
以下是Java代码实现:
import java.util.ArrayList; import java.util.List; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areShifts(ListNode headA, ListNode headB) { if (headA == null || headB == null) return false; // 将第一个链表的值存入数组 List<Integer> valuesA = new ArrayList<>(); ListNode currentA = headA; while (currentA != null) { valuesA.add(currentA.val); currentA = currentA.next; } // 创建扩展数组 List<Integer> extendedValuesA = new ArrayList<>(valuesA); extendedValuesA.addAll(valuesA); // 检查第二个链表的值是否按顺序出现在扩展数组中 ListNode currentB = headB; int index = 0; while (currentB != null) { index = extendedValuesA.indexOf(currentB.val, index); if (index == -1) return false; // 如果找不到,返回false index++; // 移动到下一个位置 currentB = currentB.next; } return true; // 如果所有元素都按顺序找到,返回true } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areShifts方法来检测两个链表是否互为平移。该方法首先检查两个链表是否为空,如果有一个为空则直接返回false。接着,它将第一个链表的所有元素值存储到一个ArrayList中,并创建一个扩展的ArrayList,该ArrayList是原ArrayList的两倍长。然后,它遍历第二个链表,检查每个元素是否按顺序出现在扩展的ArrayList中。如果所有元素都按顺序找到,则返回true;否则返回false。26实现一个函数来检测两个单链表是否互为缩放。
要检测两个单链表是否互为平移,我们可以将第一个链表的节点值存储在一个数组中,然后将这个数组拼接到自身,形成一个新的“扩展”数组。接着,我们检查第二个链表的节点值是否按顺序出现在这个“扩展”数组中。
以下是Java代码实现:
import java.util.ArrayList; import java.util.List; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areShifts(ListNode headA, ListNode headB) { if (headA == null || headB == null) return false; // 将第一个链表的值存入数组 List<Integer> valuesA = new ArrayList<>(); ListNode currentA = headA; while (currentA != null) { valuesA.add(currentA.val); currentA = currentA.next; } // 创建扩展数组 List<Integer> extendedValuesA = new ArrayList<>(valuesA); extendedValuesA.addAll(valuesA); // 检查第二个链表的值是否按顺序出现在扩展数组中 ListNode currentB = headB; int index = 0; while (currentB != null) { index = extendedValuesA.indexOf(currentB.val, index); if (index == -1) return false; // 如果找不到,返回false index++; // 移动到下一个位置 currentB = currentB.next; } return true; // 如果所有元素都按顺序找到,返回true } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areShifts方法来检测两个链表是否互为平移。该方法首先检查两个链表是否为空,如果有一个为空则直接返回false。接着,它将第一个链表的所有元素值存储到一个ArrayList中,并创建一个扩展的ArrayList,该ArrayList是原ArrayList的两倍长。然后,它遍历第二个链表,检查每个元素是否按顺序出现在扩展的ArrayList中。如果所有元素都按顺序找到,则返回true;否则返回false。27实现一个函数来检测两个单链表是否互为旋转缩放。
要检测两个单链表是否互为旋转缩放,我们可以使用一个栈来帮助反转第一个链表,然后逐个比较第二个链表的节点和栈顶元素。
以下是Java代码实现:
import java.util.Stack; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areRotatedScales(ListNode headA, ListNode headB) { Stack<Integer> stack = new Stack<>(); // 将第一个链表的所有元素压入栈中 ListNode currentA = headA; while (currentA != null) { stack.push(currentA.val); currentA = currentA.next; } // 遍历第二个链表,并检查每个元素是否与栈顶元素相同 ListNode currentB = headB; while (currentB != null) { if (stack.isEmpty() || stack.pop() != currentB.val) { return false; // 如果栈为空或栈顶元素不等于当前节点的值,返回false } currentB = currentB.next; } return stack.isEmpty(); // 如果栈为空,则两个链表互为旋转缩放,返回true;否则返回false } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areRotatedScales方法来检测两个链表是否互为旋转缩放。该方法使用一个栈来存储第一个链表的所有元素,然后遍历第二个链表并检查每个元素是否与栈顶元素相同。如果所有对应的元素都相同,并且栈为空,则返回true;否则返回false。28实现一个函数来检测两个单链表是否互为旋转缩放平移。
要检测两个单链表是否互为旋转缩放平移,我们可以将第一个链表的节点值存储在一个数组中,然后将这个数组拼接到自身,形成一个新的“扩展”数组。接着,我们检查第二个链表的节点值是否按顺序出现在这个“扩展”数组中。
以下是Java代码实现:
import java.util.ArrayList; import java.util.List; class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areRotatedScaledShifts(ListNode headA, ListNode headB) { if (headA == null || headB == null) return false; // 将第一个链表的值存入数组 List<Integer> valuesA = new ArrayList<>(); ListNode currentA = headA; while (currentA != null) { valuesA.add(currentA.val); currentA = currentA.next; } // 创建扩展数组 List<Integer> extendedValuesA = new ArrayList<>(valuesA); extendedValuesA.addAll(valuesA); // 检查第二个链表的值是否按顺序出现在扩展数组中 ListNode currentB = headB; int index = 0; while (currentB != null) { index = extendedValuesA.indexOf(currentB.val, index); if (index == -1) return false; // 如果找不到,返回false index++; // 移动到下一个位置 currentB = currentB.next; } return true; // 如果所有元素都按顺序找到,返回true } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areRotatedScaledShifts方法来检测两个链表是否互为旋转缩放平移。该方法首先检查两个链表是否为空,如果有一个为空则直接返回false。接着,它将第一个链表的所有元素值存储到一个ArrayList中,并创建一个扩展的ArrayList,该ArrayList是原ArrayList的两倍长。然后,它遍历第二个链表,检查每个元素是否按顺序出现在扩展的ArrayList中。如果所有元素都按顺序找到,则返回true;否则返回false。29实现一个函数来检测两个单链表是否互为旋转缩放平移镜像。
要检测两个单链表是否互为旋转缩放平移镜像,我们需要定义一个更复杂的逻辑。首先,我们需要考虑如何表示“旋转”、“缩放”和“平移”。对于这个例子,我们可以假设:
- 旋转:将链表中的元素循环移动。
- 缩放:改变链表中元素的值(例如,乘以一个常数)。
- 平移:在链表中添加或删除元素。
- 镜像:反转链表。
为了简化问题,我们可以先实现一个函数来检测两个链表是否互为镜像,然后再考虑其他操作。以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areMirrors(ListNode headA, ListNode headB) { if (headA == null || headB == null) return false; // Reverse the first list ListNode reversedA = reverseList(headA); // Check if reversedA is equal to headB return compareLists(reversedA, headB); } private ListNode reverseList(ListNode head) { ListNode prev = null; ListNode current = head; while (current != null) { ListNode next = current.next; current.next = prev; prev = current; current = next; } return prev; } private boolean compareLists(ListNode head1, ListNode head2) { while (head1 != null && head2 != null) { if (head1.val != head2.val) return false; head1 = head1.next; head2 = head2.next; } return head1 == null && head2 == null; } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areMirrors方法来检测两个链表是否互为镜像。该方法首先检查两个链表是否为空,如果有一个为空则直接返回false。接着,它将第一个链表反转,然后比较反转后的链表和第二个链表是否相同。如果所有对应的元素都相同,并且两个链表都遍历完了,则返回true;否则返回false。30实现一个函数来检测两个单链表是否互为旋转缩放平移镜像翻转。
要检测两个单链表是否互为旋转、缩放、平移、镜像和翻转的组合,我们需要定义一个更复杂的逻辑。首先,我们需要考虑如何表示这些操作:
- 旋转:将链表中的元素循环移动。
- 缩放:改变链表中元素的值(例如,乘以一个常数)。
- 平移:在链表中添加或删除元素。
- 镜像:反转链表。
- 翻转:交换链表中的相邻节点。
为了简化问题,我们可以先实现一个函数来检测两个链表是否互为镜像,然后再考虑其他操作。以下是Java代码实现:
class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public class Solution { public boolean areMirrors(ListNode headA, ListNode headB) { if (headA == null || headB == null) return false; // Reverse the first list ListNode reversedA = reverseList(headA); // Check if reversedA is equal to headB return compareLists(reversedA, headB); } private ListNode reverseList(ListNode head) { ListNode prev = null; ListNode current = head; while (current != null) { ListNode next = current.next; current.next = prev; prev = current; current = next; } return prev; } private boolean compareLists(ListNode head1, ListNode head2) { while (head1 != null && head2 != null) { if (head1.val != head2.val) return false; head1 = head1.next; head2 = head2.next; } return head1 == null && head2 == null; } }
这段代码首先定义了一个
ListNode类来表示链表的节点。然后定义了Solution类,其中包含一个areMirrors方法来检测两个链表是否互为镜像。该方法首先检查两个链表是否为空,如果有一个为空则直接返回false。接着,它将第一个链表反转,然后比较反转后的链表和第二个链表是否相同。如果所有对应的元素都相同,并且两个链表都遍历完了,则返回true;否则返回false。