- 已知一个带有表头结点的单链表,结点结构为:
data next
假设该链表只给出了头指针 head。 在不改变链表的前提下,请设计一个尽可能高效的算法,查找链 表中倒数第k(k 为正整数)个位置上的结点。若查找成功,算法输出该结点的 data 值,并返回1;否则,
只返回0。
要求:
(1)描述算法的基本设计思想。
(2)描述算法的详细实现步骤。
(3)根据设计思想和实现步骤,采用程序设计语言描述算法(使用 C 或 C++ 语言实现),关键之处
请给出简要注释。
渣男回答:
- 基本设计思想:
我们可以使用双指针法来解决这个问题。首先,我们创建两个指针,一个快指针和一个慢指针。快指针先向前移动 k 个节点,然后快指针和慢指针同时向前移动,直到快指针到达链表的末尾。此时,慢指针所指向的节点就是倒数第 k 个节点。
(2) 详细实现步骤:
初始化两个指针,fast 和 slow,都指向头结点 head。
将 fast 指针向前移动 k 个节点。
同时移动 fast 和 slow 指针,直到 fast 指针到达链表的末尾。
如果 fast 指针到达链表末尾时,slow 指针指向的节点是倒数第 k 个节点,则返回该节点的 data 值;否则,返回 0。
(3) C++ 代码实现:
cpp
复制代码运行
include
using namespace std;
struct ListNode {
int data;
ListNode *next;
};
int findKthFromEnd(ListNode *head, int k, int &result) {
if (head == nullptr || k <= 0) {
return 0;
}
ListNode *fast = head;
ListNode *slow = head;
// 将 fast 指针向前移动 k 个节点
for (int i = 0; i < k; ++i) {
if (fast == nullptr) {
return 0;
}
fast = fast->next;
}
// 同时移动 fast 和 slow 指针,直到 fast 指针到达链表的末尾
while (fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
// 如果 slow 指针指向的节点是倒数第 k 个节点,则返回该节点的 data 值
if (slow != nullptr) {
result = slow->data;
return 1;
}
return 0;
}
include <stdio.h>
include <stdlib.h>
typedef struct ListNode {
int data;
struct ListNode *next;
} ListNode;
int findKthFromEnd(ListNode *head, int k, int *result) {
if (head == NULL || k <= 0) {
return 0;
}
ListNode *fast = head;
ListNode *slow = head;
// 将 fast 指针向前移动 k 个节点
for (int i = 0; i < k; ++i) {
if (fast == NULL) {
return 0;
}
fast = fast->next;
}
// 同时移动 fast 和 slow 指针,直到 fast 指针到达链表的末尾
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
// 如果 slow 指针指向的节点是倒数第 k 个节点,则返回该节点的 data 值
if (slow != NULL) {
*result = slow->data;
return 1;
}
return 0;
}
为什么要两个指针?
在链表问题中使用两个指针,通常被称为“快慢指针”技巧,是一种高效的算法设计技术。具体原因如下:
提高效率:通过使用快慢指针,可以在一次遍历中解决问题,而不需要对整个链表进行多次遍历。快指针先行k步,然后慢指针开始移动,当快指针到达链表末尾时,慢指针即位于倒数第k个节点。
节省空间:这种方法不需要额外的数据结构来存储链表的节点信息,因此可以节省空间。
简化逻辑:使用两个指针可以使逻辑更加直观和简单。快指针用于确定范围,慢指针用于跟踪目标节点。这种方法的逻辑比单指针要清晰得多。
避免修改原链表:使用两个指针可以在不修改链表结构的情况下找到倒数第k个节点,这对于不允许或不希望修改原始数据结构的情况非常有用。
解决特定问题:对于一些特定的链表问题,如寻找链表中的环或者寻找倒数第k个节点,双指针技术提供了一种有效的解决方案。
总之,双指针技术在链表操作中非常有用,尤其是在需要高效解决特定类型问题时。
这个是谁最先使用的
双指针法这一技术的具体起源并不明确,因为它是算法和数据结构领域中一个逐渐发展起来的概念。
双指针法是一种常用的算法设计技巧,它在计算机科学的许多领域都有应用。这种方法可以通过两个移动的指针来遍历数据结构,如数组或链表,以解决特定类型的问题。例如,在数组中寻找两数之和等于特定值的问题,或者在链表中寻找倒数第k个节点的问题。双指针法可以提高效率,因为它通常只需要一次遍历即可找到解决方案。
在编程竞赛和算法教育中,双指针法经常被提及和教授,LeetCode等在线算法平台也有许多使用双指针法的示例和问题。然而,由于这是一种基础的算法技巧,很多早期的算法文献和教材都可能没有特别强调其起源,而是作为解决特定问题的常用方法来介绍。
总的来说,双指针法是一个在算法发展中自然形成的概念,它并没有一个明确的发明者,而是众多计算机科学家和工程师在解决问题时共同贡献的结果。
双指针法的最高级形态体现在其应用的多样性和复杂性上,能够解决各种复杂的算法问题。以下是一些高级应用:
- 滑动窗口算法:在数组问题中,双指针可以用来维护一个动态的窗口,例如在解决“盛最多水的容器”问题时,两个指针分别表示容器的左右边界,通过移动这两个指针来找到能装最多水的容器。
- 有序数组合并:当涉及到有序数组的合并时,双指针可以用来追踪下一个待选值可能出现的位置,从而高效地完成合并操作。
- 链表问题处理:在链表中,双指针可以用来处理断链问题,其中一个指针用于维护断链后链表的头部,另一个指针指向当前节点以完成特定操作。
- 对撞指针:在有序数组中,双指针可以从两端向中间遍历,这种方法适用于需要在有序数组中寻找特定元素或条件的问题。左指针从最左侧开始,右指针从最右侧开始,向中间移动,直到找到满足条件的解或者两指针相遇。
- 快慢指针:在链表问题中,快慢指针技巧可以用来检测链表中的环或者寻找倒数第k个节点。快指针先行k步,然后慢指针开始移动,当快指针到达链表末尾时,慢指针即位于倒数第k个节点。
总的来说,双指针法的最高级形态体现在其能够灵活应用于多种数据结构和算法问题中,通过精心设计的两个指针的移动策略,可以高效地解决问题。这些应用不仅提高了算法的效率,也展现了双指针法的强大能力和灵活性。
对撞指针是一种高效的算法技巧,用于在有序数组中查找特定元素或满足特定条件的元素对。
对撞指针的工作原理是使用两个指针,一个从数组的开始位置(左指针)出发,另一个从数组的结束位置(右指针)出发。这两个指针向数组中间移动,直到它们相遇或者找到所需的元素。这种技术适用于有序数组,因为元素的有序性允许通过比较两个指针所指向的元素来确定继续搜索的方向。具体来说,对撞指针的应用可以分为以下几个方面:
- 查找元素:当需要找到一个特定的值时,可以通过比较左右指针所指向的值与目标值的大小关系来决定是向左移动左指针还是向右移动右指针。这样可以逐步缩小搜索范围,直到找到目标值或者指针相遇。
- 查找元素对:在某些问题中,比如需要找到两个数的和等于特定值的情况,也可以通过对撞指针来减少搜索时间。如果当前两指针之和小于目标值,则右指针向右移动;如果之和大于目标值,则左指针向左移动。这样不断迭代,直到找到满足条件的数对或者指针相遇。
- 效率提升:由于每次迭代都在有序数组中排除了一半的搜索空间,对撞指针的效率非常高,时间复杂度通常为O(log n),其中n是数组的长度。
此外,对撞指针不仅提高了解决问题的效率,还简化了代码逻辑,使得算法更加简洁易懂。在实际应用中,对撞指针可以解决各种类型的问题,包括但不限于数组中的元素查找、有序数组的最大值和最小值查找、以及一些复杂的双元素问题。
总的来说,对撞指针是双指针法的一种高级应用,它利用了数组的有序特性,通过两个指针从两端向中间迭代,高效地解决了有序数组中的查找问题。
当然,下面是一些使用对撞指针的代码示例:
- 查找有序数组中的目标值:
def binary_search(arr, target):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
- 查找有序数组中两个数之和等于特定值的元素对:
def two_sum_sorted(arr, target):
left = 0
right = len(arr) - 1
while left < right:
current_sum = arr[left] + arr[right]
if current_sum == target:
return [left, right]
elif current_sum < target:
left += 1
else:
right -= 1
return []
- 查找有序数组中第一个大于等于目标值的元素:
def lower_bound(arr, target):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] >= target:
right = mid - 1
else:
left = mid + 1
return left
这些示例展示了对撞指针在有序数组中的不同应用。通过比较左右指针所指向的值与目标值的大小关系,可以逐步缩小搜索范围,直到找到目标值或者指针相遇。这种技巧在解决有序数组问题时非常有效,并且具有高效性和简洁性。
标签:历险记,slow,代码,fast,链表,数组,节点,指针 From: https://www.cnblogs.com/jjjkkklll/p/18151439