



排序算法是要整理文件、数据库、表中的记录,使其按照一定的顺序(常用的是递增或递减两种)排列起来;查找算法是在一系列数据中根据输入条件找出要获得的数据。这两类算法在众多的嵌入式软件程序中得到较多的应用。
1.3.4.1 算法的概念
算法是指在确定输入后,要通过一系列计算步骤或方法得到输出结果的过程,其中每个步骤或方法要在有限时间内完成。算法不是一个简单函数,它还有时间复杂度和空间复杂度的计算。所以算法必须能正确地解决一类问题,而不是一个问题,具有通用性。设计一个好的算法要注意以下方面。
(1)即使输入不同,算法也不应无限计算,若没有终止条件,就会陷入僵死状态。
(2)在终止时,算法不能输出错误的或不一致的结果。
(3)一定要评估复杂度,否则可能会影响其他任务的调度。
嵌入式程序中对算法的时间复杂度要求比较高,因为有较高的实时性要求,尤其是内核里的算法实现。如移动电话代码中,要查找某人移动电话中存储的号码,就需要选择一个好的算法,以快速响应。
1.3.4.2 排序算法
排序算法有很多种,如插入排序、选择排序、冒泡排序、快速排序、堆排序和归并排序等。本节给出目前常用的5种排序算法的代码,可直接移植运行,供读者参考。
1.常用排序算法
(1)插入排序算法
算法描述:将一个待排序的数据元素,插入已经有序(通常是从小到大或从大到小的顺序)的数列中的适当位置,要求插入后数列依然按照原先的顺序,循环直到待排序数据元素全部处理完。
假设按从小到大的顺序排序,这里用数组存储待排序元素,用一个临时变量temp 存储无序区的第一个元素;temp和有序区元素逐一比较,找到比temp小的元素p,p右边的有序元素逐一后移一位;然后在p后插入temp。
void InsertSort (int a[], int n)
{
/*将输入数组a[1..n]按递增顺序进行插入排序,a[0]是监视哨*/
int i,j;
int temp;
/*依次插入a[2],...,a[n]*/
for (i =0; i <n;i++)
{
temp = a[i];
j = i - 1;
while (temp < a[j])//逐个比较
{
a[j+1] = a[j];
j = j- 1;
}
a[j + 1] = temp;
}
return;
}
(2)选择排序算法
算法思想:根据预定目标,在一组数据中每次选出最小(或最大)的一个元素,放在已排好序的数列最后(或最前),再反复此过程,直到全部数据处理结束。
voidSelectSort (int a[], int n)
{
int i,j, k;
int temp;
/*做n-1趟选择排序*/
for (i = 0;i < n-1;i++)
{
k = i;
/*在当前a[i..n]中选最小的元素a[k]*/
for (j = i+1;j < n;j++)
{
if (a[j] < a[k])
k=j;
}
if (k != i)
{
temp = a[i];
a[i] = a[k];
a[k] = temp;
}
}
return;
}
(3)冒泡排序算法
算法思想:按预定目标每次比较一组数据中的两个元素,若发现它们的大小顺序相反,则直接交换,直到所有数据有序为止。
voidBubbleSort (int a[], int n)
{
int i,j;
int temp, flag;
/*从下向上扫描的起泡排序*/
for (i = 1;i <= n-1;i++)
{
/*置未排序的标志为真*/
flag = true;
for (j = n-1;j >= 0;j--)
{
if (a[j+1] < a[j])
{
/*交换元素*/
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
flag = false;
}
}
if (flag)
return;
}
return;
}
这种算法可形象理解为:一组数据中的元素对应一个重量的气泡,所有气泡有轻重之分。若要求轻气泡在重气泡上面,从下往上扫描这个数组。当轻气泡元素在重气泡下时就使其上浮,重气泡下沉,如此反复进行,直至所有元素对应的气泡重量完全有序为止。
(4)快速排序算法
算法思想:在当前无序数组a[1..n]中先任取一个数据元素作为基准数X=a[i],用基准数将当前无序数组划分为左右两个较小的无序数组:a[1..i−1]和a[i+1..n],基准数位于最终排序的位置上,左边和右边两组数满足a[1..i−1]≤X≤ a[i+1..n] (1≤i≤n),然后再对左边、右边的数组元素重复上述的划分过程,直至所有元素均有序为止。
int Parttion (int a[], int low, int high )
{
int i,j;
int temp = a[low];
i = low;
j = high;
/*从右向左扫描,查找第一个小于temp的元素*/
while (i < j)
{
while ((i < j)&& (temp < a[j]))
j− −;
if (i <j)
{
a[i] = a[j];
i++;
}
while ((i < j)&& (temp >= a[i]))
i++;
if (i <j)
{
a[j] = a[i];
j− −;
}
}
a[i] = temp;
return i;
}
void QuickSort (int a[], int low, int high)
{
int pos = 0;
if (low < high)
{
pos = Parttion (a,low,high);
QuickSort (a,low, pos −1);
QuickSort (a, pos +1,high);
}
return;
}
(5)堆排序算法
算法思想:将key[1..n]视为完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点的内在关系来确定最小元素。即先建立树形结构的堆,将当前无序数组调整为一个大根堆/小根堆,再选择关键值最大或最小的堆顶元素与堆的最后一个元素进行交换。
void HeapAdjust (int sortdata[],int index, int length)
{
int childIndex, tmpData;
while (2* index +1 <length)
{
childIndex = 2* index +1 ;
if (2* index +2 <length)
{
/*比较左子树和右子树,记录最大值的index*/
if (sortdata[2* index +1]< sortdata[2* index +2])
childIndex = 2* index +2;
}
If (sortdata[index] <sortdata[childIndex])
{
tmpData = sortdata[index];
sortdata[index] = sortdata[childIndex];
sortdata[childIndex] =tmpData;
/*重新调整堆*/
index = childIndex;
}
else
break;/*比较左右孩子均大则堆未被破坏,不再需要调整*/
}
return;
}
void HeapSortData (int sortdata[], int length)
{
int i, tmpData;
/*建成大根堆*/
for (i = length/2−1; i>=0; i− −)
{
HeapAdjust (sortdata, i, length);
}
for (i = length−1; i>0; i− −)
{
tmpData = sortdata[0];
sortdata[0] = sortdata[i];
sortdata[i] =tmpData;
/*重新调整为大根堆*/
HeapAdjust (sortdata, 0, i);
}
return;
}
2.排序算法的比较和选择
选取排序算法必须要考虑复杂度,还有待排序的元素个数、元素信息大小、所选择的关键字和算法的应用场景等。例如,元素个数较少和较多时采用的排序算法不应相同。当元素的信息量较大时,可用链表作为存储结构。链表节点用指针做链接,可避免耗费大量移动时间。目前很多移动电话存储号码的排序使用快速排序算法,因为它的时间复杂度不大,在内部排序中使用较广。读者可先根据需求模拟具体的应用写出算法,再计算出运行时间和响应时间,优选后确定具体的算法类型。
1.3.4.3 查找算法
查找算法有很多种,主要有二分查找、顺序查找、哈希查找和二叉树查找等,目的是从一组数据中找到符合条件的数据。本节列出常用的4种算法的代码,可直接在嵌入式Linux软件环境下运行,供读者参考。
1.二分查找算法
二分查找算法有个前提要求是:待查找的数组采用顺序存储结构,且已是有序排列。因为该算法的思想是:先将所有 num 个元素(假设数组是升序排列)分成两组,每组元素数量大致相同,将输入的待查找数与第num/2个数比较,若两者相等则查找成功,算法结束;若小于则在前一组中继续查找;若大于则在后一组中继续查找,以此类推,这种查找算法的效率较高。若数据是无序性的,则无法确定待查找范围。
Int BinarySearch (int a[], int size, int key)
{
int low = 0, high = size – 1, mid;
while (low <= high)
{
/*获取中间的位置*/
mid = (low + high) / 2;
if (a[mid] = = key)
return mid;
if (a[mid] > key)
high=mid–1; /*向低的位置查找*/
else
low=mid+1; /*向高的位置查找*/
}
return −1;
}
2.顺序查找算法
顺序查找算法流程最为简单,具体思想是:用待查找数据和各个元素逐个比较,若找到与其相等的元素,则表示查找成功,算法结束;否则一直查找到数组最后一个元素,若还未找到与其相等的元素,则查找失败。
int SeqSearch (int a[],int key, int n)
{
int i =0;
a[n−1]=key;
while (a[i]!=key)
i++;
if (i < n−1)
return i;
else
return −1;
}
3.哈希查找算法
算法思想:先构建哈希表,根据待查找数据的特征来设计哈希函数,利用该函数计算出各元素在哈希表中的存储位置。查找时对待查键值计算出哈希地址,并将此键值映射到表中一个对应位置来确定查找是否成功。好的哈希函数可大大加快查找的速度,减少冲突。
#define m 13
#define n 10
#define p 11
#define MV 0
int hash (int k)
{
return (k%p);
}
int linerdetect (int *hl,int k)
{
int x=hash (k), i =1;
while (hl[x] != MV)
{
x = (x+i)%m;
i++;
}
return x;
}
void createhash (int *hl,int *x)
{
int i;
for (i =0;i <n;i++)
{
if (hl[hash (x[i])] = = MV)
hl[hash (x[i])] = x[i];
else
hl[linerdetect (hl,x[i])] = x[i];
}
}
int hashsearch (int *hl, int k)
{
int d,temp;
/*计算k的哈希地址*/
d =hash (k);
temp=d;
while (hl[d]!=MV)
{
if (hl[d]= =k)
return d;
else
d = (d+1)%n;
if (d= =temp)
return −1;
}
return −1;
}
/*检测哈希算法的运行效果*/
main ()
{
int hl[m],x[9]={57,7,69,11,16,82,22,8,3};
int i, key;
for (i =0;i <m;i++)
hl[i]=MV;
createhash(hl,x); /*创建哈希表*/
for (i =0;i <9;i++)
{
key = x[i];
printf ("search : %d\n", hashsearch (hl,key));
}
return;
}
4.二叉树查找算法
二叉树查找算法采用数据结构中二叉树的思想:先建立一个二叉树,将所有元素分成两组,每组元素数量大致相同,通常右边的数比左边大,即根节点的右子树>根>左子树。查找时遍历二叉树,先将待查找数与根比较,若两者相等则直接返回;若大于则在右子树中查找;若小于则在左子树中查找,逐层深入,一直到确定最终位置。
typedef struct node
{
int data;
struct node * lchild;
struct node * rchild;
} node,*Tree;
/*树的根*/
Tree root = NULL;
void insert (Tree s)
{
Tree p,f;
if (root= =NULL)
root=s;
else
{
p = root;
while (p)
{
if (s->data<p->data)
{
f =p;
p =p->lchild;
}
else
{
f =p;
p =p->rchild;
}
}
if (s->data<f->data)
f->lchild=s;
else
f->rchild=s;
}
}
void createTree ()
{
Trees;
int x;
printf ("输入0 end \n");
scanf ("%d",&x);
while (x!= 0)
{
s = (Tree)malloc (sizeof (node));
s->data=x;
s->lchild=s->rchild=NULL;
insert (s);
scanf ("%d",&x);
}
}
Tree findnode (int key)
{
Tree p= root;
while (p)
{
if (p->data= =key)
return p;
else if (p->data>key)
p=p->lchild;
else
p=p->rchild;
}
return NULL;
}
main() /*检验算法*/
{
Tree p;
int key, m;
while (1)
{
printf("1——建树;2——查找; 0——退出\n");
scanf ("%d",&m);
switch (m)
{
case 1:
createTree ();
break;
case 2:
printf ("输入要查的数字:");
scanf ("%d",&key);
p=findnode (key);
if (p!=NULL)
printf ("存在该数:%d\n", p->data);
break;
}
if (m= =0)
break;
}
}