单向链表的原地归并排序实现

前段时间准备一些面试题时,才开始考虑链表的排序问题。当时一想就感觉原地归并的实现有戏,上网找了一下,发现有人竟是先将链表打散成数组再来排,非常汗。于是自己写了个非递归的原地归并实现,如下:

#include <stdlib.h>

template<class T>
struct Node
{
Node(T d)
{
data = d;
next = NULL;
}
T data;
Node *next;
};

template<class T>
Node<T> * GetTailNode( Node<T> *startNode, int length )
//按长度找到尾节点
{
while(length-- && startNode->next != NULL)
{
startNode = startNode->next;
}
return startNode;
}

template<class T, class CmpFunc>
Node<T> * Merge( Node<T> *&startNode, int currBlockLen, CmpFunc cmp )
//从startNode开始,按currBlockLen二分,进行归并操作
{
bool canMerge = true;
Node<int> *leftBlock = startNode, *rightBlock = startNode;

// 找到rightBlock的头节点
for(int i = 0; i<currBlockLen; ++i)
{
rightBlock = rightBlock->next;
if(rightBlock == NULL)
{
   canMerge = false;
   break;
}
}

if(!canMerge)
return NULL;

Node<int> *currSortedNode;    //排序结果的当前节点
Node<int> *currTailNode = NULL; //排序结果的尾节点,用于最后将排序结果与下一段链表数据连接
int leftCount = 0, rightCount = 0;

//初始化currSortedNode
if( cmp(leftBlock->data, rightBlock->data) )
{
++rightCount;
currSortedNode = startNode = rightBlock;
rightBlock = rightBlock ->next;
}
else
{
++leftCount;
currSortedNode = leftBlock;
leftBlock = leftBlock ->next;
}

while(true)
{
if(leftCount == currBlockLen || leftBlock == NULL )
   //当leftBlock完成排序时,leftBlock尾节点即为currSortedNode
{
   currSortedNode->next = rightBlock;   //剩下的rightBlock无需再排序,直接加入结果链
   currTailNode = GetTailNode( currSortedNode, currBlockLen-rightCount );   //找到结果的尾节点
   break;
}
else if(rightCount == currBlockLen || rightBlock == NULL )
   //当右Block完成排序,此时右Block的尾节点即为currSortedNode
{
   Node<T> *tmpNode = currSortedNode->next;   //记下下一段链表的头节点

   currSortedNode->next = leftBlock;
   currTailNode = GetTailNode( currSortedNode, currBlockLen-leftCount );
   currTailNode->next = tmpNode;     //将结果链表与下一段链表连起来
   break;
}
else
   //普通的归并操作
{
   if(cmp(leftBlock->data, rightBlock->data))
   {
    ++rightCount;
    currSortedNode->next = rightBlock;
    rightBlock = rightBlock->next;
   }
   else
   {
    ++leftCount;
    currSortedNode->next = leftBlock;
    leftBlock = leftBlock->next;
   }
   currSortedNode = currSortedNode->next;
}
}
return currTailNode->next != NULL ? currTailNode : NULL;    //返回NULL意味着一轮归并的结束
}

template<class T, class CmpFunc>
void MergeSort( Node<T>* &dataLink, CmpFunc cmp )
{
int currBlockLen = 1;
Node<T> *lastTailNode;
while( true )
{
lastTailNode = Merge( dataLink, currBlockLen, cmp );
if(lastTailNode != NULL)
{
   do
   {
    lastTailNode = Merge( lastTailNode->next, currBlockLen, cmp );
   }
   while ( lastTailNode != NULL );
   currBlockLen *= 2;
}
else
   break;
}
}

对以上实现的测试代码如下:

Node<int> *BuildLink( int *data, int len )
{
Node<int> *header = new Node<int>(data[0]);
Node<int> *currNode = header;
for(int i = 1; i<len; ++i)
{
currNode->next = new Node<int>( data[i] );
currNode = currNode->next;
}
return header;
}

inline bool IntCmp( int &a, int &b )
{
return a>b;
}

#include <time.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
int main()
{
srand(time(0));
int dataLen = 10000;
int *data = new int[dataLen];
for(int i = 0; i<dataLen; ++i)
{
data[i] = rand();
}
Node<int> *link = BuildLink(data,dataLen);
MergeSort(link, IntCmp);

while(link != NULL)
{
printf("%d ", link->data);
link = link->next;
}
}

      做了这个实现后,我也问了一些人这样是否是最高效的了,结果一ACM大牛坚持认为快排才是最高效的,认为快排的均摊就是好于原地归并。这个我是不同意的,因为我还没想到单向链表上快排的高效实现(一个交换操作就很够呛了),而且即使实现了,均摊也几乎没可能超过这里的原地归并实现(这里最差就O(nlgn),系数大不到哪去,就是遍历链表的开销,而快排最优下也是O(nlgn),遍历的开销同样无法避免)。

      不过我只是一介菜鸟耳,希望听听各位高手的看法。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sgzwkrm/archive/2009/06/02/4235199.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
静态链表是一种基于数组的链表实现方式,它可以在不使用指针的情况下实现链表的基本操作。静态链表实现归并排序的基本思路是将数组分成若干个小的有序数组,然后将这些有序数组两两合并,直到最终得到一个完整的有序数组。 以下是静态链表实现归并排序的步骤: 1. 定义静态链表结构体,包含数据和指向下一个元素的指针。 2. 定义归并排序函数,该函数接收一个静态链表作为参数。 3. 在归并排序函数中,首先判断链表是否为空或只有一个元素,如果是,则直接返回。 4. 如果链表中有多个元素,则将链表分成两个部分,分别递归调用归并排序函数。 5. 在递归调用结束后,将两个有序链表合并成一个有序链表。 6. 合并两个有序链表的方法是,比较两个链表的头元素,将较小的元素插入到新链表中,并将指针指向下一个元素。 7. 最后返回新链表。 以下是静态链表实现归并排序的Python代码示例: ```python class Node: def __init__(self, val, next=None): self.val = val self.next = next def merge_sort(head): if not head or not head.next: return head mid = get_mid(head) right = mid.next mid.next = None left = head left = merge_sort(left) right = merge_sort(right) return merge(left, right) def get_mid(head): if not head: return head slow = head fast = head while fast.next and fast.next.next: slow = slow.next fast = fast.next.next return slow def merge(left, right): dummy = Node(0) cur = dummy while left and right: if left.val < right.val: cur.next = left left = left.next else: cur.next = right right = right.next cur = cur.next if left: cur.next = left if right: cur.next = right return dummy.next ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值