代码随想录训练营第四天打卡|24.两两交换链表中的结点 19.删除链表的倒数第N个结点 160.链表相交 142.环形链表||

24.两两交换链表中的结点

1.朴素思想:找到需要交换结点的前一个结点然后利用中间变量进行交换。建议像我一样的初学者不必节省变量,可以多写几行代码增加可读性,核心是统一处理逻辑,便于理解,本题中就使用了虚拟头结点,统一了每次交换的处理。

第一次处理前

第一次交换后

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* swapPairs(struct ListNode* head) {
    typedef struct ListNode ListNode;//重定义结构体
    ListNode * shead=(ListNode*)malloc(sizeof(ListNode));//创建一个虚拟头结点指向真正的头结点
    shead->next=head;
    ListNode* cur,*temp,*temp1;//cur表示当前结点,知道了cur的位置才能交换其之后两个结点
    cur=shead;//用cur代替虚拟头结点进行交换结点操作,因为最后还需要用虚拟头结点返回新链表头结点
    while(cur->next!=NULL&&cur->next->next!=NULL){//循环终止条件分别表示偶数、奇数结点两种情况
        temp=cur->next;//交换第一步断开了指针,之后还要用到该结点,所以用一个临时指针保存一下
        temp1=cur->next->next->next;
        cur->next=temp->next;//具体交换结点代码
        cur->next->next=temp;
        temp->next=temp1;
        cur=cur->next->next;//cur指向下两个需要交换的结点,开始下一轮交换
    }
    head=shead->next;//用cur代替shead进行交换操作就是为了方便最后返回头结点
    free(shead);//释放虚拟头结点
    return head;
}

2.递归写法,递归代码简单却不易理解,但核心其实还是交换逻辑,注意理解递归函数实现的功能。(注:可通过2个结点4个结点这样简单的例子模拟递归算法过程、方便理解)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* swapPairs(struct ListNode* head) {//递归写法
    if(head==NULL||head->next==NULL)//头结点或头结点的下一个结点为空
    return head;//表明结点只有0个或1个不用交换,直接返回头结点
    struct ListNode* nhead=head->next;//新头结点是头结点的下一个结点
    head->next=swapPairs(nhead->next);//交换之后的头结点指向之后完成递归链表的头结点
    nhead->next=head;//新头结点成为真正的头结点
    return nhead;
}
1.双指针法经典题,设置一快一慢两个指针。删除倒数第N个结点需要找到第倒数N+1个结点才能方便操作,所以选择先让快指针移动N+1步,然后快慢指针同时移动,当快指针遍历结束时慢指针指向的就是倒数第N+1个结点,之后进行常规删除操作。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {//双指针法
    struct ListNode* shead=(struct ListNode*)malloc(sizeof(struct ListNode));
    shead->next=head;//创建一个虚拟头结点指向当前头结点
    struct ListNode* quick=shead;//采用快慢指针法
    struct ListNode* slow=shead;
    for(int i=0;i<n+1;i++){//要删除倒数第n个结点需要找到第倒数n+1个结点
        quick=quick->next;//先让快指针跑n+1步
    }
    while(quick!=NULL){//当快指针遍历链表结束时,慢指针就指向倒数n+1个结点
        quick=quick->next;
        slow=slow->next;
    }
    struct ListNode * temp=slow->next;//中间变量保存需要删除的结点
    slow->next=temp->next;//常规的删除操作,记得释放内存空间
    free(temp);
    head=shead->next;
    free(shead);
    return head;
}

160.链表相交

1.第一反应408的数据结构真题,朴素思想是先把两个链表对齐,然后同步遍历,找到第一个相交结点然后返回,注意有无虚拟头结点代码区别。(注:相交是指指针指向同一片地址空间。)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int m=length(headA);
    int n=length(headB);
    for(headA;m>n;m--)//链表对齐
    headA=headA->next;
    for(headB;n>m;n--)
    headB=headB->next;
        while(headA!=NULL&&headA!=headB){//调整之后两个链表长度相同
            headA=headA->next;//所以headA!=NULL与headB!=NULL等价
            headB=headB->next;
        }
        return headA;
}
int length(struct ListNode* str){//链表长度计算函数
    int len=0;
    while(str){
         len++;
         str=str->next;
    }
    return len;
}

2.之后又去看了官方的另一种解法,非常有意思,很难想到。核心思想是:两个链表同时遍历,遍历完自己然后交替遍历对方。若有相交结点,停止时就是相交结点的起点,若不相交则会一直遍历结束返回NULL。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA==NULL||headB==NULL)//双指针法,A空或B空则不可能相交返回空指针
    return NULL;
    struct ListNode*PA=headA,*PB=headB;
    while(PA!=PB){//核心逻辑:A和B交替遍历,停止时就是相交结点或者空结点
        PA=PA==NULL? headB :PA->next;
        PB=PB==NULL? headA :PB->next;
    }
    return PA;
}

142.环形链表||

1.第一反应是用额外空间记录访问次数,之后进行遍历找到第一个访问第二次的结点。奈何本人水平太差以前只做过用额外数组记录访问次数的问题,不知如何下手。(看了题解之后才发现是用哈希表实现,C语言实现哈希表较复杂,遂放弃……)

2.用数学方法构造模型,然后用快慢双指针实现(也不会……看视频勉强理解,本人水平有限,精力也不支持写出更深刻的理解,有兴趣的伙伴可以去看Carl老师的视频讲解,讲的很棒)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    typedef struct ListNode ListNode;
    ListNode* slow=head,*fast=head;//采用快慢双指针
    while(fast!=NULL&&fast->next!=NULL){
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast){//相遇时由数学关系可以得出slow、fast、head距入环结点相等
            ListNode* index1=head;
            ListNode* index2=slow;
            while(index1!=index2){
                index1=index1->next;
                index2=index2->next;
            }
            return index1;
        }
    }
    return NULL;//无环则返回NULL
}

今日总结:已经开始恐惧哈希表了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值