二分检索
基本思想
通过 x 与中位数的比较,将原问题归结为规模减半的子问题,如果 x 小于中位数,则子问题由小于 x 的数构成,否则子问题由大于 x 的数构成。
步骤
- 假设表中元素是按升序排列
- 将表中间位置记录的关键字与检索关键字比较,如果两者相等,则检索成功;
- 否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于检索关键字,则进一步检索前一子表,否则进一步检索后一子表。
- 重复以上过程,直到找到满足条件的记录,检索成功;或直到子表不存在为止,此时检索不成功。
优点
- 比较次数少。
- 检索速度快。
- 平均性能好。
缺点
- 待查表为有序表。
- 待查表必须采用顺序存储结构。
- 插入删除困难。
- 只适用于不经常变动而检索频繁的有序列表。
时间复杂度分析
二分检索法充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。
代码实现
#include <stdio.h>
int BinSearch(int key,int a[],int n)
{
int left,right,mid;
left=0;
right=n-1;
while(left<=right)
{
mid=(left+right)/2;
if(key<a[mid]) //key小于中间值时
right=mid-1;//确定左子表范围
if(key>a[mid]) //key 大于中间值时
left=mid+1;//确定右子表范围
if(key==a[mid])//当key等于中间值时,证明查找成功
{
printf("查找成功! a[%d]=%d\n",mid,a[mid]);
break;
}
}
if(left > right)
printf("查找失败!\n");
return 0;
}
int main()
{
int key = 5;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
BinSearch(key,a,10);
return 0;
}
快速排序
基本思想
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此让整个数据变成有序序列。
步骤
- 设定一个分界值(通常选用数组的第一个数)
通过该分界值将数组分成左右两部分。 - 分组
将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。 - 左边和右边的数据可以独立排序
对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据做类似处理。 - 重复上述过程
递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
算法步骤
- 设置两个变量i、j,排序开始时:i=0,j=N-1;
- 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
- 从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
- 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;
- 重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束。
代码实现
#include <stdio.h>
void Qsort(int *a, int left, int right)
{
if(left >= right)return;
int i = left;
int j = right;
int key = a[left];
while(i < j) /*控制在当组内寻找一遍*/
{
while(i < j && key <= a[j])
{
j--;/*向前寻找*/
}
a[i] = a[j];
while(i < j && key >= a[i])
{
i++;
}
a[j] = a[i];
}
a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
Qsort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
Qsort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
}
int main()
{
int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24};
Qsort(a, 0, sizeof(a) / sizeof(a[0]));
for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%d ",a[i]);
}
return 0;
}
效率分析
- 快速排序算法的时间复杂度与划分的趟数有关。
- 最好情况下,每次划分所选择的中间数恰好将当前序列几乎等分,时间复杂度为O(nlogn)。
- 最坏情况下,每次所选的中间数是当前序列中的最大或最小元素,时间复杂度为O(n2)。
- 为改善最坏情况下的时间性能,可采用其他方法选取中间数。通常采用“三者值取中”方法,即比较H->r[low].key、H->r[high].key与H->r[(10w+high)/2].key,取三者中关键字为中值的元素为中间数。
- 平均时间复杂度是O(nlogn)。因此,该排序方法被认为是目前最好的一种内部排序方法。
- 从空间性能上看,快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。
- 最好情况下,所需栈的最大深度为log2(n+1)。
- 最坏情况下,所需栈的最大深度为n。
- 空间复杂度为O(logn))。
- 快速排序是一种不稳定的排序算法。
归并排序
基本思想
- 划分
将原问题归结为规模为n/2 的2 个子问题,继续划分,将原问题归结为规模为n/4 的4 个子问题. 继续…,当子问题规模为1 时,划分结束。 - 归并
从规模1到n/2,陆续归并被排好序的两个子数组。每归并一次,数组规模扩大一倍,直到原始数组。