zoukankan      html  css  js  c++  java
  • [LeetCode]Sort List

    题目:Sort List

    对链表排序;

    要求:时间复杂度O(nlogn),空间复杂度O(1)

    考虑使用归并排序。

    归并排序的思想:先将它划分成k各部分每个部分排好序后,再两两合并,知道所有的合并到一起,就排好序。

    由于要求空间复杂度O(1),所以不能递归。

    下面的实现是初始步长为2时的归并排序的情况。

    ListNode* LeetCode::mergeList(ListNode* l1, ListNode* l2){
        if (!l1)return l2;
        if (!l2)return l1;
        ListNode* head = nullptr;
        ListNode* p = nullptr;
        if (l1->val <= l2->val){//比较两个链表头的值的大小
            head = l1;//第一个小
            p = head;
            l1 = l1->next;
        }
        else{//第二个小
            head = l2;
            p = head;
            l2 = l2->next;
        }
        while (l1 && l2){//合并两个链表
            if (l1->val <= l2->val){
                p->next = l1;
                l1 = l1->next;
            }
            else{
                p->next = l2;
                l2 = l2->next;
            }
            p = p->next;
        }
        if (l1){//第一个链表有剩余
            p->next = l1;
        }
        if (l2){//第二个链表有剩余
            p->next = l2;
        }
        return head;
    }
    
    ListNode* LeetCode::sortList(ListNode* head){
        if (!head || !head->next)return head;
        int step = 2,length = 1;//step为步长,length为链表长度
        ListNode* p = head->next, *tail = nullptr;
        //因为后面从head->next开始合并所以这里也要对应,p从head->next开始
        while (p){//每两个节点排一次序
            if (!p->next){//长度为奇数
                ++length;
                break;
            }
            if (p->val > p->next->val){//替换两个相邻节点的值
                auto temp = p->val;
                p->val = p->next->val;
                p->next->val = temp;
            }
            p = p->next->next;//移动两步
            length += 2;
        }
        for (; step < length; step = step << 1)
        {
            p = head->next;//从第二个节点开始合并链表,不用判断头结点的特殊情况
            //用于合并的两个子链表
            ListNode* left = nullptr,* right = nullptr;
            tail = head;
            int cur = 1;
            while (cur + step < length){//当前位置之后没有step个节点
                left = p;
                for (int i = 1; i < step; i++){//找到第一个步长为step的子链表
                    p = p->next;
                }
                cur += step;//更新当前位置
                right = p->next;
                p->next = nullptr;//第一个子链表的尾部置为空
                p = right;//更新当前位置指针
                if (cur + step < length){//是否有足够step个节点做第二个子链表
                    for (int i = 1; i < step; i++){////找到第二个步长为step的子链表
                        p = p->next;
                    }
                    ListNode* q = p->next;
                    p->next = nullptr;//第二个子链表的尾部置为空
                    p = q;//更新当前位置指针
                    left = mergeList(left, right);
                }
                else{//不够
                    left = mergeList(left, right);//直接合并
                    p = nullptr;
                }
                tail->next = left;//连接合并后的子链表
                while (tail->next)tail = tail->next;//找到新的尾巴
                tail->next = p;
                cur += step;//更新当前位置
            }
        }
        if (p && tail && tail != head){//尾部不够一个步长的子链表未合并
            tail->next = nullptr;
            p = mergeList(head->next, p);//保证head->next到tail的链表不为空
        }
        p = head->next;
        head->next = nullptr;
        head = mergeList(head, p);
        return head;
    }

    其实对链表排序,当数据量不多时,直接插入排序效率是最好的,因此,经常将直接插入排序和归并排序结合起来。

    数据量少时,就直接使用直接插入排序;数据量多时,先拆分到数据量较小时,使用直接插入排序,然后再归并,使其整体有序。

    下面的实现可以是任意步长,其中会先用直接插入排序来对初始的步长值的子链表排序。

    ListNode* LeetCode::sortList(ListNode* head){
        if (!head || !head->next)return head;
        int step = 3,length = 1;//step为步长,length为链表长度
        ListNode* p = head;
        while (p->next){
            ListNode* pa = p;//记录需要插入排序的子链表的头结点的父节点
            ListNode* gson = nullptr;//记录需要插入排序的子链表的尾结点的子节点
            for (int i = 0; p->next && i < step; i++){//长度为步长的子链表
                p = p->next;
                ++length;
            }
            if (p->next){//未到链表尾部
                gson = p->next;//切断子链表尾部与原链表的联系
                p->next = nullptr;
            }
            pa->next = insertionSortList(pa->next);//插入排序
            p = pa;
            while (p->next)p = p->next;//找到子链表的尾部
            if (gson){//将切断的子链表与原链表的联系重新连接起来
                p->next = gson;
            }
        }
        ListNode* tail = nullptr;
        for (; step < length; step = step << 1)
        {
            p = head->next;//从第二个节点开始合并链表,不用判断头结点的特殊情况
            //用于合并的两个子链表
            ListNode* left = nullptr,* right = nullptr;
            tail = head;
            int cur = 1;
            while (cur + step < length){//当前位置之后没有step个节点
                left = p;
                for (int i = 1; i < step; i++){//找到第一个步长为step的子链表
                    p = p->next;
                }
                cur += step;//更新当前位置
                right = p->next;
                p->next = nullptr;//第一个子链表的尾部置为空
                p = right;//更新当前位置指针
                if (cur + step < length){//是否有足够step个节点做第二个子链表
                    for (int i = 1; i < step; i++){////找到第二个步长为step的子链表
                        p = p->next;
                    }
                    ListNode* q = p->next;
                    p->next = nullptr;//第二个子链表的尾部置为空
                    p = q;//更新当前位置指针
                    left = mergeList(left, right);
                }
                else{//不够
                    left = mergeList(left, right);//直接合并
                    p = nullptr;
                }
                tail->next = left;//连接合并后的子链表
                while (tail->next)tail = tail->next;//找到新的尾巴
                tail->next = p;
                cur += step;//更新当前位置
            }
        }
        if (p && tail && tail != head){//尾部不够一个步长的子链表未合并
            tail->next = nullptr;
            p = mergeList(head->next, p);//保证head->next到tail的链表不为空
        }
        p = head->next;
        head->next = nullptr;
        head = mergeList(head, p);
        return head;
    }
  • 相关阅读:
    我们毕业了!!!@全体成员
    华东交通大学编译原理期末试卷
    软件设计师中级下午答题解题策略分析~
    Java实现旅行商最短距离
    基于SSH的医院在线挂号
    基于Java的模拟写字板的设计与实现
    基于java的雷电游戏
    基于Java的飞机大战游戏的设计与实现
    基于Java的超级玛丽游戏的设计与实现
    基于Javaee的影视创作论坛的设计与实现
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6755973.html
Copyright © 2011-2022 走看看