在链表题目开始之前我们来复习一道数组元素的逆序问题:
给定一个整数数组 nums
,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
提示:
1 <= nums.length <= 10^5
-2^31 <= nums[i] <= 2^31 - 1
0 <= k <= 10^5
思路1:旋转k次,每一次项右轮转一个元素。时间复杂度O(N^2)。空间复杂度O(1)。
思路2:创建一个新的数组,现将原数组的后k个元素放置到新数组中,然后再把剩余元素依次放入新数组中,最后把新数组的元素按顺序放回原数组。时间复杂度O(N)。空间复杂度O(N)。
上面两种思路各有优势,也有缺点。我们再来看看思路三。
思路3:先逆序前n-k个元素,再逆序后k个元素,最后整体逆序。这种方法在时间、空间复杂度上都是最优的,但是思路不好想到,这就要大家多多积累,我们来看看代码:
void revese(int* nums,int left ,int right)
{
while(left <= right)
{
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
right--;
}
}
void rotate(int* nums, int numsSize, int k)
{
k %= numsSize;
revese(nums,0,numsSize-k-1);
revese(nums,numsSize-k,numsSize-1);
revese(nums,0,numsSize-1);
}
例1:返回链表的倒数第k个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。给定的 k 保证是有效的。
解:
这到题的思路和之前的快慢指针相似,我称之为先后指针,我们先创建两个指针(fast、slow),既然是找倒数第k个节点,那我们就先让fast走k个节点,然后让两个指针同时向后走,当fast指针走到尾节点时,slow节点就是倒数第k个节点。代码如下:
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k)
{
ListNode* fast = head;
ListNode* slow = head;
while(k--)
{
fast = fast->next;
}
while(fast)
{
fast = fast->next;
slow = slow->next;
}
return slow->val;
}
例2:链表的回文结构
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
解:
这道题有些难度,不过我们使用之前学的解法思路可以很轻松的过这道题。既然是回文结构,那我们就先找到他的中间节点,然后我们在逆序后半段链表,然后再比较原头结点和逆序后的头结点的值,如果不相等就返回false,反之继续向后遍历,直到其中有一个指针指向为空,返回true。
bool chkPalindrome(ListNode* A)
{
//寻找中间节点
struct ListNode * fast = A;
struct ListNode * slow = A;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
//翻转后半链表:头插法
struct ListNode* pcur = slow;
struct ListNode* newhead = NULL;
while(pcur)
{
struct Li