前言
今天链表的内容突出一个注意细节,判空条件,头节点是否为空等等。采用虚拟头节点可以方便链表进行更改,还需要学会使用临时变量。
Leetcode 24 两两交换链表中的节点
题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/
代码随想录题解:代码随想录 (programmercarl.com)
思路:交换逻辑本身不难,但就是因为要对很多节点进行操作所以很麻烦,采用临时变量和虚拟头节点会简单很多。
代码:
struct ListNode* swapPairs(struct ListNode* head) {
typedef struct ListNode ListNode;
ListNode *p;
p=(ListNode*)malloc(sizeof(ListNode));
ListNode *temp;
temp=(ListNode*)malloc(sizeof(ListNode));
p->next=head; //虚拟头节点
temp=p; //从虚拟头节点开始
while(temp->next!=NULL&&temp->next->next!=NULL)
{
ListNode *temp1=temp->next;
ListNode *temp2=temp->next->next;
ListNode *temp3=temp->next->next->next;//三个临时变量
temp->next=temp2;
temp->next->next=temp1;
temp->next->next->next=temp3; //交换逻辑
temp=temp->next->next;
}
return p->next;
}
Leetcode 19 删除链表的倒数第N个节点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/
代码随想录题解:代码随想录 (programmercarl.com)
思路:快慢指针,让快指针在开始前多走n个,这样快指针在走完整个链表时慢指针正好比快指针慢n个也就是指向倒数第n个。但要注意需要让fast多走一个,这样会使slow指向倒数第n-1个节点,方便删除。
代码:
struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
struct ListNode *fast;
struct ListNode *slow;
struct ListNode *i;
i=(struct ListNode*)malloc(sizeof(struct ListNode));
i->next=head;
fast=i;
slow=i;//虚拟头节点
for(int i=0;i<n;i++)
{
fast=fast->next;
}
fast=fast->next; //注意多走一次
while(fast!=NULL)
{
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
head = i->next;
return head;
}
02.07 链表相交
题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/
代码随想录题解:代码随想录 (programmercarl.com)
思路:这个题之所以要进行截断操作是为了给出判断条件,这个判断条件就是两个指针相等,那么只有两个指针相等从相同的位置出发才能实现(想象成交汇的火车道?)。或者说长的里面从某个位置开始都是短的,那么这个结束位置肯定要延申到结尾,肯定不可能从前面开始找。
代码:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
struct ListNode* p;
struct ListNode *q;
int count1=0,count2=0;
p=headA;
q=headB;
while(p!=NULL)
{
p=p->next;
count1++; //统计长度
}
while(q!=NULL)
{
q=q->next;
count2++;
}
p=headA;
q=headB;
if(count1>count2)//判断哪个长
{
for(int i=0;i<(count1-count2);i++)
{
p=p->next; //截断
}
}
if(count1<count2)
{
for(int i=0;i<(count2-count1);i++)
{
q=q->next;
}
}
while(q!=NULL&p!=NULL) //找
{
if(p==q)
{
return q;
}
p=p->next;
q=q->next;
}
return NULL;
}
Leetcode 142 环形链表II
题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/
代码随想录题解:代码随想录 (programmercarl.com)
思路:想象成跑圈,看跑得快的人能不能套圈跑得慢的人。要注意的是快指针的速度一定要比慢指针小一,这样才能保证追的时候是一个节点一个节点的追,不然会发生永远也进入不了某些节点的情况。其他的数学关系见图,比较难理解的是为什么慢指针只转一圈,因为相同时间下快指针比慢指针多走一倍距离,但二者起始距离差小于等于一倍,所以一圈肯定能追上。
代码:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast=head;
struct ListNode *slow=head;
while(fast!=NULL&&fast->next!=NULL) //注意条件,因为一次走两格
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
struct ListNode *p=fast;
struct ListNode *q=head;
while(p!=q)
{
p=p->next;
q=q->next;
}
return p;
}
}
return NULL;
}
C++ 指针和引用
指针是一个变量类型,存放的是某个变量的地址,可变;引用是变量的别名,不可变且必须初始化。指针主要用于分配空间,引用在实现多态时用的多。
C++ inline内联函数
内联函数一般用于声明一个短小精干的函数,相比于正常的函数声明节省了切换栈寄存器,压栈出栈等过程,减小了程序的开销。内联函数的实现主要是在编译阶段将函数体嵌入到所调用的位置。相比于define宏定义一个函数,使用内联函数的优点在于可以自动检测其类型的正确性。
总结
今天的四道算法题都有要注意的细节,明天算法休息一天但要开始学习STL的源码部分,准备在学习完源码部分自己实现一个mini STL,既可以帮助理解也可以作为项目经历。