本篇文章涉及到前面文章的相关内容:
- C语言简单的数据结构:单链表的有关算法题(1)
- C语言简单的数据结构:单链表的有关算法题(2)
本文内容如下:
- 1.[链表返回倒数k个节点OJ链接](https://leetcode.cn/problems/kth-node-from-end-of-list-lcci/description/)
- 2.[ 链表的回文结构](https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking)
- 3.[ 两个链表,找出它们的第一个公共结点](https://leetcode.cn/problems/intersection-of-two-linked-lists/description/)
1.链表返回倒数k个节点OJ链接
这道题的解法有很多,可以通过先遍历一遍来获取链表长度,然后通过将倒数k个节点转换为正数k个节点来达到目的
但是如果我们要求这道题的空间复杂度为O(1),且只能遍历一遍链表,那么该如何解题呢?
我们可以通过创建两个指针,
一个指针先走k步然后让两个指针一起向前走,
如果先走的指针到达末尾
那么后走的就是我们要找的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k) {
struct ListNode *p1 = head, *p2 = head;
while (k--) {
p1 = p1->next;
}
while (p1) {
p1 = p1->next;
p2 = p2->next;
}
return p2->val;
}
2. 链表的回文结构
判断回文结构实际上就是判断这个链表是不是对称的
那么我们如何去判断它是不是对称的呢
它是一个单链表要注意就不能让一个指针从后向前
那么要想比较数字,就只能将后半部分的链表进行反转,在从前向后遍历
那么问题就是找到链表的中间节点,和进行链表的反转两个问题了
这两个问题我们之前都处理过:C语言简单的数据结构:单链表的有关算法题(1)
1.链表的反转:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
//判空
if(head == NULL)
{
return head;
}
ListNode* n1,*n2,*n3;
n1 = NULL;
n2 = head;
n3 = head->next;
while(n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3)//如果n3不为NULL就继续向下
n3 = n3->next;
}
return n1;
}
2.链表的中间节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
ListNode* fast = head;
ListNode* slow = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
3.判断部分
由于我们并没有对前半部分的链表进行修改,所以2后面仍然指向原有的位置
那么我们时偶数个可以直接判空
奇数个时2指向的数和逆置后的来链表2的数是一样的
我们也可以用同样判空的条件
bool chkPalindrome(ListNode* A) {
// write code here
ListNode* mid = middleNode(A);
ListNode* rmid = reverseList(mid);
while (rmid && A) {
if (rmid ->val != A->val) {
return false;
}
rmid = rmid->next;
A = A->next;
}
return true;
}
最后总结到一起:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
if (head == NULL) {
return head;
}
ListNode* n1, *n2, *n3;
n1 = NULL;
n2 = head;
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) {
ListNode* fast = head;
ListNode* slow = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
// write code here
ListNode* mid = middleNode(A);
ListNode* rmid = reverseList(mid);
while (rmid && A) {
if (rmid ->val != A->val) {
return false;
}
rmid = rmid->next;
A = A->next;
}
return true;
}
};
3. 两个链表,找出它们的第一个公共结点
这道题一共有两个问题:
- 判断两个链表是否相交
- 找到相交节点
1.判断两个链表是否相交
这里我们仅仅需要确定最后一个节点的地址是一样的
就可以确定两个链表一定是相交的
这时我们可以写出代码:
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
struct ListNode* cur1 = headA;
struct ListNode* cur2 = headB;
while (cur1->next) {
cur1 = cur1->next;
}
while (cur2->next) {
cur2 = cur2->next;
}
if (cur1 != cur2) {
return NULL;
}
}
2.2. 找到相交节点
这个问题我们可以通过两个相同的指针同时走来判断
如何同时走呢?
这里刚好一些数学关系:
用两个链表的长度相减 就是长的链表指针应当先走的距离
然后让两个指针一起走 走到第一个地址一样的地方就是相交点
这里的判断可以使用假设法:
假设len1为长链表
如果len1的长度小于len2的长度
就将两个对调 而我们仅需用控制shortList和longList即可
int a = abs(len1 - len2);
struct ListNode *longList = headA, *shortList = headB;
if (len2 > len1) {
shortList = headA;
longList = headB;
}
总结到一起:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
struct ListNode* cur1 = headA;
struct ListNode* cur2 = headB;
int len1 = 1, len2 = 1;
while (cur1->next) {
cur1 = cur1->next;
++len1;
}
while (cur2->next) {
cur2 = cur2->next;
++len2;
}
if (cur1 != cur2) {
return NULL;
}
int a = abs(len1 - len2);
struct ListNode *longList = headA, *shortList = headB;
if (len2 > len1) {
shortList = headA;
longList = headB;
}
while (a--) {
longList = longList->next;
}
while (longList != shortList) {
longList = longList->next;
shortList = shortList->next;
}
return longList;
}