链表/单链表 O(nlogn)排序

写单链表的时候突然想到,链表该怎么排序呢? 毕竟我们链表也要有自己的O(nlogn)排序方法!

第一个想法:用quicksort,带头尾指针的双链表确实可以,但是效率不优,因为在选择pivot的时候只能选择最左端,很容易退化成冒泡,更别说单链表了压根就不能用了。

第二个想法:嗯嗯嗯....这种问题肯定前人已经解决过了。所以去看看stl库吧。

用C++ list库,然后写一行 .sort() 再看定义。

template<typename _Tp, typename _Alloc>
    void
    list<_Tp, _Alloc>::
    sort()
    {
      // Do nothing if the list has length 0 or 1.
      if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node
	  && this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node)
      {
        list __carry;
        list __tmp[64];
        list * __fill = __tmp;
        list * __counter;
	__try
	  {
	    do
	      {
		__carry.splice(__carry.begin(), *this, begin());

		for(__counter = __tmp;
		    __counter != __fill && !__counter->empty();
		    ++__counter)
		  {
		    __counter->merge(__carry);
		    __carry.swap(*__counter);
		  }
		__carry.swap(*__counter);
		if (__counter == __fill)
		  ++__fill;
	      }
	    while ( !empty() );

	    for (__counter = __tmp + 1; __counter != __fill; ++__counter)
	      __counter->merge(*(__counter - 1));
	    swap( *(__fill - 1) );
	  }
	__catch(...)
	  {
	    this->splice(this->end(), __carry);
	    for (int __i = 0; __i < sizeof(__tmp)/sizeof(__tmp[0]); ++__i)
	      this->splice(this->end(), __tmp[__i]);
	    __throw_exception_again;
	  }
      }
    }

这里确实有点看不太懂。

关于stl这里的源码解析,推荐:C++ SGI STL 的 list::sort() 分析_list.sort函数 c++-CSDN博客​​​​​​

解析的很清晰,用的是非递归的mergesort,尤其是运用splice函数和counter数组,充分发挥了链表的优势,如果数组用这种方法反而会变慢。

我这里只是用单链表进行了实践,双向链表也是一样的。

首先定义:

typedef struct LIST{
    int data;
    LIST*next;
}*Linklist;

几个辅助函数:


void init_list(Linklist & list)
{
    Linklist p= new LIST;
    list=p;
    list->next=NULL;
}

//尾插法初始化
void init(Linklist&list)
{
    int n,x;
    Linklist p=list;

    cin>>n;

    while(n--)
    {
        cin>>x;

        Linklist temp=new LIST;
        temp->next=NULL;
        temp->data=x;

        p->next=temp;
        p=p->next;
    }
}

void show(Linklist&list)
{
    Linklist p=list;

    while(p->next!=NULL)
    {
        cout<<p->next->data<<' ';
        p=p->next;
    }
    
    cout<<endl;
    return;
}
    

然后是stl库中几个辅助函数的时间:

empty():

bool empty(Linklist&p)
{
    return p==NULL||p->next==NULL;
}

splice(): 因为只需要用到第一个节点,所以我这里只要了首节点。

void splice_front(Linklist&head,Linklist & p)
{
    Linklist temp=head->next;
    head->next=temp->next;
    temp->next=p->next;
    p->next=temp;
}

merge():

void list_merge(Linklist &list,Linklist& p)
{
    Linklist it=list,p1=list->next,p2=p->next;

    while(p1&&p2)
    {
        if(p1->data<p2->data)
        {
            it->next=p1;
            p1=p1->next;
        }
        else
        {
            it->next=p2;
            p2=p2->next;
        }

        it=it->next;
    }

    if(!p1)it->next=p2;
    else it->next=p1;

    p->next=NULL;
}

最后是sort部分的函数:

void list_sort(Linklist& list)
{
    if(list->next==NULL||list->next->next==NULL)return;

    Linklist carry=new LIST;
    Linklist counter[64];

    for(int i=0;i<64;++i)
        counter[i]=new LIST,counter[i]->next=NULL;
        
    carry->next=NULL;

    int fill=0;

    while(!empty(list))
    {
        splice_front(list,carry);
        int i=0;

        while(i<fill&&!empty(counter[i]))
        {
            list_merge(counter[i],carry);
        
            swap(carry,counter[i++]);
        }

        swap(carry,counter[i]);

        if(i==fill)++fill;
    }

    for(int i=1;i<fill;++i)
        list_merge(counter[i],counter[i-1]);
    
    list->next=counter[fill-1]->next;

    for(int i=0;i<64;++i)
        delete counter[i];
    
    delete carry;

    return;
}

全部代码:

#include <iostream>
#include <list>

#define ll long long

using namespace std;

typedef struct LIST{
    int data;
    LIST*next;
}*Linklist;

void init_list(Linklist & list)
{
    Linklist p= new LIST;
    list=p;
    list->next=NULL;
}

//尾插法初始化
void init(Linklist&list)
{
    int n,x;
    Linklist p=list;

    cin>>n;

    while(n--)
    {
        cin>>x;

        Linklist temp=new LIST;
        temp->next=NULL;
        temp->data=x;

        p->next=temp;
        p=p->next;
    }
}

void show(Linklist&list)
{
    Linklist p=list;

    while(p->next!=NULL)
    {
        cout<<p->next->data<<' ';
        p=p->next;
    }
    
    cout<<endl;
    return;
}
    
bool empty(Linklist&p)
{
    return p==NULL||p->next==NULL;
}

void splice_front(Linklist&head,Linklist & p)
{
    Linklist temp=head->next;
    head->next=temp->next;
    temp->next=p->next;
    p->next=temp;
}

void list_merge(Linklist &list,Linklist& p)
{
    Linklist it=list,p1=list->next,p2=p->next;

    while(p1&&p2)
    {
        if(p1->data<p2->data)
        {
            it->next=p1;
            p1=p1->next;
        }
        else
        {
            it->next=p2;
            p2=p2->next;
        }

        it=it->next;
    }

    if(!p1)it->next=p2;
    else it->next=p1;

    p->next=NULL;
}


void list_sort(Linklist& list)
{
    if(list->next==NULL||list->next->next==NULL)return;

    Linklist carry=new LIST;
    Linklist counter[64];

    for(int i=0;i<64;++i)
        counter[i]=new LIST,counter[i]->next=NULL;
        
    carry->next=NULL;

    int fill=0;

    while(!empty(list))
    {
        splice_front(list,carry);
        int i=0;

        while(i<fill&&!empty(counter[i]))
        {
            list_merge(counter[i],carry);
        
            swap(carry,counter[i++]);
        }

        swap(carry,counter[i]);

        if(i==fill)++fill;
    }

    for(int i=1;i<fill;++i)
        list_merge(counter[i],counter[i-1]);
    
    list->next=counter[fill-1]->next;

    for(int i=0;i<64;++i)
        delete counter[i];
    
    delete carry;

    return;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    Linklist it;

    init_list(it);
    init(it);

    list_sort(it);

    show(it);

    cout<<endl;
    system("pause");
    return 0;
}

去洛谷试试吧:P1177 【模板】排序 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

用时104ms

内存3.67MB

内存逼近O(logn)但是list的空间密度较低,所以内存较大,时间和数组的quicksort逼近了。让我怀疑在数组中 nlogn 的排序方法中mergesort之所以是 n 的空间复杂度,就是因为它实际上是为list设计的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
链表的归并排序是一种常见的排序算法,它通过分治的思想将链表分成若干个子链表,然后对每个子链表进行排序,最后将这些有序的子链表合并成一个有序的链表。 具体实现步骤如下: 1. 定义一个函数merge(head1, head2),用于将两个有序链表head1和head2合并成一个有序链表,并返回合并后的链表头节点。 2. 定义一个函数sortList(head),用于将链表head排序,并返回排序后的链表头节点。排序的具体实现可以按以下步骤进行: a. 如果链表为空或只有一个节点,则直接返回。 b. 定义两个指针slow和fast,分别指向链表头节点和中间节点。使用快慢指针的方法将链表分成两个子链表。 c. 递归调用sortList函数对两个子链表进行排序。 d. 调用merge函数将两个有序的子链表合并成一个有序的链表,并返回合并后的链表头节点。 3. 最后,调用sortList函数对整个链表进行排序即可。 下面是链表的归并排序的Python代码实现: ```python class ListNode: def __init__(self, x): self.val = x self.next = None def merge(head1, head2): if not head1: return head2 if not head2: return head1 if head1.val < head2.val: head1.next = merge(head1.next, head2) return head1 else: head2.next = merge(head1, head2.next) return head2 def sortList(head): if not head or not head.next: return head slow, fast = head, head.next while fast and fast.next: slow = slow.next fast = fast.next.next mid = slow.next slow.next = None left = sortList(head) right = sortList(mid) return merge(left, right) ``` 时间复杂度:O(nlogn)。 空间复杂度:O(logn),因为归并排序使用了递归,所以需要O(logn)的栈空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DogDu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值