笔记
1.移除链表元素
这道题目有什么思路吗?
画图
代码实现
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode*prev=NULL,*cur=head;
while(cur!=NULL)
{
if(cur->val==val)
{
if(cur==head)
{
cur=head->next;
free(head);
head=cur;
}
else
{
prev->next=cur->next;
free(cur);
cur=prev->next;
}
}
else
{
prev=cur;
cur=cur->next;
}
}
return head;
}
2.反转链表
-
-
- 这里是列表文本
-
代码实现1:利用头插的方法进行巧妙的解题,但是他需要创建新的newnode,空间复杂度较大
代码实现2:直接暴力反转
用三个指针n1 n2 n3进行暴力反转
n1指向n2的前驱,解决找前一个节点的问题 n2则代替head的节点进行迭代,n3指向n2的后驱,来控制循环的中断
画图
实现:
3.寻找链表的中间节点
题目:![输入图片说明](https://img-blog.csdnimg.cn/img_convert/eae7fa45a92f5f68c1277f4ef6350841.png)
代码实现:(利用快慢指针步长差距来解决问题)
4.寻找链表中的倒数第k个节点
题目:
特殊情况需要注意:k哪些情况下是返回空指针的?
a.当这个链表为空的时候,或者k<=0的时候,是没有倒数元素的
b.当k>n的时候,这种正着数的 元素遍历算法就会失效
思路1:通过倒数第k个求出正数是第(n-k)个元素,假设链表为 1 2 3 4 5 倒数第二个则是4 ,那么n为链表元素个数5,正数则需要往后移动(5-2)个指针,找到4
代码实现:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(k<=0||!pListHead)
{
return NULL;
}
int n=0;
ListNode* cur=pListHead;
while(cur)
{
cur=cur->next;
n++;
}
if(n<k)
{
return NULL;
}
n-=k;
while(n--)
{
pListHead=pListHead->next;
}
return pListHead;
}
};
思路二:假设链表为 1 2 3 4 5 ,需要找到倒数第二个元素 4 ,则设置快慢指针,假设在链表5个元素 1 2 3 4 5 的情况下 ,假设k=2,则4 为所求,slow和fast的组合,让fast比slow先走k步 (1 3)(2 4)(3 5)(4 null),当fast指针移动到null时,终止循环,slow指向的就是倒数第k个元素。如图所示
代码实现:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(k<=0||!pListHead)
{
return NULL;
}
ListNode*slow=pListHead;
ListNode*fast=pListHead;
while(k--)
{
if(fast)
{
fast=fast->next;
}
else
{
return NULL;
}
}
while(fast!=NULL)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
};
5.合并两个有序链表
题目:
需要考虑的问题:
1.两个链表都为空的情况
2.两个链表其中一个为空
3.其中任意一个链表先遍历完
思路一:利用尾部插入,没什么技巧
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if(l1==NULL)
{
return l2;
}
if(l2==NULL)
{
return l1;
}
struct ListNode* newhead=NULL,*newtail=NULL;
if(l1->val>=l2->val)
{
newhead=newtail=l2;
l2=l2->next;
}
else
{
newhead=newtail=l1;
l1=l1->next;
}
while(l1&&l2)
{
if(l1->val>l2->val)
{
newtail->next=l2;
newtail=l2;
l2=l2->next;
}
else
{
newtail->next=l1;
newtail=l1;
l1=l1->next;
}
}
if(l1)
newtail->next=l1;
if(l2)
newtail->next=l2;
return newhead;
}
思路二:利用迭代在原链表中进行,减少空间复杂度为o(1),创建哨兵位的头节点
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if(l1==NULL)
{
return l2;
}
if(l2==NULL)
{
return l1;
}
struct ListNode* newhead=NULL,*newtail=NULL;
newhead=newtail=(struct ListNode*)malloc(sizeof(struct ListNode));
//记得malloc要带struct,不然会越界
while(l1&&l2)
{
if(l1->val>l2->val)
{
newtail->next=l2;
newtail=l2;
l2=l2->next;
}
else
{
newtail->next=l1;
newtail=l1;
l1=l1->next;
}
}
if(l1)
newtail->next=l1;
if(l2)
newtail->next=l2;
struct ListNode* list=newhead->next;
free(newhead);
return list;
}
6.链表分割
思路:用lessHead greatHead 分成两个链表,在将greatHead尾插到lessHead上
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
ListNode*lessHead,*lessTail,*greatHead,*greatTail;
lessHead=lessTail=(struct ListNode*)malloc(sizeof(struct ListNode));
lessTail->next=NULL;
greatHead=greatTail=(struct ListNode*)malloc(sizeof(struct ListNode));
greatTail->next=NULL;
struct ListNode* cur=pHead;
while(cur)
{
if(cur->val>x)
{
greatTail->next=cur;
greatTail=cur;
}
else
{
lessTail->next=cur;
lessTail=cur;
}
cur=cur->next;
}
lessTail->next=greatHead->next;
greatTail->next= NULL;
struct ListNode* newHead=lessHead->next;
free(lessHead);
free(greatHead);
return newHead;
// write code here
}
};
链表的回文结构
思路:1.先用快慢指针得出链表的中间元素所在
2.题目要求空间复杂度为o(1),所以只能递归的方式(双指针)将链表从中间位置以后进行逆转
3.从开始的head,命名为curA 和 逆置开始的节点 curB一一比较,从而得出结果
struct ListNode* midList(struct ListNode*head)
{
struct ListNode*slow=head,*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
struct ListNode* reverseList(struct ListNode *pos)
{
struct ListNode*pre=NULL,*cur=pos;
while(cur)
{
struct ListNode*next=cur->next;
cur->next=pre;
pre=cur;
cur=next;
}
return pre;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
struct ListNode* mid=midList(A);
struct ListNode* rHead=reverseList(mid);
struct ListNode* curA=A;
struct ListNode* curB=rHead;
while(curA&&curB)
{
if(curA->val!=curB->val)
{
return false;
}
else
{
curA=curA->next;
curB=curB->next;
}
}
return true;
}
};
7.相交链表的节点
思路:1.通过两个链表的尾节点判断是否相交
2.通过两个链表的步长差,让长的链表先走步长,再一一比较
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* tail1=headA;
struct ListNode* tail2=headB;
int len1=1;
while(tail1->next)
//为什么判断条件是tail->next 而不是tail
//因为我们需要得到tail,如果判断条件为tail,它总会变为null
{
tail1=tail1->next;
++len1;
}
int len2=1;
while(tail2->next)
{
tail2=tail2->next;
++len2;
}
if(tail1!=NULL&& tail2!=NULL &&tail1!=tail2)
{
return NULL;
}
int gap=abs(len1-len2);
//if(tail1!=NULL && tail2!=NULL && tail2->val==tail1->val)
//有交点呀
struct ListNode* longList=headA;
struct ListNode* shortList=headB;
if(len1<len2)
{
shortList=headA;
longList=headB;
}
while(gap--)
{
longList=longList->next;
}
while(longList!=shortList)
{
longList=longList->next;
shortList=shortList->next;
}
return longList;
}
9.环形链表1
思路:设置快慢指针
bool hasCycle(struct ListNode *head) {
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(slow==fast)
return true;
}
return false;
}
10.环形链表2
思路1:见图
利用公式推导出结论:
meetNode到入环点的距离 +(n-1)圈环周长 =head到入环点的距离
代码实现
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode*slow=head,*fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
{
struct ListNode* meetNode=slow;
while(meetNode!=head)
{
meetNode=meetNode->next;
head=head->next;
}
return meetNode;
}
}
return NULL;
}
思路二:将meetNode的节点 看成一个链表的起始点和尾节点的交点
11.赋值带随机指针的链表
思路:这道题最难的地方在于怎么得到每个指针的random
1.将copy节点复制其元素,插入到每个节点的next位置
2.将random定位到每一个指定元素的next位置就可以解决了
3.将新链表和原链表分离
代码如下:
struct Node* copyRandomList(struct Node* head) {
struct Node*cur=head;
while(cur)
{
struct Node*copy=(struct Node*)malloc(sizeof(struct Node));
copy->val=cur->val;
//依次插入copy节点
copy->next=cur->next;
cur->next=copy;
cur=copy->next;
}
cur=head;
while(cur)
{
struct Node* copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
cur=copy->next;
}
struct Node* copyHead=NULL,*copyTail=NULL;
cur=head;
while(cur)
{
struct Node*copy=cur->next;
//为什么要多次设置copy=cur->next;
struct Node*next=copy->next;
if(copyTail==NULL)
{
copyHead=copyTail=copy;
}
else
{
copyTail->next=copy;
copyTail=copy;
}
cur->next=next;
cur=next;
}
return copyHead;
}