快速排序(Quick Sort)是一种改进的排序算法,其平均性能在各种排序算法中最优而被广泛使用。STL中的sort就是对快速排序的一种非递归实现。在嵌入式平台,特别是部分基于DOS的低端嵌入式系统而言,由于编译器年代久远,对C++标准支持都相去甚远,更不可能有STL了,特别是在实模式,并且CPU速度较低,系统的内存和存储空间都紧缺情况下。另外,DBMS在嵌入式上是一种奢侈,即使有嵌入式DB,但在低端系统上也很难得以应用,所以大部分低端嵌入式设备都采用的简单文本方式来存取数据。下面我会自行实现2个CLASS用以在一个数据规则格式文本文件中进行快速排序算法(递归方式)并输出排序后的文件。
代码如下(在Borland C++ 5.02中编译通过):
#include <string.h>
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <unistd.h>
//类名:MsgPair
//作用:储存一个键值对(int,char*)
class MsgPair
{
public:
~MsgPair();
MsgPair();
MsgPair(int i, const char* pc);
void SetPair(int i, const char* pc);
int No() const{return _no;};
void No(int i) {_no = i;};
const char* Msg() const{return _msg;};
void Msg(const char* pc);
MsgPair& operator=(MsgPair& mp);
private:
int _no;
char *_msg;
};
/* public function */
MsgPair::~MsgPair()
{
delete[] _msg;
_msg = NULL;
_no = 0;
}
MsgPair::MsgPair()
{
_no = 0;
_msg = NULL;
}
MsgPair::MsgPair(int i, const char* pc)
{
SetPair(i, pc);
}
//重载=操作符,赋值给this对象(深拷贝)
MsgPair& MsgPair::operator=(MsgPair& mp)
{
if(&mp == NULL)
{
delete[] _msg;
_msg = NULL;
_no = 0;
}
else
{
_no = mp._no;
delete[] _msg;
_msg = new char[strlen(mp._msg) + 1];
strcpy(_msg, mp._msg);
}
return *this;
}
//重新给该键值对复制(深拷贝)
void MsgPair::SetPair(int i, const char* pc)
{
_no = i;
_msg = NULL;
Msg(pc);
}
//设置_msg私有字段(深拷贝)
void MsgPair::Msg(const char* pc)
{
if(_msg != NULL)
delete[] _msg;
if(strlen(pc) > 0)
{
_msg = new char[strlen(pc)+1];
strcpy(_msg, pc);
}
}
/* End of Class MsgPair */
//类名:MsgQuickSort
//作用:用快速排序算法对一个规则数据文本文件进行排序,把排序后的数据输出到指定文件
class MsgQuickSort
{
public:
~MsgQuickSort();
MsgQuickSort(const char* filename, int lineLength, int msgOffset, int msgLength);
void SetMsgFormat(const char*filename, int lineLength, int msgOffset, int msgLength);
int LoadMsgFromFile();
int ParseMsg(FILE* fp);
long GetFileSize(FILE *fp);
int Sort();
void DisplayAll();
int SaveToFile(const char* filename);
private:
int Partition(MsgPair *(&mp), int low, int high);
void QSort(MsgPair *(&mp), int low, int high);
unsigned int _msgPairNum;
MsgPair* _pMsgPair;
const char* _filename;
int _lineLength, _msgOffset, _msgLength;
};
/* public function */
MsgQuickSort::~MsgQuickSort()
{
delete[] _filename;
_filename = NULL;
delete[] _pMsgPair;
_pMsgPair = NULL;
}
MsgQuickSort::MsgQuickSort(const char* filename, int lineLength, int msgOffset, int msgLength)
{
_pMsgPair = NULL;
_filename = NULL;
_msgPairNum = 0;
SetMsgFormat(filename, lineLength, msgOffset, msgLength);
}
//设置数据规则(filename=文件名; lineLength=行长度(注意行结束符); msgOffset=关键字偏移量; msgLength=关键字长度)
void MsgQuickSort::SetMsgFormat(const char*filename, int lineLength, int msgOffset, int msgLength)
{
_lineLength = lineLength;
_msgOffset = msgOffset;
_msgLength = msgLength;
if(_filename != NULL)
{
delete[] _filename;
_filename = NULL;
}
_filename = new char[strlen(filename) + 1];
strcpy((char*)_filename, filename);
}
//从文件中加载关键字段数据到内存中
int MsgQuickSort::LoadMsgFromFile()
{
FILE *fp;
int ret = 0;
fp = fopen(_filename, "rb");
if(fp != NULL)
{
ret = ParseMsg(fp);
fclose(fp);
}
return ret;
}
//获得文件大小
long MsgQuickSort::GetFileSize(FILE *fp)
{
struct stat buf;
fstat(fileno(fp), &buf);
return buf.st_size;
}
//提取关键字,保存到内存
int MsgQuickSort::ParseMsg(FILE* fp)
{
long fileSize = 0;
char *msg = NULL;
if(fp == NULL)
return -1;
msg = new char[_msgLength + 1];
fileSize = GetFileSize(fp);
_msgPairNum = (fileSize / _lineLength); //一共多少行
_pMsgPair = new MsgPair[_msgPairNum];
for(unsigned int i = 0; i < _msgPairNum; i++)
{
memset(msg, 0, _msgLength + 1);
fseek(fp, long((i * _lineLength) + _msgOffset), SEEK_SET);
fread(msg, 1, _msgLength, fp);
_pMsgPair[i].SetPair(i, msg); //MsgPair从第0开始,到uiMaxItem - 1结束
}
delete[] msg;
return 1;
}
//显示当前内存中的关键数据,以及它对应加载文件的行号(从第0行开始)
void MsgQuickSort::DisplayAll()
{
for(unsigned int i = 0; i < _msgPairNum; i++)
{
printf("[%05d]:[%s]\r\n", _pMsgPair[i].No(), _pMsgPair[i].Msg());
}
}
// 保存当前的顺序到文件(未执行排序函数将保持原顺序)
int MsgQuickSort::SaveToFile(const char* saveFilename)
{
char *msg = NULL;
FILE *fp_save;
FILE *fp_from;
unlink(saveFilename);
fp_save = fopen(saveFilename, "ab");
fp_from = fopen(_filename, "rb");
if(fp_save == NULL)
return -1;
else if(fp_from == NULL)
{
fclose(fp_save);
return -1;
}
msg = new char[_lineLength + 1];
for(unsigned int i = 0; i < _msgPairNum; i++)
{
memset(msg, 0, _msgLength + 1);
fseek(fp_from, long(_pMsgPair[i].No() * _lineLength), SEEK_SET);
fread(msg, 1, _lineLength, fp_from);
fwrite(msg, 1, _lineLength, fp_save);
}
delete[] msg;
fclose(fp_save);
fclose(fp_from);
return 1;
}
//数据排序
int MsgQuickSort::Sort()
{
QSort(_pMsgPair, 0, _msgPairNum-1);
}
/* private function */
//快速排序法一趟排序
int MsgQuickSort::Partition(MsgPair *(&mp), int low, int high)
{
MsgPair pivotkey;
pivotkey = mp[low];
while(low<high)
{
while(low<high && strcmp(mp[high].Msg(), pivotkey.Msg()) >= 0)
{
--high;
}
mp[low] = mp[high];
while(low<high && strcmp(mp[low].Msg(), pivotkey.Msg()) <= 0)
{
++low;
}
mp[high] = mp[low];
}
mp[low] = pivotkey;
return low;
}
//快速排序递归函数
void MsgQuickSort::QSort(MsgPair *(&mp), int low, int high)
{
if(low < high)
{
int pivotloc = Partition(mp, low, high);
QSort(mp, low, pivotloc-1);
QSort(mp, pivotloc+1, high);
}
}
/* End Of Class MsgQuickSort */
/* Test */
int main()
{
MsgQuickSort mqs("msg.txt", 41, 0, 10); //待排文件msg.txt;每行41个字节;关键字行中偏移0,长度10字节
mqs.LoadMsgFromFile(); //加载文件中的关键字到内存
mqs.Sort(); //快速排序(这里是按关键字的ASCII码进行排序)
mqs.DisplayAll(); //显示目前的关键字顺序(排序后)
mqs.SaveToFile("sortmsg.txt"); //把根据关键字进行排序后的数据保存到sortmsg.txt文件
getch();
return 0;
}
msg.txt示例:
7905C5AEFC06120720503312018162236739999
7903FADFF106120720504012018162236739999
7F7F56E23006120720505212018162236739999
7A9686FB5106120720505412018162236739999
7A4C7B723F06120720505512018162236739999
79093698CA06120720505612018162236739999
7A9686FB5106120720505412018162236739999
7A4C7B723F06120720505512018162236739999
79093698CA06120720505612018162236739999
7A9686FB5106120720505412018162236739999
7A4C7B723F06120720505512018162236739999
sortmsg.txt输出:
7903FADFF106120720504012018162236739999
7905C5AEFC06120720503312018162236739999
79093698CA06120720505612018162236739999
79093698CA06120720505612018162236739999
7A4C7B723F06120720505512018162236739999
7A4C7B723F06120720505512018162236739999
7A4C7B723F06120720505512018162236739999
7A9686FB5106120720505412018162236739999
7A9686FB5106120720505412018162236739999
7A9686FB5106120720505412018162236739999
7F7F56E23006120720505212018162236739999
实现过程分析:
1、快速排序(Quick Sort)在各种排序方案中的平均时间效率最高。
2、因为嵌入式系统中多用FLASH(主要是NOR和NAND)进行存取,尽可能应该减少FLASH读写,在内存中处理效率高且磨损率低。所以采用对应原文件行号+关键字方式读取到内存中进行排序处理,然后根据排序后的顺序,通过对应原文件行号,进行查找输出后保存。一般来说,即使低端嵌入式,上DOS系统后,总内存空间也应该在512KB左右,FLASH也至少应该采用1MB(保证正常操作系统以及主程序存放),按照本例子,关键字10字节,假设有1W行数据,进行上述运算时候占内存约=10*10K=100KB。
3、另外,在老编译器下,没有简单且现成的可用方案来避免内存碎片(主要指外部碎片)的分配工具,基于这种情况,在MsgQuickSort的生存周期内,尽可能不要插入其他涉及动态分配内存语句,以此也可以减少在这过程中产生的内存碎片(外部碎片)的几率。