写在最前面lol,为链表创建一个头结点的方法
eg:struct ListNode* l1,指针l1指向一个无头结点的链表
struct ListNode header;
header.next=l1;
l1=&header;
则,l1现在指向有头结点的链表了
另外,对于&&
part1&&part2,如果part1为0,则不再继续验证part2的真假
160. 相交链表
这道题,说来很伤心,我初试的原题,当时抽了,没写出来,结果在准备复试的时候再一次刷到了。
这里提供和王道上(先分别计算两个链表的长度,然后先遍历长的链表,直到两个链表剩下未遍历的长度同之后,两个链表同时遍历,直到所指向的值相同,或者任何一个链表到尽头为止)不同的方法:
设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表
A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *l1=headA,*l2=headB;
while(l1!=l2){
l1=(NULL!=l1)? l1->next:headB;
l2=(NULL!=l2)? l2->next:headA;
}
return l1;
}
206. 反转链表
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* p,*q,*pre=NULL;//pre为造的头结点
p=head;
while(p){
q=p->next;
p->next=pre;//注意此处为pre,和本来就带有头结点的单链表不同
pre=p;
p=q;
}
return pre;
}
21. 合并两个有序链表
值得注意的是,这两个链表都为有序的
我将L1做为返回链表,把L2的每一个值与L1中的值依次比较,找到插入点
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){//l1为被插入链表,l2为被遍历的链表
struct ListNode* t;
struct ListNode header;
header.next=l1;
l1=&header;
while(l2!=0){
while(l1->next!=0&&l1->next->val<l2->val)//呜呜,这个先后顺序也要讲究一下,要是第一个结点就为空就不行了
l1=l1->next;
t=l2;
l2=l2->next;
t->next=l1->next;
l1->next=t;
}
return header.next;
}
92. 反转链表 II
这道题也不难,就是指针指来指去有点昏。
思路就是把链表切成三段,然后再连起。
建议使用无头结单链表的方式进行逆置。
对于无头结单链表的总结:
- 新创的指针在逆置完成后,会指向新链表的的第一个结点
- 用来遍历原链表的指针,会指向原链表之后的那个结点
分析自我的错误原因:我之前使用的带头结单链表逆置的方式,故把需要逆置的第一个结点的前驱(pre)做为头结点。但是报错,欲使用空指针,原因如下:
- 因为我首先想到可能从第一个结点就开始逆置了,那这是pre=NULL;
- 但是在我逆置的时候,需要pre作为头结点,所以这时候就有以上的报错信息了
所以强烈建议使用无头结单链表的方式进行逆置!!!!
struct ListNode* reverseBetween(struct ListNode* head, int m, int n){
int len = n - m + 1;//计算出需要逆置的个数
struct ListNode *pre = NULL;
struct ListNode *res = head;
while(--m && head){
pre = head;
head = head->next;//将head向前移动到需要逆置的位置
}
struct ListNode *mod = head;
struct ListNode *new = NULL;
while(head && len){
struct ListNode *next = head->next;
head->next = new;//无头结点的单链表逆置
new = head;
head = next;
len--;//每完成一个,需要的个数减1
}
mod->next = head;//连接逆置段的尾部与原链表不需要逆置尾部
if(pre){
pre->next = new;//如果pre不为空,说明不是从第一个开始的,则将前半段不逆置的与逆置的链接起来
}else{
res = new;//说明从第一个开始,直接链头
}
return res;
}
面试题 02.02. 返回倒数第 k 个节点
不得不说的一句是,leetcode上给的单链表是 没有头结点的,没有头结点!!!没有头结点!!!我刚直接认为是有头结点的,一直报错(欲使用空指针)
然后解决此问,我用到了链表逆置,单链表有无头结点,操作方法不怎么一样哈。
我去参考了下别人怎么做的,是造一个结点来用
下面是我自己用图形来增强记忆
int kthToLast(struct ListNode* head, int k){//先头插,变倒数为正数
struct ListNode* p,*q,*pre=NULL;//pre为造的头结点
p=head;
while(p){
q=p->next;
p->next=pre;//注意此处为pre,和本来就带有头结点的单链表不同
pre=p;
p=q;
}
q=pre;
int count=1;
while(q){
if(count==k)
return q->val;
q=q->next;
count++;
}
return -1;
}
2. 两数相加
一遍AC的感觉有点爽,因为之前遇到过类似的问题,所以这次,每种情况都有考虑到哦。开森
int calculate(struct ListNode* p){
int length=0;
while(p){
length++;
p=p->next;
}
return length;
}
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){//在两数和用数组实现的基础上,用链表实现
//若有进位,则还需要申请新的结点挂在链表尾部
int carry=0,sum,length1=0,length2=0;
struct ListNode* p,*q;
//分别计算下这两个链表的长度,从而决定把最终结果存在哪个链表中
length1=calculate(l1);
length2=calculate(l2);
if(length1>length2){
p=l1,q=l2;
}else{
p=l2,q=l1;
}
struct ListNode* pre;
while(p&&q){
sum=p->val+q->val+carry;
carry=0;
if(sum>=10){
carry=1;
sum-=10;
}
p->val=sum;
pre=p;
p=p->next;
q=q->next;
}
if(carry==1&&p==NULL&&q==NULL){//进位挂尾结点
struct ListNode* t=(struct ListNode*)malloc(sizeof(struct ListNode));
t->val=1;
t->next=NULL;//尾结点的next记得赋值为空
pre->next=t;
}else if(p!=NULL){//长度不同的时候
while(p){
p->val+=carry;
carry=0;
if(p->val>=10){
carry=1;
p->val-=10;
}
pre=p;
p=p->next;
}
if(carry==1){
struct ListNode* t=(struct ListNode*)malloc(sizeof(struct ListNode));
t->val=1;
t->next=NULL;//尾结点的next记得赋值为空
pre->next=t;
}
}
return length1>length2?l1:l2;
}