zoukankan      html  css  js  c++  java
  • 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)

    参考http://www.cnblogs.com/TenosDoIt/p/3666585.html

    插入排序(算法中是直接交换节点,时间复杂度O(n^2),空间复杂度O(1)

     1 class Solution {
     2 public:
     3     ListNode *insertionSortList(ListNode *head) {
     4         // IMPORTANT: Please reset any member data you declared, as
     5         // the same Solution instance will be reused for each test case.
     6         if(head == NULL || head->next == NULL)return head;
     7         ListNode *p = head->next, *pstart = new ListNode(0), *pend = head;
     8         pstart->next = head; //为了操作方便,添加一个头结点
     9         while(p != NULL)
    10         {
    11             ListNode *tmp = pstart->next, *pre = pstart;
    12             while(tmp != p && p->val >= tmp->val) //找到插入位置
    13                 {tmp = tmp->next; pre = pre->next;}
    14             if(tmp == p)pend = p;
    15             else
    16             {
    17                 pend->next = p->next;
    18                 p->next = tmp;
    19                 pre->next = p;
    20             }
    21             p = pend->next;
    22         }
    23         head = pstart->next;
    24         delete pstart;
    25         return head;
    26     }
    27 };

    选择排序(算法中只是交换节点的val值,时间复杂度O(n^2),空间复杂度O(1)

     1 class Solution {
     2 public:
     3     ListNode *selectSortList(ListNode *head) {
     4         // IMPORTANT: Please reset any member data you declared, as
     5         // the same Solution instance will be reused for each test case.
     6         //选择排序
     7         if(head == NULL || head->next == NULL)return head;
     8         ListNode *pstart = new ListNode(0);
     9         pstart->next = head; //为了操作方便,添加一个头结点
    10         ListNode*sortedTail = pstart;//指向已排好序的部分的尾部
    11         
    12         while(sortedTail->next != NULL)
    13         {
    14             ListNode*minNode = sortedTail->next, *p = sortedTail->next->next;
    15             //寻找未排序部分的最小节点
    16             while(p != NULL)
    17             {
    18                 if(p->val < minNode->val)
    19                     minNode = p;
    20                 p = p->next;
    21             }
    22             swap(minNode->val, sortedTail->next->val);
    23             sortedTail = sortedTail->next;
    24         }
    25         
    26         head = pstart->next;
    27         delete pstart;
    28         return head;
    29     }
    30 };

    快速排序1(算法只交换节点的val值,平均时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1))

    这里的partition我们参考数组快排partition的第二种写法(选取第一个元素作为枢纽元的版本,因为链表选择最后一元素需要遍历一遍),具体可以参考here

    这里我们还需要注意的一点是数组的partition两个参数分别代表数组的起始位置,两边都是闭区间,这样在排序的主函数中:

    void quicksort(vector<int>&arr, int low, int high)

    {

      if(low < high)

      {

       int middle = mypartition(arr, low, high);

       quicksort(arr, low, middle-1);

       quicksort(arr, middle+1, high);

      }

    }

    对左边子数组排序时,子数组右边界是middle-1,如果链表也按这种两边都是闭区间的话,找到分割后枢纽元middle,找到middle-1还得再次遍历数组,因此链表的partition采用前闭后开的区间(这样排序主函数也需要前闭后开区间),这样就可以避免上述问题

     
     1 class Solution {
     2 public:
     3     ListNode *quickSortList(ListNode *head) {
     4         // IMPORTANT: Please reset any member data you declared, as
     5         // the same Solution instance will be reused for each test case.
     6         //链表快速排序
     7         if(head == NULL || head->next == NULL)return head;
     8         qsortList(head, NULL);
     9         return head;
    10     }
    11     void qsortList(ListNode*head, ListNode*tail)
    12     {
    13         //链表范围是[low, high)
    14         if(head != tail && head->next != tail)
    15         {
    16             ListNode* mid = partitionList(head, tail);
    17             qsortList(head, mid);
    18             qsortList(mid->next, tail);
    19         }
    20     }
    21     ListNode* partitionList(ListNode*low, ListNode*high)
    22     {
    23         //链表范围是[low, high)
    24         int key = low->val;
    25         ListNode* loc = low;
    26         for(ListNode*i = low->next; i != high; i = i->next)
    27             if(i->val < key)
    28             {
    29                 loc = loc->next;
    30                 swap(i->val, loc->val);
    31             }
    32         swap(loc->val, low->val);
    33         return loc;
    34     }
    35 };

    快速排序2(算法交换链表节点,平均时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1))

    这里的partition,我们选取第一个节点作为枢纽元,然后把小于枢纽的节点放到一个链中,把不小于枢纽的及节点放到另一个链中,最后把两条链以及枢纽连接成一条链。

    这里我们需要注意的是,1.在对一条子链进行partition时,由于节点的顺序都打乱了,所以得保正重新组合成一条新链表时,要和该子链表的前后部分连接起来,因此我们的partition传入三个参数,除了子链表的范围(也是前闭后开区间),还要传入子链表头结点的前驱;2.partition后链表的头结点可能已经改变

    class Solution {
    public:
        ListNode *quickSortList(ListNode *head) {
            // IMPORTANT: Please reset any member data you declared, as
            // the same Solution instance will be reused for each test case.
            //链表快速排序
            if(head == NULL || head->next == NULL)return head;
            ListNode tmpHead(0); tmpHead.next = head;
            qsortList(&tmpHead, head, NULL);
            return tmpHead.next;
        }
        void qsortList(ListNode *headPre, ListNode*head, ListNode*tail)
        {
            //链表范围是[low, high)
            if(head != tail && head->next != tail)
            {
                ListNode* mid = partitionList(headPre, head, tail);//注意这里head可能不再指向链表头了
                qsortList(headPre, headPre->next, mid);
                qsortList(mid, mid->next, tail);
            }
        }
        ListNode* partitionList(ListNode* lowPre, ListNode* low, ListNode* high)
        {
            //链表范围是[low, high)
            int key = low->val;
            ListNode node1(0), node2(0);//比key小的链的头结点,比key大的链的头结点
            ListNode* little = &node1, *big = &node2;
            for(ListNode*i = low->next; i != high; i = i->next)
                if(i->val < key)
                {
                    little->next = i;
                    little = i;
                }
                else
                {
                    big->next = i;
                    big = i;
                }
            big->next = high;//保证子链表[low,high)和后面的部分连接
            little->next = low;
            low->next = node2.next;
            lowPre->next = node1.next;//为了保证子链表[low,high)和前面的部分连接
            return low;
        }
    };

    归并排序(算法交换链表节点,时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1))                        本文地址

    首先用快慢指针的方法找到链表中间节点,然后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表。归并排序应该算是链表排序最佳的选择了,保证了最好和最坏时间复杂度都是nlogn,而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了O(1)

    class Solution {
    public:
        ListNode *mergeSortList(ListNode *head) {
            // IMPORTANT: Please reset any member data you declared, as
            // the same Solution instance will be reused for each test case.
            //链表归并排序
            if(head == NULL || head->next == NULL)return head;
            else
            {
                //快慢指针找到中间节点
                ListNode *fast = head,*slow = head;
                while(fast->next != NULL && fast->next->next != NULL)
                {
                    fast = fast->next->next;
                    slow = slow->next;
                }
                fast = slow;
                slow = slow->next;
                fast->next = NULL;
                fast = sortList(head);//前半段排序
                slow = sortList(slow);//后半段排序
                return merge(fast,slow);
            }
             
        }
        // merge two sorted list to one
        ListNode *merge(ListNode *head1, ListNode *head2)
        {
            if(head1 == NULL)return head2;
            if(head2 == NULL)return head1;
            ListNode *res , *p ;
            if(head1->val < head2->val)
                {res = head1; head1 = head1->next;}
            else{res = head2; head2 = head2->next;}
            p = res;
             
            while(head1 != NULL && head2 != NULL)
            {
                if(head1->val < head2->val)
                {
                    p->next = head1;
                    head1 = head1->next;
                }
                else
                {
                    p->next = head2;
                    head2 = head2->next;
                }
                p = p->next;
            }
            if(head1 != NULL)p->next = head1;
            else if(head2 != NULL)p->next = head2;
            return res;
        }
    };

    冒泡排序(算法交换链表节点val值,时间复杂度O(n^2),空间复杂度O(1))

    class Solution {
    public:
        ListNode *bubbleSortList(ListNode *head) {
            // IMPORTANT: Please reset any member data you declared, as
            // the same Solution instance will be reused for each test case.
            //链表快速排序
            if(head == NULL || head->next == NULL)return head;
            ListNode *p = NULL;
            bool isChange = true;
            while(p != head->next && isChange)
            {
                ListNode *q = head;
                isChange = false;//标志当前这一轮中又没有发生元素交换,如果没有则表示数组已经有序
                for(; q->next && q->next != p; q = q->next)
                {
                    if(q->val > q->next->val)
                    {
                        swap(q->val, q->next->val);
                        isChange = true;
                    }
                }
                p = q;
            }
            return head;
        }
    };


  • 相关阅读:
    关于在MAC上进行 LARAVEL 环境 Homestead 安装过程记录
    js 贷款计算器
    js 实现阶乘
    js 两点间距离函数
    composer Your requirements could not be resolved to an installable set of packages
    vue 项目优化记录 持续更新...
    vue 项目打包
    vue 真机调试页面出现空白
    vue 真机调试
    谈谈-Android状态栏的编辑
  • 原文地址:https://www.cnblogs.com/StarZhai/p/9984230.html
Copyright © 2011-2022 走看看