创建时间:2006年5月28
属性:原创
内容简介:快速排序,堆排序
关键字:快速排序,堆排序,排序
前序
从某网上看到google的面试题目,要求从1000000个数中选出10个最大的数,题目好像有些挑战性,想了半天,只知道用快速排序或堆排序或者归并排序比较好,于是开始动手写写。
一.随即生成数字(异常就懒得写了)
void CreateTheDataFile(long *&data, long &count)// data为引用一个指针,以便传回生成的数组
// count为data数组中数据的个数
{
char num[9] = {'/0'};
CFile file;
if(!file.Open("num.dat",
CFile::modeReadWrite |
CFile::modeCreate |
CFile::shareDenyNone |
CFile::typeBinary))
return;
srand((long)time(NULL));
for (int i = 0; i< 0xFFFFF; i++) //生成1000000个数字
{
int j = rand(); //随机生成数字
int temp = j;
int k = 0; // 数字的位数
if (j % 10 != 0)
{
for(;j % 10 != 0;) // 以下生成数字的位数
{
j /= 10;
k++;
}
}
else
{
for(;j > 0;) // 以下生成数字的位数
{
j /= 10;
k++;
}
}
j = temp;
_itoa(j, num, 10);
file.Write(num, k); // 只写k位数字
file.Write("/n", 1);
}
/**/
long *num2 = new long[0xFFFFFF]; //申请16M的空间,存储要比较的整数
if (num2 == NULL)
return;
memset(num2, '/0', sizeof(num2));
char num3[2048] = {'/0'};
char num5[1][9]; // 存放字符串,表示一个整数字符串
memset(num5[0], '/0', sizeof(num5));
file.SeekToBegin();
long i2 = 0, j = 0, k = 0;
for (DWORD ii = 0; ii<=file.GetLength();)
{
if(!file.Read(num3, sizeof(num3)))
return;
for (i2 = 0 ; i2< sizeof(num3); i2++) // 把含有“/n”的字符给分离出来
{
if (num3[i2] != '/n') // 如果不是换行符,则是数字字符,把其提出来
num5[0][j++] = num3[i2];
else // 出现换行符,则表示提出来的字符完成了,应该转换成整数,
{
num2[k] = atoi(num5[0]);
memset(num5[0], '/0', sizeof(num5)); // 清空,以便下次用
k++;
j = 0;
continue;
}
}
// 清空num3,以便正确统计k
memset(num3, '/0', sizeof(num3));
ii += sizeof(num3);
}
file.Close();
data = num2;
count = k;
}
二.快速排序和堆排序
考虑到快速排序用递归的时候可能导致栈溢出(即使设置编译参数调整默认堆栈大小大于1M也不行),只好用非递归算法了,关于堆排序很简单。以下为排序算法:
void QuickSort(long data[], long low, long high) // 非递归算法
{
long pivotpos = low, i, j, ii = 0, jj = 0;
long lo = 0, hi = 0;
if (low < 0 || high <0 || low >= high)
return;
long **Qstack = new long*[2]; // 申请一个二维数组
for(i = 0; i<2; i++)
{
Qstack[i] = new long[0xFFFFF];
}
Qstack[0][ii++] = low; // 记录低位数值
Qstack[1][jj++] = high; // 记录高位数值
long PivotNum = data[pivotpos];
while(1)
{
lo = Qstack[0][--ii];
hi = Qstack[1][--jj];
i = lo;
j = hi;
PivotNum = data[lo];
do
{
while(data[j] >= PivotNum && i < j) j--;
if(i < j)
data[i] = data[j];
while(data[i] <= PivotNum && i < j) i++;
if(i < j)
data[j] = data[i];
}while(i < j);
data[i] = PivotNum; // 基准值对位
pivotpos = i;
/* // 对pivotpos的左边进行排序
if(low < pivotpos - 1)
QuickSort(num, low, pivotpos - 1);
*/
if(lo < pivotpos - 1)
{
Qstack[0][ii++] = lo;
Qstack[1][jj++] = pivotpos - 1;
}
/* // 对pivotpos的右边边进行排序
if(pivotpos + 1 < high)
QuickSort(num, pivotpos + 1, high);
*/
if(pivotpos + 1 < hi)
{
Qstack[0][ii++] = pivotpos + 1;
Qstack[1][jj++] = hi;
}
if (ii <= 0)
break;
}
delete []Qstack[0];
delete []Qstack[1];
}
void HeapSift(long data[], long i, long count) // 堆筛选,对第i个数进行筛选,构造初始堆(大堆)
{
long j = 2 * i +1;
long temp = data[i];
while(j <= count - 1)
{
if(i < count - 1 && data[j] < data[j + 1])
j++;
if(temp < data[j] )
{
data[i] = data[j];
i = j;
j = 2 * i + 1;
}
else
break;
}
data[i] = temp; //找到位置,放回temp
}
void HeapSort(long data[], long count) // 堆排序
{
long temp;
long i = count / 2 - 1;
for (; i >= 0; i--)
HeapSift(data, i, count); //对每个数进行筛选
for (i = 1; i <= count - 1; i++) //取出最大值,与堆尾的书进行交换
{
temp = data[0];
data[0] = data[count - i];
data[count - i] = temp;
HeapSift(data, 0, count - i); //再进行筛选
}
}
三.消息函数(我用的是MFC了,当然Win32也行)
消息映射就不写了,大同小异.
void OnQuickSort()
{
long *data, count;
long result[10];
CreateTheDataFile(data, count); // 生成数据文件
QuickSort(data, 0, count-1); // 开始排序
OutPutTensMaxNums(data, result, count); // 输出结果
delete []data;
}
void OnHeapSort()
{
long *data, count;
long result[10];
CreateTheDataFile(data, count); // 生成数据文件
HeapSort(data, count);
OutPutTensMaxNums(data, result, count); // 输出结果
delete []data;
}
四.输出结果(弹出对话框)
void OutPutTensMaxNums(long data[], long result[10], long CountOfNum)
{
long pos = CountOfNum - 10;
char ResultOfNum[9*10 + 1]; //存放显示的结果
char ResultOfTemp[12];
memset(ResultOfNum, '/0', sizeof(ResultOfNum));
memset(ResultOfTemp, '/0', sizeof(ResultOfTemp));
for (int i = 0; i< 10; i++)
{
result[i] = data[pos++];
itoa(result[i], ResultOfTemp, 10);
strcat(ResultOfNum, ResultOfTemp);
strcat(ResultOfNum, "/n");
}
AfxMessageBox(ResultOfNum);
}
五.后记
1.经过测试,以上两个排序算法对1000000个数字进行完全排序大约要几秒钟的时间(cpu p4 2.6G),如果不生成数据文件存到硬盘中那就很省时,关键是生成数字存与读很浪费时间。当然由于自身功底的问题,我暂时还想不到用其他更好的算法(周末去书店看了半天 Donald E. Knuth的《The Art of Computer Programming》也没看懂,网上也找不到更合适的算法,呵呵)。感觉而言,HeapSort在此排序中比较快(无可厚非QuickSort的算法了,总体而言,这是最快的排序算法了!),如果只找出10个的的数字,那么HeapSort可能更快(QuickSort大概也行,我懒得想了,近来的了颈椎病,一考虑问题就头疼,特别是不能低着头看书或搞电脑,原谅!),QuickSort的递归算法只能排大约100个数字,否则就堆栈溢出(因为前面有些数据占用堆栈的空间较大),当然不是一个好的解决办法,非递归更好一些,但占用堆的空间较大,而HeapSort则好一点。MergeSort在这里没有写出,不知什么时间开销,DS书中都称这三个都差不多,而QuickSort更好一点。
2.代码运行中占用内存空间较大70M左右,这可能是个最烂的算法了,前面申请的内存空间太大了,当然有更高效的算法,时间关系就不想了,笨想法就用笨方法解决好了,暂时只是要个结果。
3.以上代码写的很乱,加上思考这个问题的时间零零散散,所以就没什么水平了,望看到这篇文章的各位原谅!
六.参考书目
1.《数据结构辅导与提高》徐孝凯,清华大学出版社
2.《数据结构(C语言版)》严蔚敏 吴伟民,清华大学出版社
3.《数据结构题集(C语言版)》严蔚敏 吴伟民,清华大学出版社)》
4.网络(这里不列出了)