一共记录单链表操作的七个问题:单链表逆序,求倒数第K个节点,判断单链表是否有环,并求环的入口节点;合并两个有序单链表;判断两个链表是否相交,并求相交节点;删除链表倒数第N个节点;旋转链表。
问题1:单链表逆序
双指针思想:将head置空,然后用两个指针P和Q,P指向链表的第一个节点,q指向p的下一个节点(防止头插后,后面的节点丢失);然后采用头插,让p->next = head->next; head->next = p; p = q;
void ReverseLink(Clink &link)
{
Node *head = link.head_;
Node *p = head->next_;
if (p == nullptr)
{
return;
}
head->next_ = nullptr;
while (p != nullptr)
{
Node *q = p->next_;
// 头插
p->next_ = head->next_;
head->next_ = p;
p = q;
}
}
问题2:单链表求倒数第K个节点
第一步:假设p q同时指针head, p先移动正数第k个节点;
第二步:这时候p 和 q同时移动,当p等于空时,q指向的就是倒数第k个节点;
梳理正数k和倒数k关系:假设K=1,即求倒数第一个节点,用两个指针p和q,并且p和q间距是1,p先走1步,然后pq同时向后走,当p为空的时候,q指向倒数第1个节点;这种方法本质上就是p和q之间的差距是K,这样p是空时候,q恰好是倒数第K个节点。
单链表中判断p和p->next时候的区别?
假设p指向当前节点,如果判断p->next != nullptr ,会漏掉最后一个节点,因为最后一个节点的下一个节点是空的;同时如果是双指针,q也会少走一个节点,
假设p指向当前节点,如果p != nullptr,那么会将从p到最后的节点都输出。
// 求链表倒数第k个节点的值
bool GetLastKNode(Clink &link, int k, int &val)
{
Node *head = link.head_;
Node *pre = head;
Node *p = head;
if (k < 1) // 程序的健壮性判断
{
return false;
}
// p 先指向正数第k个节点
for (int i = 0; i < k; i++)
{
p = p->next_;
if (p == nullptr) // 判断如果正数第k个不存在,那么倒数第k个必不存在
{
return false;
}
}
// while的判断条件,当P!=nullptr时候,q指向的是倒数第K个节点;当判断条件是p->next != nullptr时候,q指向的是倒数第k+1个节点
// 同时移动p pre 直到p为空
while (p != nullptr)d
{
p = p->next_;
pre = pre->next_;
val = pre->data_;
}
return true;
}
问题3:合并两个有序单链表
假定让两个链表合并到链表1中,一共三个节点:last,p ,q;其中, last始终指向新链表的最后一个节点,初始时指向head1节点;p指向head1.next;q指向head2.next; 循环比较p和q,如果p<q,那么last.next = p; p = p.next;
当p为空,q不为空时候,last.next = q;
当q为空,p不为空时候,last.next = p;
void MergeLink(Link &link1, Link &link2)
{
Node *last = link1.head;
Node *p = link1.head->next_;
Node *q = link2.head->next_;
// last = nullptr;
link2.head->next_ = nullptr;
while (p != nullptr && q != nullptr)
{
if (p->data_ < q->data_)
{
last->next_ = p;
p = p->next_;
}
else
{
last->next_ = q;
q = q->next_;
}
last = last->next_;
}
if (p == nullptr && q !=nullptr)
{
last->next_ = q;
}
if (p != nullptr && q == nullptr)
{
last->next_ = p;
}
}
问题4:单链表判断是否有环? 求环的入口节点
见上一篇笔记。
问题5:判断两个单链表是否相交,求相交节点
思路:两个指针分别指向两个链表,第一个指针走完第一个链表,并计算链表个数,假设为num1;第二个同理,数量为Num2;num1-num2得到一个差值=dif;
让长链表先走dif个元素,然后同时开始走,每走一步都判断是否相等,相等的节点就是两个链表相交的节点。
// 判断两个节点是否相交,如果相交,返回相交节点的值
bool IsLinkHasMerge(Node *head1, Node *head2, int &val)
{
int cnt1 = 0, cnt2 = 0; // 记录两个链表长度
Node *p = head1->next_;
Node *q = head2->next_;
while (p != nullptr)
{
cnt1++;
p = p->next_;
}
while (q != nullptr)
{
cnt2++;
q = q->next_;
}
p = head1;
q = head2;
if (cnt1 > cnt2)
{
// 第一个链表长
int offset = cnt1 - cnt2;
while (offset-- > 0)
{
p = p->next_;
}
}
else
{
int offset = cnt2 - cnt1;
while (offset-- > 0)
{
q = q->next_;
}
}
while (p != nullptr && q != nullptr)
{
if (p == q)
{
val = p->data_;
return true;
}
p = p->next_;
q = q->next_;
}
return false;
}
int main()
{
Node head1;
Node n1(1), n2(11), n3(22), n4(33), n5(44);
head1.next_ = &n1;
n1.next_ = &n2;
n2.next_ = &n3;
n3.next_ = &n4;
n4.next_ = &n5;
Node head2;
Node n6(33);
head2.next_ = &n6;
n6.next_ = &n3;
int val;
if (IsLinkHasMerge(&head1, &head2,val))
{
cout << "链表相交节点是" << "" << val << endl;
}
system("pause");
return 0;
}
问题6:删除链表的倒数第 N 个结点
倒数第N个节点,应该立即想到问题2;
可以看出这是一个不带头结点的链表,遇到这种情况,为其添加一个头结点。
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode head_;
head_.next = head;
struct ListNode *p = &head_;
struct ListNode *q = &head_;
// p 走 n
for (int i = 0; i < n; i++)
{
if (p == NULL)
{
return head;
}
p = p->next;
}
// q指向了倒数n+1个节点
while (p->next != NULL)
{
q = q->next;
p = p->next;
}
// 判断如果p是第一个节点
struct ListNode *del = q->next;
q->next = q->next->next;
free(del);
return head_.next;
}
问题7:旋转链表61
思路:旋转链表,就是向后旋转链表。
第一步:求链表总个数n;
第二步:p q 指向head, p先移动到整数第k个节点,然后q开始移动;当判断条件是p->next != nullptr,q指向的就是倒数第K+1个节点。
第三步:p->next = head; head = q->next; q->next = nullptr;
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
ListNode *p = head;
ListNode *q = head;
if (head == nullptr || k ==0)
{
return head;
}
// 第一步:求链表的个数
int count = 0;
for (ListNode *p = head; p != nullptr; p=p->next)
{
count++ ;
}
k = k % count;
// 第二步:p先移动到正数第k个节点,然后q开始移动
for (int i = 0; i < k; i++)
{
p = p->next;
/* code */
}
// q指向了倒数第k+1个节点
while (p->next != nullptr)
{
p = p->next;
q = q->next;
/* code */
}
p->next = head;
head = q->next;
q->next = nullptr;
return head;
}
};