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;
        }
    };
  • 相关阅读:
    局部类
    内部类
    程序的异常
    四种修饰符
    接口之间的多继承
    多态
    继承父类并实现多个接口
    接口内容小结
    接口的静态方法和私有方法
    顺序栈与链式栈
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/11167942.html
Copyright © 2011-2022 走看看