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

    1、使用常量空间复杂度在O(n log n)时间内对链表进行排序。

    思路:

    因为题目要求复杂度为O(nlogn),故可以考虑归并排序的思想。
    归并排序的一般步骤为:
    1)将待排序数组(链表)取中点并一分为二;
    2)递归地对左半部分进行归并排序;
    3)递归地对右半部分进行归并排序;
    4)将两个半部分进行合并(merge),得到结果。
     
    所以对应此题目,可以划分为三个小问题:
    1)找到链表中点 (快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针恰好在链表中点);
    2)写出merge函数,即如何合并链表。 (见merge-two-sorted-lists 一题解析)
    3)写出mergesort函数,实现上述步骤。
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        //找到链表中的中点
        ListNode *findMiddle(ListNode *head){
            ListNode *chaser = head;
            ListNode *runner = head->next;
            while(runner !=NULL &&runner->next != NULL){
                chaser = chaser->next;
                runner = runner->next->next;
            }
            return chaser;
        }
        //将两组链表进行排序
        ListNode *mergeTwoLists(ListNode* l1, ListNode* l2){
            if(l1==NULL) return l2;
            if(l2==NULL) return l1;
            ListNode *dummy = new ListNode(0);
            ListNode *head = dummy;
            while(l1!=NULL && l2!=NULL){
                if(l1->val > l2->val){
                    head->next = l2;
                    l2 = l2->next;
                }else{
                    head->next = l1;
                    l1 = l1->next;
                }
                head = head->next;
            }
            if(l1==NULL) head->next = l2;
            if(l2==NULL) head->next = l1;
            return dummy->next;
        }
        
        ListNode *sortList(ListNode *head) {
            if(head==NULL || head->next == NULL) return head;
            ListNode* middle = findMiddle(head);
            ListNode* right = sortList(middle->next);
            middle -> next = NULL;
            ListNode* left = sortList(head);
            return mergeTwoLists(left, right);
        }
    };

     2、给出单链表L:L 0→L 1→...→L n-1→L n,

    将其重新排序为:L 0→L n→L 1→L n-1→L 2→L n-2→......
    您必须在不改变节点值的情况下就地执行此操作。
    例如,
    给定{1,2,3,4},将其重新排序为{1,4,2,3}。

    思路:快慢指针找到中间节点,将后面的链表反转(前插法),合并链表

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        void reorderList(ListNode *head) {
            ListNode *slow = head;
            ListNode *fast = head;
            //快慢指针找到中间节点
            while(fast->next!=NULL && fast->next->next!=NULL){
                slow = slow->next;
                fast = fast->next->next;
            }
            //拆分链表,并反转中间节点之后的链表
            ListNode *after = slow->next;
            slow->next = NULL;
            ListNode *pre = NULL;
            while(after!=NULL){
                ListNode *temp = after->next;
                after->next = pre;
                pre = after;
                after = temp;
            }
            // 合并两个表
            ListNode *left = head;
            after = pre;
            while(left!=NULL && after!=NULL){
                ListNode *ltemp = left->next;
                ListNode *rtemp = after->next;
                left->next = after;
                left = ltemp;
                after->next = left;
                after = rtemp;
            }
        }
    };

    3、给定链表,返回循环开始的节点。 如果没有循环,则返回null。
    跟进:
    你能不用额外的空间解决它吗?

    思路:

    1)首先判断是否有环,有环时,返回相遇的节点,无环,返回null
    2)有环的情况下, 求链表的入环节点
    从头结点开始走,一边走一边将走过的路清空,当遇到空时,此时就是环的入口点
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *detectCycle(ListNode *head) {
            ListNode *slow = head;
            ListNode *fast = head;
            //找到相遇点
            while(fast!=NULL && fast->next!=NULL){
                slow=slow->next;
                fast=fast->next->next;
                //如果快慢指针相遇
                if(slow==fast){
                    ListNode *temp = NULL;
                    //一边走一边将走过的路清空,当遇到空时,此时就是相遇点
                    while(head->next){
                        temp = head->next;
                        head->next = NULL;
                        head = temp;
                    }
                    return head;
                } 
            }
            return NULL;
        }
    };

    4、给定一个链表,确定它是否有一个循环。
    跟进:
    你能不用额外的空间解决它吗?

    思路:

    利用快慢节点相遇即有环,

    慢节点一次走一格

    快捷点一次走两格

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        bool hasCycle(ListNode *head) {
            ListNode *slow = head;
            ListNode *fast = head;
            //找到相遇点
            while(fast!=NULL && fast->next!=NULL){
                slow=slow->next;
                fast=fast->next->next;
                //如果快慢指针相遇
                if(slow==fast) return true;
            }
            return false;
        }
    };

     5、给出链表,使得每个节点包含一个附加的随机指针,该指针可以指向列表中的任何节点或为空。
    返回列表的深层副本。

    思路:

    先拷贝新节点,插入到原节点的后边;然后再 拷贝随机指针;最后将新节点从原链表中分离出,注意要保证原链表正常。

    /**
     * Definition for singly-linked list with a random pointer.
     * struct RandomListNode {
     *     int label;
     *     RandomListNode *next, *random;
     *     RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
     * };
     */
    class Solution {
    public:
        RandomListNode *copyRandomList(RandomListNode *head) {
            RandomListNode *p,*copy;
            if (!head) return NULL;
            //先将拷贝原节点产生的新节点,放在原节点的后面
            for(p=head;p;p=p->next){
                copy = new RandomListNode(p->label);
                copy->next = p->next;
                p = p->next = copy;
            }
            //拷贝random指针
            for(p=head;p;p=copy->next){
                copy = p->next;          
                copy->random = (p->random?p->random->next:NULL);
            }
            //删除新节点
            for(p=head,head=copy=p->next;p;){
                p = p->next = copy->next; 
                copy = copy->next = (p?p->next:NULL);
            }
            return head;
        }
    };

    6、将位置m的链接列表反转到n。 在原地和一次通过。
    例如:
    给定1-> 2-> 3-> 4-> 5-> NULL,m = 2且n = 4,
    return1->4->3-> 2->5-> NULL。
    注意:
    给定m,n满足以下条件:
    1≤m≤n≤列表长度。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *reverseBetween(ListNode *head, int m, int n) {
            ListNode *dummy = new ListNode(-1);
            ListNode *preStart,*Start;
            dummy->next = head;
            preStart = dummy;
            Start = head;
            for(int i=1;i<m;i++){
                preStart = Start;
                Start = Start->next;
            }
            for(int i=0;i<n-m;i++){
                ListNode *temp = Start->next;
                Start->next = temp->next;
                temp->next = preStart->next;
                preStart->next = temp;
            }
            return dummy->next;
        }
    };

    7、给定链表和值x,对其进行分区,使得小于x的所有节点都在大于或等于x的节点之前。
    您应该保留两个分区中每个分区中节点的原始相对顺序。
    例如,
    给定1-> 4-> 3-> 2-> 5-> 2和x = 3,
    return1-> 2->2->4->3->5。

    思路:

    创建两张链表,分别我list01和list01

    把节点值小于x的节点链接到链表1上,节点值大等于x的节点链接到链表2上。

    最后把两个链表相连即可
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *partition(ListNode *head, int x) {
            //创建两个链表
            ListNode *list01 = new ListNode(-1);
            ListNode *list02 = new ListNode(-2);
            ListNode *cur1 = list01;
            ListNode *cur2 = list02;
            while(head!=NULL){
                //把节点值小于x的节点链接到链表1上
                if(head->val < x){
                    cur1->next = head;
                    cur1=cur1->next;
                }
                //节点值大等于x的节点链接到链表2上
                else{ 
                    cur2->next = head;
                    cur2=cur2->next;
                }
                head = head->next;
            }
            //合并两个链表
            cur1->next = list02->next;
            cur2->next = NULL;
            return list01->next;
        }
    
    };

    8、给定一个列表,将列表向右旋转k个位置,其中k为非负数。

    例如:
    给定1-> 2-> 3-> 4-> 5-> NULL和k = 2,
    返回4-> 5-> 1-> 2-> 3-> NULL。

    思路:

    先遍历一遍,得出链表长度len,注意k可能会大于len,因此k%=len。
    将尾结点next指针指向首节点,形成一个环,接着往后跑len-k步,从这里断开,就是结果
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *rotateRight(ListNode *head, int k) {
            if(head==NULL) return NULL;
            ListNode *p=head;
            int len=1;
            //计算连链表的长度
            while(p->next){
                len++;
                p=p->next;
            }
            k = len - k%len;
            //首尾相连
            p->next = head;
            for(int i=0;i<k;i++){
                p=p->next;
            }
            head = p->next;
            p->next = NULL;
            return head;
        }
    };

    9、给定已排序的链接列表,删除所有具有重复数字的节点,只留下原始列表中的不同数字。

    例如,
    给定1-> 2-> 3-> 3-> 4-> 4-> 5,返回1-> 2-> 5。
    给定1-> 1-> 1-> 2-> 3,return2-> 3。

    思路:

    首先要找到第一个非重复的节点作为头结点,
    直接找比较麻烦,可以添加一个新结点list作为伪头结点,
    最后返回list->next即可。
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *deleteDuplicates(ListNode *head) {
            if(head==NULL) return NULL;
            ListNode *list = new ListNode(head->val-1);
            list->next = head;
            ListNode *preStart = list;
            while(preStart->next && preStart->next->next){
                ListNode *Start = preStart->next;
                ListNode *aftStart = Start->next;
                if(Start->val!=aftStart->val){
                    preStart->next = Start;
                    preStart = Start;
                }else{
                    while(aftStart && Start->val==aftStart->val){
                        aftStart=aftStart->next;
                    }
                    preStart->next = aftStart;
                }
            }
            return list->next;
        }
    };

    10、给定已排序的链接列表,删除所有重复项,使每个元素只出现一次。

    例如,
    给定1-> 1-> 2,return1-> 2。
    给定1-> 1-> 2-> 3-> 3,返回1-> 2-> 3。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *deleteDuplicates(ListNode *head) {
            if(head == NULL || head->next == NULL) return head;
            ListNode* p1 = head;
            while(p1 != NULL && p1->next != NULL) {
                ListNode* p2 = p1->next;
                if(p1->val != p2->val) {
                    p1->next = p2;
                    p1 = p1->next;
                    continue;
                }
                while(p2->next != NULL && p1->val == p2->next->val) 
                    p2 = p2->next;
                p1->next = p2->next;
                delete p2;
                p1 = p1->next;
            }
            return head;
        }
    };

    11、合并两个已排序的链接列表并将其作为新列表返回。 新列表应该通过拼接前两个列表的节点来完成。

    思路:和归并排序的思想一样

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
            ListNode *head = new ListNode(0);
            ListNode *t = head;
            while (l1 != NULL || l2 != NULL) {
                if (l1 == NULL) {
                    t->next = l2;
                    l2 = l2->next;
                }
                else if (l2 == NULL) {
                    t->next = l1;
                    l1 = l1->next;
                }
                else if (l1->val < l2 -> val){
                    t->next = l1;
                    l1 = l1->next;
                }
                else {
                    t->next = l2;
                    l2 = l2->next;
                }
                t = t->next;
            }
            return head->next;
        }
    };

    12、给定链表,一次反转链表k的节点并返回其修改后的列表。
    如果节点数不是k的倍数,那么最后的剩余节点应该保持不变。
    您可能无法更改节点中的值,只能更改节点本身。
    只允许常量内存。
    例如,
    鉴于此链表:1-> 2-> 3-> 4-> 5
    对于k = 2,您应该返回:2-> 1-> 4-> 3-> 5
    对于k = 3,您应该返回:3-> 2-> 1-> 4-> 5

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *reverseKGroup(ListNode *head, int k) {
            /*
            if(head==NULL || head->next==NULL || k<2) return head;
            ListNode *dummy = new ListNode(-1);
            dummy->next = head;
            ListNode *Start = head;
            ListNode *preStart = dummy;
            ListNode *temp;
            int len = 1;
            while(head!=NULL){
                len++;
                head=head->next;
            }
            for(int i=0;i<len/2;i++){
                for(int j=i+1;j<len;j++){
                    temp = Start->next;
                    Start->next = temp->next;
                    temp->next = preStart->next;
                    preStart->next = temp;
                }
                preStart = Start;
                Start = Start->next;
            }
            return dummy->next;
            */
            if(head == NULL || head->next == NULL || k < 2) return head;
            ListNode *dummy = new ListNode(-1);
            dummy->next = head;
            ListNode *pre = dummy, *cur = head, *temp;
            int len = 0;
            //计算链表的长度
            while (head != NULL) {
                len ++ ;
                head = head->next;
            }
            for (int i = 0; i < len / k; i ++ ) {
                for (int j = 1; j < k; j ++ ) {
                    temp = cur->next;
                    cur->next = temp->next;
                    temp->next = pre->next;
                    pre->next = temp;
                }
                pre = cur;
                cur = cur->next;
            }
            return dummy->next;
        }
    };

    13、给定链表,交换每两个相邻节点并返回其头部。
    例如,
    给定1-> 2-> 3-> 4,您应该返回列表as2-> 1-> 4-> 3。
    您的算法应该只使用恒定空间。 您可能无法修改列表中的值,只能更改节点本身。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *swapPairs(ListNode *head) {
            if(head==NULL || head->next==NULL ) return head;
            ListNode *dummy = new ListNode(-1);
            dummy->next = head;
            ListNode *Start = head;
            ListNode *aftStart = head->next;
            ListNode *preStart = dummy;
            ListNode *temp;
            while(Start!=NULL && aftStart!=NULL){
                temp = aftStart->next;
                Start->next = temp;
                aftStart->next = Start;
                preStart->next = aftStart;
                preStart = Start;
                Start = temp;
                aftStart = temp->next;
            }
            return dummy->next;
        }
    };

    14、给定链表,从列表末尾删除第n个节点并返回其头部。
    例如,
    给定链表:1-> 2-> 3-> 4-> 5,n = 2。
    从末尾删除第二个节点后,链表变为1-> 2-> 3-> 5。
    注意:
    给定n将始终有效。
    尝试一次性完成此操作。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *removeNthFromEnd(ListNode *head, int n) {
            if(head==NULL ) return head;
            ListNode *dummy = new ListNode(0);
            dummy->next = head;
            head = dummy;
            ListNode *slow = head;
            ListNode *fast = head;
            for(int i=0;i<n;i++){
                fast = fast->next;
            }
            while(fast->next!=NULL){
                fast = fast->next;
                slow = slow->next;
            }
            ListNode *temp = slow->next;
            slow->next = slow->next->next;
            delete temp;
            return dummy->next;
        }
    };
  • 相关阅读:
    HDU 1010 Tempter of the Bone
    HDU 4421 Bit Magic(奇葩式解法)
    HDU 2614 Beat 深搜DFS
    HDU 1495 非常可乐 BFS 搜索
    Road to Cinema
    Sea Battle
    Interview with Oleg
    Spotlights
    Substring
    Dominating Patterns
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/11167942.html
Copyright © 2011-2022 走看看