目录


题目描述中要求将链表每 k 个节点作为一组进行翻转。这是一个比较经典的链表问题,可以使用迭代的方法来解决。具体思路如下:
- /**
- * Definition for singly-linked list.
- * public class ListNode {
- * int val;
- * ListNode next;
- * ListNode() {}
- * ListNode(int val) { this.val = val; }
- * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
- * }
- */
- class Solution {
- public ListNode reverseKGroup(ListNode head, int k) {
- ListNode dummy = new ListNode(0);
- dummy.next = head;
- ListNode prev = dummy;
- ListNode curr = head;
- int count = 0;
-
- while (curr != null) {
- count++;
- ListNode next = curr.next;
- if (count == k) {
- prev = reverse(prev, next);
- count = 0;
- }
- curr = next;
- }
-
- return dummy.next;
- }
-
- private ListNode reverse(ListNode start, ListNode end) {
- ListNode prev = start;
- ListNode curr = start.next;
- ListNode first = curr;
-
- while (curr != end) {
- ListNode next = curr.next;
- curr.next = prev;
- prev = curr;
- curr = next;
- }
-
- start.next = prev;
- first.next = curr;
- return first;
- }
-
- }
复杂度分析:
时间复杂度分析:
因此,总的时间复杂度是 O(n)。
空间复杂度分析:
综上所述,该解法的时间复杂度为 O(n),空间复杂度为 O(1)。
LeetCode运行结果:

除了迭代的解法,还可以使用递归的思路来实现链表的翻转。
具体的递归思路如下:
reverseKGroupRecursive,其功能是将以 head 为头节点的链表每 k 个节点进行翻转,并返回翻转后的链表的头节点。next。
next!=null,说明当前组内有至少 k 个节点,可以进行翻转操作。
reverse 函数对以 head 为头节点、以 next 为尾节点的子链表进行翻转,并将翻转后的尾节点返回,将其赋值给 newHead。head 的 next 指针指向下一组的头节点,即调用 reverseKGroupRecursive(next, k)。newHead,作为翻转后的链表的头节点。next==null,说明剩余的节点数少于 k 个,无需翻转,直接返回 head。- /**
- * Definition for singly-linked list.
- * public class ListNode {
- * int val;
- * ListNode next;
- * ListNode() {}
- * ListNode(int val) { this.val = val; }
- * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
- * }
- */
- class Solution {
- public ListNode reverseKGroup(ListNode head, int k) {
- ListNode curr = head;
- int count = 0;
-
- while (curr != null && count < k) {
- curr = curr.next;
- count++;
- }
-
- if (count == k) {
- ListNode newHead = reverseKGroupRecursive(head, curr, k);
- head.next = reverseKGroup(curr, k);
- return newHead;
- }
-
- return head;
- }
-
- private ListNode reverseKGroupRecursive(ListNode head, ListNode tail, int k) {
- ListNode prev = tail;
- ListNode curr = head;
-
- while (curr != tail) {
- ListNode next = curr.next;
- curr.next = prev;
- prev = curr;
- curr = next;
- }
-
- return prev;
- }
-
- }
复杂度分析:
时间复杂度分析:
因此,总的时间复杂度是 O(n)。
空间复杂度分析:
综上所述,递归解法的时间复杂度为 O(n),空间复杂度为 O(n/k)(在最坏情况下为 O(n))。
需要注意的是,递归方法对于链表长度较大时可能会导致递归调用栈溢出,因此在实际使用中需要注意链表长度是否适合使用递归解法。
LeetCode运行结果:

除了迭代、递归的方法,还可以使用指针转向的思路来实现链表的翻转。
- /**
- * Definition for singly-linked list.
- * public class ListNode {
- * int val;
- * ListNode next;
- * ListNode() {}
- * ListNode(int val) { this.val = val; }
- * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
- * }
- */
- class Solution {
- public ListNode reverseKGroup(ListNode head, int k) {
- if (head == null || k <= 1) {
- return head;
- }
-
- ListNode dummy = new ListNode(0); // 创建一个虚拟头节点
- dummy.next = head;
- ListNode prev = dummy;
-
- while (head != null) {
- ListNode tail = prev;
-
- // 判断剩余节点数是否大于等于 k
- for (int i = 0; i < k; i++) {
- tail = tail.next;
- if (tail == null) {
- // 剩余的节点数不满 k 个,直接返回结果
- return dummy.next;
- }
- }
-
- ListNode nextGroupHead = tail.next; // 下一组节点的头节点
-
- // 翻转当前组内的节点
- ListNode[] reversed = reverse(head, tail);
- head = reversed[0];
- tail = reversed[1];
-
- // 将翻转后的组连接到结果链表中
- prev.next = head;
- tail.next = nextGroupHead;
-
- // 更新 prev 和 head,准备处理下一组
- prev = tail;
- head = nextGroupHead;
- }
-
- return dummy.next;
- }
-
- private ListNode[] reverse(ListNode head, ListNode tail) {
- ListNode prev = tail.next;
- ListNode curr = head;
-
- while (prev != tail) {
- ListNode next = curr.next;
- curr.next = prev;
- prev = curr;
- curr = next;
- }
-
- return new ListNode[] {tail, head};
- }
- }
该方法通过指针转向的方式,每次翻转一组的节点。其中,reverse 方法用于翻转当前组内的节点。
复杂度分析:
时间复杂度分析:
空间复杂度分析:
综合来说,该方法的时间复杂度为 O(n),空间复杂度为 O(1)。需要注意的是,在处理不满 k 个节点的情况时,需要额外判断并返回结果。
LeetCode运行结果:

26. 删除有序数组中的重复项 - 力扣(LeetCode)


由于数组已经按非严格递增排列,可以利用快慢指针来遍历数组。慢指针指向当前不重复的元素,快指针用于遍历整个数组。如果快指针指向的元素与慢指针指向的元素不同,说明找到了一个新的不重复元素,将其存放在慢指针的下一个位置,并将慢指针后移一位。最后返回慢指针加1即可。
- class Solution {
- public int removeDuplicates(int[] nums) {
- if(nums.length == 0){
- return 0;
- }
- int i = 0;
- for(int j = 1; j < nums.length; j++){
- if(nums[i] != nums[j]){
- i++;
- nums[i] = nums[j];
- }
- }
- return i + 1;
- }
-
- }
复杂度分析:
时间复杂度:O(n),其中n为数组的长度。该算法通过使用快慢指针,只需一次遍历数组即可完成操作。
空间复杂度:O(1)。该算法只使用了常数级的额外空间,不随输入规模的增加而增加。
LeetCode运行结果:

除了使用快慢指针的解法外,在Java中还可以使用Arrays类的sort方法来解决该问题。
该方法首先对数组进行排序,使得重复元素相邻。然后使用快慢指针的方法遍历数组,将不重复的元素放到慢指针所指向的位置,并移动慢指针。最后返回慢指针加1作为新数组的长度。
- class Solution {
- public int removeDuplicates(int[] nums) {
- Arrays.sort(nums);
- int i = 0;
- for (int j = 1; j < nums.length; j++) {
- if (nums[j] != nums[i]) {
- i++;
- nums[i] = nums[j];
- }
- }
- return i + 1;
- }
-
- }
复杂度分析:
LeetCode运行结果:

除了前面提到的思路和方法,还可以使用一个计数器来记录重复元素的个数,并根据计数器的值进行相应的操作。
- class Solution {
- public int removeDuplicates(int[] nums) {
- if (nums.length == 0) {
- return 0;
- }
-
- int count = 1;
- int duplicateCount = 0;
-
- for (int i = 1; i < nums.length; i++) {
- if (nums[i] == nums[i - 1]) {
- duplicateCount++;
- } else {
- count++;
- }
-
- if (duplicateCount > 0) {
- nums[i - duplicateCount] = nums[i];
- }
- }
-
- return count;
- }
-
- }
复杂度分析:
在遍历数组时,只有在出现重复元素时才会对数组进行修改。对数组的修改是通过将重复元素向前移动来实现的,而不是删除重复元素。因此,不需要额外的空间来存储删除后的新数组。这种方法的空间复杂度是常数级别的。
综上所述,使用计数器的解法是一种高效的解决方案,具有线性的时间复杂度和常数级别的空间复杂度。
LeetCode运行结果:

除了之前提到的思路和方法,还可以使用额外的数组来存储不重复的元素。
该方法创建一个新的数组uniqueNums,用于存储不重复的元素。遍历原始数组时,如果当前元素与前一个元素不相同,则将当前元素添加到uniqueNums数组中,并将计数器count加1。最后,将uniqueNums数组中的元素赋值回原数组nums,并返回count作为新数组的长度。
- class Solution {
- public int removeDuplicates(int[] nums) {
- if (nums.length == 0) {
- return 0;
- }
-
- int[] uniqueNums = new int[nums.length];
-
- uniqueNums[0] = nums[0];
- int count = 1;
-
- for (int i = 1; i < nums.length; i++) {
- if (nums[i] != nums[i - 1]) {
- uniqueNums[count] = nums[i];
- count++;
- }
- }
-
- // 将uniqueNums数组中的元素赋值回原数组
- for (int i = 0; i < count; i++) {
- nums[i] = uniqueNums[i];
- }
-
- return count;
- }
-
- }
复杂度分析:
在遍历原始数组时,只有在出现与前一个元素不相同的元素时才将其添加到新数组中。因此,新数组的长度最多为原数组的长度,即使用了额外的O(n)空间。
综上所述,使用额外数组的解法是一种线性时间复杂度的解决方案,但需要额外的空间来存储新的数组。这种方法适用于不要求原地修改数组的情况。
LeetCode运行结果:



这道题可以使用双指针的方法来解决。定义两个指针:慢指针slow和快指针fast。
算法的思路如下:
- class Solution {
- public int removeElement(int[] nums, int val) {
- int slow = 0;
- for (int fast = 0; fast < nums.length; fast++) {
- if (nums[fast] != val) {
- nums[slow] = nums[fast];
- slow++;
- }
- }
- return slow;
- }
-
- }
复杂度分析:
综上所述,该算法的时间复杂度为 O(n),空间复杂度为 O(1)。
LeetCode运行结果:

除了双指针的方法,还可以使用 Java 中的 List 来解决这个问题。具体的做法如下:
- class Solution {
- public int removeElement(int[] nums, int val) {
- List
list = new ArrayList<>(); - for (int num : nums) {
- if (num != val) {
- list.add(num);
- }
- }
- Integer[] arr = list.toArray(new Integer[list.size()]);
- for (int i = 0; i < arr.length; i++) {
- nums[i] = arr[i];
- }
- return list.size();
- }
-
- }
这种方法的本质是通过使用 List 集合来存储不等于给定值val的元素,然后将 List 转换为数组并重新赋值给原数组。最后返回 List 的大小即为移除元素后的新长度。
复杂度分析:
时间复杂度:
综上所述,整个算法的时间复杂度为 O(n)。
空间复杂度:
综上所述,整个算法的空间复杂度为 O(n)。
相比之下,双指针和的方法在空间复杂度上是优于使用 List 的方法的,因为它只需要常数个额外变量,即空间复杂度为 O(1)。因此,建议使用双指针的方法来解决这个问题,以获得更好的空间效率。
LeetCode运行结果:

除了双指针、List,还可以使用交换元素的思路来解决这个问题。具体的做法如下:
- class Solution {
- public int removeElement(int[] nums, int val) {
- int i = 0;
- int j = nums.length - 1;
- while (i <= j) {
- while (i <= j && nums[i] != val) {
- i++;
- }
- while (i <= j && nums[j] == val) {
- j--;
- }
- if (i < j) {
- nums[i] = nums[j];
- i++;
- j--;
- }
- }
- return i;
- }
-
- }
这种交换元素的方法通过使用两个指针分别从头尾向中间移动,并通过交换元素的方式来实现移除元素的目的。
复杂度分析:
综上所述,使用交换元素的方法的时间复杂度为 O(n),空间复杂度为 O(1)。与双指针的方法相比,交换元素的方法具有相同的时间复杂度,但空间复杂度更低,因为不需要额外的数据结构(如 List)来存储元素。从空间效率的角度来看,交换元素的方法是较优的选择。
LeetCode运行结果:

除了双指针、交换元素和使用 List 的方法,还可以利用递归来解决这个问题。具体的做法如下:
- class Solution {
- public int removeElement(int[] nums, int val) {
- return removeElementRecursive(nums, val, 0, 0);
- }
-
- private int removeElementRecursive(int[] nums, int val, int index, int count) {
- if (index == nums.length) {
- return count;
- }
- if (nums[index] != val) {
- nums[count] = nums[index];
- count++;
- }
- return removeElementRecursive(nums, val, index + 1, count);
- }
-
-
- }
这种递归的方法通过逐个处理数组元素,将不等于给定值 val 的元素覆盖到数组的前部分,从而实现移除元素的目的。
复杂度分析:
综上所述,使用递归的方法的时间复杂度为 O(n),空间复杂度为 O(n)。
需要注意的是,递归方法的空间复杂度相对较高,因为每次递归调用都需要在栈上保存一些信息。在处理大规模数组时,可能会导致栈溢出。因此,如果要处理大规模数组,建议使用其他方法,如双指针或交换元素的方法,以保证较低的空间开销。
LeetCode运行结果:
