zoukankan      html  css  js  c++  java
  • LeetCode——链表

    160. 相交链表

    题目描述:编写一个程序,找到两个单链表相交的起始节点。

    算法1:

    1. 先计算出两个链表的长度lenA和lenB,以及长度差diff
    2. 让指针p和q分别指向链表头部,让长链表先走过diff,使两链表的剩余长度相同
    3. 然后让p、q同时移动,相等时即为交点,或是同时到达结尾两值均为NULL
    struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
        struct ListNode *p=headA;
        struct ListNode *q=headB;
        int lenA=0,lenB=0;
        while(p){
            lenA++;
            p=p->next;
        }
        while(q){
            lenB++;
            q=q->next;
        }
        p=headA;
        q=headB;
        int diff=lenA-lenB;
        if(diff>0){
            while(diff--){
                p=p->next;
            }
        }else{
            diff=-diff;
            while(diff--){
                q=q->next;
            }
        }
        while(p!=q){
            p=p->next;
            q=q->next;
        }
        return q;
    }
    

    算法2:

    1. 将两个链表相接,即A->B,B->A
    2. 此时让p从A头部出发,q从B头部出发,最终p将指向B最后一个节点,q指向A最后一个节点,因为两指针都走过A+B的总长
    3. 如果A、B有相交点,则必然会有一段路是p、q共同走过
    4. 因此,如果有交点,则在终点前就有p==q,反之,则p、q只有在终点时才同为NULL
    struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
        struct ListNode *p=headA;
        struct ListNode *q=headB;
        if(p==NULL||q==NULL) return NULL;
        while(p!=q){
            p=p==NULL?headB:p->next;
            q=q==NULL?headA:q->next;
        }
        return p;
    }
    

    206. 反转链表

    题目描述:反转一个单链表

    算法1:迭代

    使用头插法的思想,分别用prev和curr两指针前两个元素

    struct ListNode* reverseList(struct ListNode* head){
        struct ListNode* curr=head;
        struct ListNode* prev=NULL;
        struct ListNode* tmp;
        while(curr){
            tmp=curr->next;
            curr->next=prev;
            prev=curr;
            curr=tmp;
        }
        return prev;
    }
    

    算法2:递归

    image-20200412135812459

    struct ListNode* reverseList(struct ListNode* head){
        if(head==NULL||head->next==NULL) return head;
        struct ListNode *p=reverseList(head->next);
        head->next->next=head;
        head->next=NULL;//避免产生循环
        return p;
    }
    

    21. 合并两个有序链表

    题目描述:将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

    算法1:迭代

    1. 设置一个头结点head和和一个尾指针res,初始时尾指针指向head
    2. 分别比较链表头结点处的值大小,较小的结点则接在头结点head后,并让res始终指向最后一个节点
    3. 最后将两链表剩余的部分接在最后
    4. 返回头结点的下一个结点
    struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
        struct ListNode *res=(struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode *head=res;
        while(l1&&l2){
            if(l1->val>l2->val){
                res->next=l2;
                l2=l2->next;
            }else{
                res->next=l1;
                l1=l1->next;
            }
            res=res->next;
        }
        res->next=l1==NULL?l2:l1;//最终两链表至少有一个为空,如果都为空,则res->next=NULL
        return head->next;
    }
    

    算法2:递归

    struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
        if(l1==NULL)
            return l2;
        if(l2==NULL)
            return l1;
        if(l1->val < l2->val){
            l1->next = mergeTwoLists(l1->next,l2);
            return l1;
        }else{
            l2->next = mergeTwoLists(l1,l2->next);
            return l2;
        }
    }
    

    83. 删除排序链表中的重复元素

    题目描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

    算法1:迭代

    1. 删除一个结点必须要知道该结点的前一个结点
    2. 从头开始用指针p遍历链表,在指针p与p->next都不为NULL的情况下比较所指结点的值
    3. 注意释放空结点
    struct ListNode* deleteDuplicates(struct ListNode* head){
        struct ListNode *p=head;
        while(p&&p->next){
            if(p->val==p->next->val){
                struct ListNode *tmp=p->next;
                p->next=p->next->next;
                free(tmp);
            }else{
                p=p->next;
            }
        }
        return head;
    }
    

    算法2:递归

    1. 递归终止条件:当前链表为空,或是只有一个元素
    2. 找返回值:返回给上层处理好的链表的头指针
    3. 本层中要完成的任务:从宏观上来看,如果当前结点的值与上层返回的头结点的值相等,则要删除当前的结点,即返回上一层的头结点,如果不等,则返回当前结点
    struct ListNode* deleteDuplicates(struct ListNode* head){
        if(head==NULL||head->next==NULL) return head;
        head->next=deleteDuplicates(head->next);
        if(head->val==head->next->val) head=head->next;
        return head;
    }
    

    19. 删除链表的倒数第N个节点

    题目描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。给定的 n 保证是有效的。

    算法1:快慢指针

    先构造一个头结点dummy,方便对特殊情况的处理,让快指针从head处先走n个结点,然后让慢指针从dummy处和快指针一起走,直到快指针到达链表结尾,此时慢指针所在的位置即为要删除结点的前一个结点

    总结:添加一个头结点,在第一个结点需要修改的情况下很好用

    struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
        struct ListNode *fast = head;//快指针
        struct ListNode *dummy = malloc(sizeof(struct ListNode));//头结点
        dummy->next=head;
        struct ListNode *slow = dummy;//慢指针
        while(n--){
            fast=fast->next;
        }
        while(fast){
            slow=slow->next;
            fast=fast->next;
        }
        struct ListNode *tmp = slow->next;
        slow->next=slow->next->next;
        free(tmp);
        return dummy->next;
    }
    

    算法2:递归

    思路:先递归到最后一个结点,在返回上一层的过程中,记录层数,如果为当前层数为n,则说明当前结点即为要删除的结点。

    struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
        static int curr=0;
        if(head==NULL) return NULL;
        head->next=removeNthFromEnd(head->next,n);
        curr++;
        if(curr==n) return head->next;
        return head;
    }
    

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

    题目描述:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

    算法1:递归

    1. 递归终止条件:链表中只剩一个结点或是没有结点,此时无法交换,则返回head
    2. 找返回值:返回给上一层交换好的链表头结点
    3. 本层要完成的任务:从宏观上考虑,当前的链表由头结点,头结点的下一个结点,还有处理好的链表头结点,此时对这三个结点交换即可
    struct ListNode* swapPairs(struct ListNode* head){
        if(head==NULL||head->next==NULL) return head;
        struct ListNode *p=head->next;
        head->next=swapPairs(head->next->next);
        p->next=head;
        return p;
    }
    

    算法2:迭代

    利用两结点之前结点的指针prev即可完成反转

    struct ListNode* swapPairs(struct ListNode* head){
        struct ListNode *dummy=malloc(sizeof(struct ListNode));
        dummy->next=head;
        struct ListNode *prev=dummy;
    
        while(prev->next&&prev->next->next){
            struct ListNode *l1=prev->next,*l2=prev->next->next;
            l1->next=l2->next;
            l2->next=l1;
            prev->next=l2;
            prev=l2->next;
        }
        return dummy->next;
    }
    

    445. 两数相加 II

    题目描述:给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。

    算法1:双栈

    注意最高位的进位

    class Solution {
    public:
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
             stack<int> st1,st2;
             while(l1){
                 st1.push(l1->val);
                 l1=l1->next;
             }
             while(l2){
                 st2.push(l2->val);
                 l2=l2->next;
             }
             int carry=0;
             ListNode *dummy=new ListNode(0);
             while(!st1.empty()||!st2.empty()||carry==1){
                 int x1,x2;
                 if(!st1.empty()){
                     x1=st1.top();
                     st1.pop();
                 }else{
                     x1=0;
                 }
                 if(!st2.empty()){
                     x2=st2.top();
                     st2.pop();
                 }else{
                     x2=0;
                 }
                 int sum=x1+x2+carry;
                 ListNode *p=new ListNode(sum%10);
                 carry=sum/10;
                 p->next=dummy->next;
                 dummy->next=p;
             }
             return dummy->next;
        }
    };
    

    算法2:递归

    先将两个链表的长度补齐,然后递归求解

    struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
        int len1=0,len2=0;
        struct ListNode *h1=l1,*h2=l2;
        while(h1){
            len1++;
            h1=h1->next;
        }
        while(h2){
            len2++;
            h2=h2->next;
        }
        int diff=0;
        if(len1<len2){
            struct ListNode *tmp=l1;
            l1=l2;
            l2=tmp;
            diff=len2-len1;
        }else{
            diff=len1-len2;
        }
        while(diff--){
            struct ListNode *p=malloc(sizeof(struct ListNode));
            p->val=0;
            p->next=l2;
            l2=p;
        }
        int cout=0;
        addList(l1,l2,&cout);
        if(cout==1){
            struct ListNode *p=malloc(sizeof(struct ListNode));
            p->val=1;
            p->next=l1;
            return p;
        }
        return l1;
    }
    void addList(struct ListNode* l1,struct ListNode* l2,int *cout){
        if(l1==NULL||l2==NULL) return;
        addList(l1->next,l2->next,cout);
        int sum=l1->val+l2->val+*cout;
        l1->val=sum%10;
        *cout=sum/10;
        return;
    }
    

    234. 回文链表

    题目描述:请判断一个链表是否为回文链表。你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

    算法:

    1. 先求出中间结点

    2. 从中间结点处反转链表,反转算法如下图所示

      image-20200409164304115

    3. 比较前后两部分对应结点的值

    bool isPalindrome(struct ListNode* head){
        int len=0;
        struct ListNode *p=head;
        while(p){
            len++;
            p=p->next;
        }
        if(len==1||len==0) return true;
        len=len%2?len/2+1:len/2;//奇数个结点时,中间结点设置为中心位置右边的结点
        struct ListNode *mid=head;
        while(len--){
            mid=mid->next;
        } 
        struct ListNode *prev=NULL;
        while(mid){
            struct ListNode *ovn=mid->next;
            mid->next=prev;
            prev=mid;
            mid=ovn;
        }
        while(prev&&head){
            if(prev->val!=head->val){
                return false;
            }
            prev=prev->next;
            head=head->next;
        }
        return true;
    }
    

    725. 分隔链表

    思路:

    1. 先求出链表总长度len,len/k即为最短链长,len%k为最长链长的个数,最长的链长比最短链长多1
    2. 根据链长分配链表,这段代码尤其要注意考虑临界情况
    struct ListNode** splitListToParts(struct ListNode* root, int k, int* returnSize){
        *returnSize=k;
        struct ListNode *p=root;
        int len=0;
        while(p){
            len++;
            p=p->next;
        }
        struct ListNode** res=(struct ListNode*)calloc(k,sizeof(struct ListNode));
        struct ListNode *prev=NULL,*head=root;//prev一定要初始化为NULL,没有初始化的指针很危险
        for(int i=0;i<k;i++){
            res[i]=head;
            int width=len/k+(i<len%k?1:0);
            for(int j=0;j<width;j++){
                prev=head;
                if(head) head=head->next;//这里的if可省略,不过有head->next还是加上比较好
            }
            if(prev) prev->next=NULL;
        }
        return res;
    }
    
  • 相关阅读:
    P4365 [九省联考2018]秘密袭击coat
    P3705 [SDOI2017]新生舞会 01分数规划+费用流
    P4313 文理分科 最小割
    P1707 刷题比赛
    P3994 高速公路 树形DP+斜率优化+二分
    P3384 【模板】树链剖分
    P4915 帕秋莉的魔导书
    P3690 【模板】Link Cut Tree (动态树)
    P3615 如厕计划
    loj #2538. 「PKUWC2018」Slay the Spire
  • 原文地址:https://www.cnblogs.com/xkf97/p/12632565.html
Copyright © 2011-2022 走看看