代码来自闵老师”日撸 Java 三百行(41-50天)“,链接:https://blog.youkuaiyun.com/minfanphd/article/details/116975863
最近对于代码学习的执行力越来越差,每天断断续续学,结果一个归并排序用了这么久。今天对归并排序进行一下总结梳理。
归并排序的思想是将数据分成一个一个的数据,然后每次按照2个组,合并在一起排序,直到最后合并成一组数。共进行log n轮排序,每一轮共有length / (tempSize * 2)组,其中tempSize指的是该轮循环的步长。
/**
* *******************************************************
* Merge sort. Results are stored in the member variable data.
* *******************************************************
*/
public void mergeSort() {
// Step 1. Allocate space.
int tempFirstStart, tempSecondStart, tempSecondEnd;
int tempFirstIndex, tempSecondIndex;
int tempRow;
int tempNextRow = 0;
int tempActualRow;
int tempGroups;
int tempGroupNumber;
int tempNumCopied;
DataNode[][] tempMatrix = new DataNode[2][length];
for (int i = 0; i < length; i++) {
System.out.print(data[i]);
}//of for i
System.out.println();
//copy data.
for (int i = 0; i < length; i++) {
tempMatrix[0][i] = data[i];
}//of for i
tempRow = -1;
for (int tempSize = 1; tempSize < length; tempSize = tempSize * 2) {
// Reuse the space of the two rows.
tempRow++;
tempActualRow = tempRow % 2 ;
tempNextRow = (tempRow + 1) % 2;
tempGroups = length / (tempSize * 2);
if (length % (tempSize * 2) != 0) {
tempGroups++;
}//of if
for (tempGroupNumber = 0; tempGroupNumber < tempGroups; tempGroupNumber++) {
tempFirstStart = 2 * tempGroupNumber * tempSize;
tempSecondStart = 2 * tempGroupNumber * tempSize + tempSize;
if (tempSecondStart > length - 1) {
// Copy the first part. Because the following three while loops
//do not meet this condition, you need to copy the data of the first group.
for (int i = tempFirstStart; i < length; i++) {
tempMatrix[tempNextRow][i] = tempMatrix[tempActualRow][i];
}//of for i
continue;
}//of if
tempSecondEnd = 2 * tempGroupNumber * tempSize + 2 * tempSize - 1;
//Because the following three while loops contain conditions
//that meet this condition, there is no need to copy data.
if (tempSecondEnd > length - 1) {
tempSecondEnd = length - 1;
}//of if
tempFirstIndex = tempFirstStart;
tempSecondIndex = tempSecondStart;
tempNumCopied = 0;
System.out.println("tempFirstStart & tempSecondStart" + tempFirstStart + " " + tempSecondStart);
while ((tempFirstIndex <= tempSecondStart - 1) && (tempSecondIndex <= tempSecondEnd)) {
if (tempMatrix[tempActualRow][tempFirstIndex].key <= tempMatrix[tempActualRow][tempSecondIndex].key) {
tempMatrix[tempNextRow][tempFirstStart + tempNumCopied] = tempMatrix[tempActualRow][tempFirstIndex];
tempFirstIndex++;
}else {
tempMatrix[tempNextRow][tempFirstStart + tempNumCopied] = tempMatrix[tempActualRow][tempSecondIndex];
tempSecondIndex++;
} //of if
tempNumCopied++;
System.out.println("tempNumCopied of while 1: " + tempNumCopied);
}//of while
while (tempFirstIndex <= tempSecondStart - 1) {
tempMatrix[tempNextRow][tempFirstStart + tempNumCopied] = tempMatrix[tempActualRow][tempFirstIndex];
tempFirstIndex++;
tempNumCopied++;
}//of while
System.out.println("tempNumCopied of while 2: " + tempNumCopied);
System.out.println("tempSecondEnd of while 2: " + tempSecondEnd);
while (tempSecondIndex <= tempSecondEnd) {
tempMatrix[tempNextRow][tempFirstStart + tempNumCopied] = tempMatrix[tempActualRow][tempSecondIndex];
tempSecondIndex++;
tempNumCopied++;
}//of while
System.out.println("tempNumCopied of while 3: " + tempNumCopied);
System.out.println("tempSecondEnd of while 3: " + tempSecondEnd);
}//of for tempGroupNumber
}//of for tempSize
data = tempMatrix[tempNextRow];
}//of mergeSort
/**
* ********************************************************
* Test the method.
* ********************************************************
*/
public static void mergeSortTest() {
int[] tempUnsortedKeys = { 5, 3, 6, 10, 7, 1, 9 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
tempDataArray.mergeSort();
System.out.println("Result\r\n" + tempDataArray);
}//of mergeSortTest
归并排序的思路梳理如下:
1、分配空间,需声明各种变量。变量有归并时第一组的开始、第二组的开始和结尾;归并时第一组的序列、第二组的序列;数据在每一轮的组数、组序列;申请的两个排序空间循环使用,就存在当前列和下一列。还需要两个变量做“标尺”变量,一个是tempRow,用于自生长记录然后对2取余循环使用分配的两组空间;第二个是用于记录当前归并组内排序了几个数(这里记为tempNumCopied),然后下一个排序的数就放在tempNextRow的firstIndex + tempNumCopied 的位置。
2、for循环,用于控制循环的轮数,步长(tempSize)递增方式为每次乘以2。该for循环里有一些准备工作,比如两组空间的循环使用,还有组数tempGroups的计算,如果不为整数采取上取整。
3、for循环内部嵌套的第二个for循环,作为主要是按照组数循环,即每一轮需要归并多少次。该for循环也有一些准备工作,需要初始化第一组的开始以及第二组的开始和结束;初始化第一组、第二组的序列,初始化每一轮当前组排序个数tempNumCopied。这里需要注意tempSecondEnd如果有大于length-1,即超过数据总长度的情况,需修改tempSecondEnd为数据总长度。
4、正式进行归并排序,分为三种情况进行while循环。
第一个while循环是每一组的第一个小组的序列小于第二个小组的开头,且第二个小组的序列小于第二个小组的结束。也就是说当前的while循环处理的是两个小组内的数据都没有排序结束的情况。
第二个while循环是每一组第一个小组的序列小于第二个小组的开始,也就是第二个小组已排序完成,但是第一个小组没有排序完成的情况。此时将第一小组的剩余数据全部复制到tempNextRow的对应位置即可。
第三个while循环是第一个小组已经排序完成,但是第二个小组还有剩余数据。此时将第二个小组的剩余数据全部复制到tempNextRow的对应位置即可。
5、最终循环结束需要将tempMatrix[tempNextRow](排序好)的数据赋值于data保存。
自己写代码的过程中共出现两个错误:
1、DataNode[][] tempMatrix = new DataNode[2][length];写成了DataNode[][] tempMatrix = new DataNode[2][];出现该错误是数组声明没搞清楚,属于语法错误,数组声明应该是行可以空,列不能空。
2、tempGroups = length / (tempSize * 2);写成了tempGroups = length %(tempSize * 2);出现该错误是逻辑错误,取余第一轮的时候就只有两组。而其实算法的第一轮应该是有length/2组,第二轮应该有length/(2*2)组,依次类推。