25.K个一组翻转链表
思路:常规情况,使用栈遍历链表,每当栈中的元素等于K时,则翻转一次。因为栈是后进先出的,出栈顺序恰好与入栈顺序相反,可以轻易实现翻转。但这种思路,需要K的空间来存储链表结点,题目提出挑战使用常数空间来翻转,因此挑战使用常数空间。可以观察到每一次翻转,需要保存的主要有这个K元组的前一个结点和后一个结点,因此使用四个结点指针,其中一个保存前一个结点,另一个保存后一个结点,第三个指针和第四个指针用来在中间的k元组进行翻转用。以链表{1,2,3,4,5}, k = 3为例子,步骤如下:
- 首先添加VirtualHead,用来保留结果返回,并且用来辅助第一次翻转。保存前一个结点。
- 将head指针推进k-1用来寻找后一个结点,若寻找过程中遇到结尾,则说明剩余的结点不够翻转了,直接返回。
-
此时K元组为{1,2,3},其中第一个结点即为下一个K元组的前一个结点。同时将前一个结点的next指向此时K元组的最后一个结点,因为翻转过后,最后一个结点会变为第一个结点。
-
将此时newHead的下一个结点指向tail,因为翻转完成后,newHead会作为K元组的最后一个结点。更改前记得保留newHead原本的next结点,即2结点。
-
此时k元组与前一个结点和后一个结点的关系已经转换完成,接下来只需遍历k元组并将其之间的关系翻转。这里需要用三个指针用来翻转,分别保留当前结点的前一个结点,当前结点以及当前结点的后一个结点,以当前结点为2为例。
-
使2指向1,同时递进当前结点为3,更新相关指针。
-
翻转3后,此时当前结点指向tail,表明k元组翻转完成。翻转后的结果如图。
-
此时,在向前递进head的时候,提前遇到了链表末尾,则表明当前已不存在k元组可以翻转,直接返回结果。
代码:
public ListNode reverseKGroup(ListNode head, int k) {
ListNode virtualHead = new ListNode(-1), ptr = virtualHead, newHead = ptr, tail = ptr;
virtualHead.next = head;
while (head != null) {
for (int i = 0; i < k - 1; i++) {
if (head == null) {
break;
}
head = head.next;
}
if (head == null) {
break;
}
tail = head.next;
ptr = newHead;
newHead = newHead.next;
ptr.next = head;
head = newHead.next;
newHead.next = tail;
ptr = newHead;
while (head != tail) {
ListNode next = head.next;
head.next = ptr;
ptr = head;
head = next;
}
}
return virtualHead.next;
}
时间复杂度:O(n)
空间复杂度:O(1)
26.删除有序数组中的重复项
思路:双指针,一个写指针记录上一次写入的位置,一个读指针遍历数组。因为第一个数总是不重复,因此写指针指向第一个数代表已经将其写入,而读指针从第二个数开始遍历。当读指针指向的值等于上次写指针指向的值时,说明当前读到的是重复值,读指针向前推进一步。当不相等时,首先让写指针推进1指向现在需要写入的位置,写入当前值,然后读指针继续向后遍历。当读指针遍历完成后,返回写指针的位置。
代码:
public int removeDuplicates(int[] nums) {
if (nums.length <= 1) {
return nums.length;
}
int writePtr = 0, readPtr = 1;
while (readPtr < nums.length) {
if (nums[readPtr] == nums[writePtr]) {
readPtr ++;
} else {
writePtr ++;
nums[writePtr] = nums[readPtr];
readPtr ++;
}
}
return writePtr + 1;
}
时间复杂度:O(n)
空间复杂度:O(1)
27.移除元素
思路:与前一道题类似,需要注意的是两个指针的起始位置。此时第一个数可能是需要移除的元素,因此写指针为-1代表还没有写入任何元素,而读指针则从第一个元素开始遍历。
代码:
public int removeElement(int[] nums, int val) {
if (nums.length == 0) {
return 0;
}
int writePtr = -1, readPtr = 0;
while (readPtr < nums.length) {
if (nums[readPtr] == val) {
readPtr ++;
} else {
writePtr ++;
nums[writePtr] = nums[readPtr];
readPtr ++;
}
}
return writePtr + 1;
}
时间复杂度:O(n)
空间复杂度:O(1)