算法与数据结构——排序(八)归并排序的非递归实现
2012-11-18 16:08 左眼微笑右眼泪 阅读(776) 评论(0) 编辑 收藏 举报前面我们用递归的方法实现了归并排序,递归比较占用内存空间,能不能用非递归的方法来实现归并排序呢。答案当然是可以的。
使用非递归排序的思路如下(以序列{0,4,8,9,7,1,3,9,2}为例):
1.首先s=1,把数组中数两两归并,并放入一个新数组中(一般就拿它本身的那个数组来放),如果最后多一个数(例如最后一个数字2单独为一组),那么直接把这个数加入到新数组中去。这步完成后,每相邻的两个数内部是排好了序的,在这一步里面,会对9个序列(每个数是一个序列)进行归并,最终得到5个序列。
2.第二步s=2,把第1到第4个数,第5到第8个数进行归并,最后一个数字2又是单独的一个序列,不参与归并。第二步完成后,从第一个数开始,每相邻的4个数是排好序了的,在这一步里面会对5个序列(每2个数一个序列,最后一个数单独一个序列)进行归并,最终得到3个序列。
3.第三步s=4,把第1到8个数进行归并,最后一个数字2又是单独的一个序列,归并完成后,前面8个数内部是有序的,这一步里面会对3个序列(每4个数一个序列,最后一个数单独一个序列)进行归并,最终得到2个序列。
4第四步s=8,把第1到16个数进行归并,这时候最后一个数字2就参与到归并中去了,归并完成后,整个序列就都是有序的了,这一步里面会对两个序列(每8个数一个序列,最后一个数单独一个序列)进行归并,最终得到一个序列。
下面看具体的代码实现:
首先是通用的合并两个有序list的方法:
/// <summary>
/// 把两个有序的序列A和B合并到一个有序列temp里面
/// </summary>
/// <param name="sortListA">有序序列A</param>
/// <param name="sortListB">有序序列B</param>
/// <param name="tempList">有序序列Temp</param>
public void Merge(List<int> sortListA, List<int> sortListB, List<int> tempList)
{
int countB = 0;
int countA = 0;
//1.以某个序列为原始序列,把它的每一个数,与另外的一个序列做比较
for (int i = 0; i < sortListA.Count; i++)
{
//1.1循环遍历序列B的每一个序列(因为B序列是有序列的,所以第一个元素是最小的)
for (int j = countB; j < sortListB.Count; j++)
{
//1.2如果A序列中的某个元素比B中的小,就把A中的这个元素加到temp中去
if (sortListA[i] < sortListB[j])
{
tempList.Add(sortListA[i]);
countA++;//记录加到temp中去的A序列中元素的数量
break;
}
//1.2否则把B中的元素加到temp中去
else
{
tempList.Add(sortListB[j]);
countB++;//记录加到temp中去的B序列中元素的数量
}
}
}//注意,此for循环结束后,一定有一个序列中的元素全部加到了temp中去了。
//如果此处不相等,代表B中的元素还没有加完,那么把B中剩下的元素加入到Temp中去
if (countB != sortListB.Count)
{
for (int k = countB; k < sortListB.Count; k++)
{
tempList.Add(sortListB[k]);
}
}
//否则的话,代表A中的元素还没有加完,那么把A中剩下的元素加入到Temp中去
else
{
for (int k = countA; k < sortListA.Count; k++)
{
tempList.Add(sortListA[k]);
}
}
}
然后来看主方法,在这个方法里面采用循环,实现上面的四步归并:
public List<int> SortByMergeNew(List<int> sortList)
{
List<int> resultList=new List<int>();
int i = 1;
while (i<sortList.Count)
{
resultList.Clear();
MergePass(sortList,resultList,i,sortList.Count);
i = 2*i;
sortList.Clear();
MergePass(resultList, sortList, i, resultList.Count);
i = 2*i;
}
return sortList;
}
里面调用了MergePass方法,这个方法的作用就是具体实现上面的每一步,在这个例子里面,这个方法会被调用4遍。
public void MergePass(List<int> sortList,List<int> resultList,int s,int listLength )
{
int i = 0;
int sumCount = listLength - 2*s;
while (i<=sumCount)
{
MergeList(sortList, resultList, i, i + s, i + 2 * s);
i = i + 2*s;//i每次要增加2s
}
if (i < listLength - s)
{
//如果有单数的话,把最后的两个序列归并,(如果有9个数的话,这句话就是把前面8个数与最后一个数归并)
MergeList(sortList, resultList, i, i + s, listLength);
}
else//如果后面有剩余的数,则追加到数组中,如果没有,则不追加
{
for (int k = i; k < listLength; k++)
{
resultList.Add(sortList[k]);
}
}
}
在上面的4个步骤里面,每一步,实现上是对多对序列进行归并的,每对序列的归并是通过MergeList进行实现的,MergeList的作用是产生每个序列,产生后调用最前面的那个Merge方法来实现真正的归并的。
private void MergeList(List<int> sortList, List<int> resultList, int start, int mid,int end)
{
var listA = new List<int>();
var listB = new List<int>();
//如果s等于1的时候,下面的代码是把第0,1//4,5,//8,9装入到一个List中去
for (int j = start; j < mid; j++)
{
listA.Add(sortList[j]);
}
//如果s等于1的时候,下面的代码是把第2,3//6,7装入到一个List中去
for (int k = mid; k < end; k++)
{
listB.Add(sortList[k]);
}
Merge(listA, listB, resultList);//把两个List进行合并
}
通过非递归的方法来实现归并排序,减少了空间的复杂度,空间复杂度为O(n),在时间性能上也有一定的提升。