设置虚拟头节点的好处
1就是不用另外处理头节点比如在进行删除操作时,最后返回dummyHead→next即可
2就是最后返回链表用的,意思就是保存一下链表的“入口”(因为链表在操作过程中需要改变指向,操作完毕的时候指针可能已经指到最后一个元素了,如果返回房当前这个指针显然是不恰当的,而这个哨兵节点的next恰好是指向操作完毕了的链表的头节点,返回它即可)
24. 两两交换链表中的节点
使用虚拟头节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//虚拟头节点法(记得删除虚拟头节点),操作节点地址而不是数值
//因为要保留head地址所以用虚拟头节点
struct ListNode* swapPairs(struct ListNode* head) {
typedef struct ListNode ListNode;
//如果头节点不存在或头节点的下一个节点不存在。此时不需要交换,直接返回head
if(!head || !head->next) return head;
ListNode* shead;
shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
ListNode* cur=shead;
while(cur->next&&cur->next->next){//如果没有第二个节点就直接return
//交换
//temp和temp1分别是需要交换一组节点中的原第一个和原第二个节点
ListNode* temp=cur->next;
ListNode* temp1=temp->next;
cur->next=cur->next->next;
temp->next=temp->next->next;
temp1->next=temp;
//移动指针
cur=cur->next->next;
}
ListNode* result=shead->next;
free(shead);
return result;
}
交换数值的暴力解法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//直接交换数值
struct ListNode* swapPairs(struct ListNode* head) {
typedef struct ListNode ListNode;
ListNode* cur=head;
while(cur&&cur->next){//如果没有第二个节点就直接return
//交换再递进
ListNode* temp=cur->next;
int x=cur->val;
cur->val=temp->val;
temp->val=x;
cur=temp->next;
}
return head;
}
19.删除链表的倒数第N个节点
19. Remove Nth Node From End of List
快慢指针法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//使用快慢双指针法,一次扫描实现
//需要注意head==NULL或n大于链表长度
//fast最后也只能为尾结点,fast=fast->next不能为NULL
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//先让快节点走n步,使得快慢节点相差n步(倒数第一和倒数第n+1{其前一步}相差n步)
//使得快节点到倒数第一时,慢节点为倒数第n+1{其前一步}
typedef struct ListNode ListNode;
ListNode* shead;
shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
if(head){
ListNode* slow=shead;
ListNode* fast=shead;
for(int i=0;i<n;i++){
if(fast&&fast->next){
fast=fast->next;//fast最后也只能为尾结点,fast=fast->next不能为NULL
}else{
head=shead->next;
free(shead);
return head;
}
}
//快节点到倒数第一时,慢节点为倒数第n+1{其前一步}
while(fast->next){
fast=fast->next;
slow=slow->next;
}
//移除slow后一位即倒数第n
ListNode* temp=slow->next;
slow->next=slow->next->next;
free(temp);
}
head=shead->next;
free(shead);
return head;
}
暴力解法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//使用虚拟头节点的暴力解法,无需单独处理头指针的移除
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//先得到链表大小再回溯
typedef struct ListNode ListNode;
ListNode* shead;
shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
ListNode* tail=head;
int i=0;
int j=0;
while(tail){
tail=tail->next;
i++;
}
//找到要移除的第i-n+1元素然后使前节点的next等于后节点地址即可
//如果第i-n+1元素不存在就不操作
//如果移除头指针用虚拟头节点的话就无需单独处理
if(i-n+1>=1){
ListNode* cur=shead;
for(j=0;j<i-n;j++){//从第0个元素虚拟头指针到第i-n+1元素的前一个元素
cur=cur->next;
}
ListNode* temp=cur->next;
cur->next=cur->next->next;
free(temp);
}
return shead->next;
}
面试题 02.07. 链表相交(同:160.链表相交)
面试题 02.07. Intersection of Two Linked Lists LCCI
思路:先末尾对齐再移动寻找相交处
(末尾对齐即得到两者长度之差再移动长链表指针和短链表头指针对齐,寻找相交处即curA和curB同时后移看什么时候地址相同)
末尾对齐法一
法一:末尾对齐时把长链表的头指针命名为curA指针,可以仅写一遍移动指针的代码即只移动curA
之前报错原因:重复赋值两链表长度差gap为int,去掉后一个int赋值就好了
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//法一:末尾对齐时把长链表的头指针命名为curA指针,可以仅写一遍移动指针的代码即只移动curA
//(之前报错原因:重复赋值两链表长度差gap为int,去掉后一个int赋值就好了)
//思路:先末尾对齐再移动寻找相交处
//(末尾对齐即得到两者长度之差再移动长链表指针和短链表头指针对齐,寻找相交处即curA和curB同时后移看什么时候地址相同)
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
typedef struct ListNode ListNode;
ListNode* curA=NULL;
ListNode* curB=NULL;
int lenA=0;
int lenB=0;
curA=headA;
curB=headB;
while(curA){
lenA++;
curA=curA->next;
}
while(curB){
lenB++;
curB=curB->next;
}
//法一:末尾对齐时把长链表的头指针命名为curA指针,可以仅写一遍移动指针的代码即只移动curA
int gap=0;
if(lenA>lenB){
curA=headA;
curB=headB;
// int gap=lenA-lenB;
gap=lenA-lenB;//之前报错原因:重复赋值两链表长度差gap为int,去掉后一个int赋值就好了
}else{
curA=headB;
curB=headA;
// int gap=lenB-lenA;
gap=lenB-lenA;//之前报错原因:重复赋值两链表长度差gap为int,去掉后一个int赋值就好了
}
while(gap--) curA=curA->next;
while(curA!=NULL&&curB!=NULL){
if(curA==curB){
return curA;
}
curA=curA->next;
curB=curB->next;
}
return NULL;
}
末尾对齐法二
法二:末尾对齐时通过判断AB哪个长来判断移动curA还是curB
之前报错原因:仅重新赋值需要移动的指针回起点即curA=headA或curB=headB,而没有将另一个也移动回起点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//法二,末尾对齐时通过判断AB哪个长来判断移动curA还是curB
//(之前报错原因:仅重新赋值需要移动的指针回起点即curA=headA或curB=headB,而没有将另一个也移动回起点)
//思路:先末尾对齐再移动寻找相交处
//(末尾对齐即得到两者长度之差再移动长链表指针和短链表头指针对齐,寻找相交处即curA和curB同时后移看什么时候地址相同)
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
typedef struct ListNode ListNode;
ListNode* curA=NULL;
ListNode* curB=NULL;
int lenA=0;
int lenB=0;
curA=headA;
curB=headB;
while(curA){
lenA++;
curA=curA->next;
}
while(curB){
lenB++;
curB=curB->next;
}
//法二,末尾对齐时通过判断AB哪个长来判断移动curA还是curB
//之前报错原因:仅重新赋值需要移动的指针回起点即curA=headA或curB=headB,而没有将另一个也移动回起点
if(lenA>lenB){
curA=headA;
curB=headB;
//长链表指针和短链表头指针对齐,即移动lenA-lenB次
for(int i=0;i<lenA-lenB;i++){
curA=curA->next;
}
}else{
curA=headA;
curB=headB;
//长链表指针和短链表头指针对齐,即移动lenA-lenB次
for(int i=0;i<lenB-lenA;i++){
if(curB->next){
curB=curB->next;
}
}
}
while(curA!=NULL&&curB!=NULL){
if(curA==curB){
return curA;
}
curA=curA->next;
curB=curB->next;
}
return NULL;
}
范例
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//先末尾对齐再移动寻找相交处
//末尾对齐即得到两者长度之差再移动长链表指针和短链表头指针对齐,寻找相交处即curA和curB同时后移看什么时候地址相同
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
typedef struct ListNode ListNode;
ListNode *l = NULL, *s = NULL;
int lenA = 0, lenB = 0, gap = 0;
// 求出两个链表的长度
s = headA;
while (s) {
lenA ++;
s = s->next;
}
s = headB;
while (s) {
lenB ++;
s = s->next;
}
// 求出两个链表长度差
if (lenA > lenB) {
l = headA, s = headB;
gap = lenA - lenB;
} else {
l = headB, s = headA;
gap = lenB - lenA;
}
// 尾部对齐
while (gap--) l = l->next;
// 移动,并检查是否有相同的元素
while (l) {
if (l == s) return l;
l = l->next, s = s->next;
}
return NULL;
}
142.环形链表II
思路:设置快慢两个指针,fast每次移动两步,slow每次移动一步,最后在环中相遇则证明有环否则无环,2(x+y)=x+y+n(z+y),求x则为x=n(z+y)-y=(n-1)y+nz=(n-1)(y+z)+z,即如果index1指针从相遇节点出发,index2指针从头节点出发,均每次移动一步,最终相遇点会在环形入口节点即距离头节点为x个节点处,此时index1比index2多走((n-1)圈+z-x),即先得到fast和slow相遇点,再得到index1和index2相遇点即可得到环形入口节点x
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//快慢双指针法
//设置快慢两个指针,fast每次移动两步,slow每次移动一步,最后在环中相遇则证明有环否则无环
//如果index1指针从相遇节点出发,index2指针从头节点出发,均每次移动一步,最终相遇点会在环形入口节点
struct ListNode *detectCycle(struct ListNode *head) {
typedef struct ListNode ListNode;
ListNode* fast=head;
ListNode* slow=head;
while(fast&&fast->next){
fast=fast->next->next;
slow=slow->next;
if(fast==slow){
ListNode* index1=fast;
ListNode* index2=head;
//fast和slow相遇已经证明有环,所以不需要再判断是否会遇到NULL
while(index1!=index2){
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return NULL;
}