题目1:链表的中间节点
给你单链表的头节点 head
,请你找出并返回链表的中间节点。
如果有两个中间节点,则返回第二个中间节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
}
思路1
对于这个题目,我们想到的第一个思路就是先遍历链表得到链表的节点总数num,然后找到num/2个节点
代码1(C语言版)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* cur=head;
int n=0;
while(cur)
{
n++;
cur=cur->next;
}
int i=0;
for(i=1;i<=n/2+1;i++)
{
if(i==1)
{
cur=head;
}
else
cur=cur->next;
}
return cur;
}
但是这种办法要遍历两遍链表,虽然代码实现起来比较简单,但是程序的效率不高,于是我们思考能否只遍历一遍链表,就可以得到链表的中间节点?
思路2
我们可以建立两个指针变量,第一个指针每次向前走两步,第二个指针每次向前走一步,于是第一个指针变量指向的节点是链表的第2n个节点,而第二个指针变量指向的节点是链表的第n个节点。我们将这两个指针形象地称为快慢指针。
当快指针指向链表的最后一个节点时,慢指针恰好指向链表中间的节点
代码2(C语言版)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast && fast->next)
{
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
题目2:返回倒数第k个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k) {}
示例:
输入: 1->2->3->4->5 和 k = 2 输出: 4
如果想要得到链表的倒数第k个节点,我们可以先遍历依次链表,直至找到链表的最后一个节点,同时得到链表的长度,有个链表的长度我们就可以再次遍历链表得到倒数第k个节点。
思路
思考一下,我们上面介绍了快慢指针的方法帮助我们在时间复杂度为O(1)就得到了链表的中间节点,那如何是否可以利用快慢指针的方法在时间复杂度O(1)就得到指针的倒数第k个节点呢?
也就是说,当fast指针指向链表最后一个节点的时候,slow指针恰好指向链表的倒数第k个节点,所以,fast指针比slow指针快了k步
于是,在最开始的时候我们让fast先走k步,然后fast和slow再一次走一步,当fast偏好指向链表最后一个节点的时候,slow指针指向的节点就是链表的倒数第k个节点
代码(C语言版)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k) {
struct ListNode* slow=head;
struct ListNode* fast=head;
while(k-1)
{
fast=fast->next;
k--;
}
while(fast->next)
{
fast=fast->next;
slow=slow->next;
}
return slow->val;
}
题目3:反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
}
思路
- 我们首先会想到,建立两个指针,先让第二个节点的next指向第一个节点,然后n2=n2->next指向第三个节点再改变第三个节点的指向,但由于此时n2已经指向了n1,所以无法通过n2=n2->next找到第三个节点,所以我们发现这里需要三个指针
建立三个指针变量n1,n2,n3,先让n2->next指向n1实现反转,然后让n1=n2,n2=n3,n3=n3=n3->next就可以实现迭代
其次我们要注意的是循环的初始条件和结束条件
- 当链表反转后,原链表的第一个节点应该是新链表的尾节点,所以再反转的第一步,应该让n2等于原链表的第一个节点,让第一个链表的next指向NULL
- 进入循环的条件是n2不为NULL
- 循环结束的条件应该是当n2为空时,n2已经将链表的所有的节点都反转,但是值得注意的是,n3比n2先一步为NULL,所以应该加上条件判断
代码(C语言版)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
if(head==NULL)
{
return NULL;
}
struct ListNode* n1=NULL;
struct ListNode* n2=head;
struct ListNode* n3=head->next;
while(n2)
{
//反转
n2->next=n1;
//迭代
n1=n2;
n2=n3;
if(n3)
n3=n3->next;
}
return n1;
}
题目4:链表的回文结构
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
// write code here
}
};
测试样例:
1->2->3->2->1
返回:true
思路
对于一个回文结构的链表来说,它的前半段和后半段是对称的,那么我们就会想到将后半部分的链表反转,然后跟前一半的链表进行比较看是否相等。
那该如何得到后一半的链表呢?
我们题目一就已经回答了如何找到链表的中间节点,题目三回答了如何将链表反转,有了这两道题的基础,我们很快就可以解答这道题。
于是我们的代码一共分为三步:
第一步得到链表中间的的节点,将这个节点作为新链表的头节点
第二步将新链表反转
第三步将新链表和原链表的前半部分进行比较,如果相等,返回true
代码(C语言版)
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
struct ListNode* reverseList(struct ListNode* head) {
if (head == NULL) {
return NULL;
}
struct ListNode* n1 = NULL;
struct ListNode* n2 = head;
struct ListNode* n3 = head->next;
while (n2) {
//反转
n2->next = n1;
//迭代
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->next;
}
return n1;
}
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
struct ListNode* n1=A;
struct ListNode* mid=middleNode(A);
struct ListNode* n2=reverseList(mid);
while(n1&&n2)
{
if(n1->val!=n2->val)
{
return false;
}
else
{
n1=n1->next;
n2=n2->next;
}
}
return true;
}
};
(持续更新,下期见)