常用的搜索排序算法总结
2016-03-07 16:28
330 查看
搜索排序算法总结
一、
二分查找
算法过程:
A、假设表中元素按升序排列
B、若中间元素与查找目标相等,则查找成功,否则利用中间元素将表划分为前后两个有序子表
C、若查找目标小于中间元素,则在前子表中查找,否则在后子表中查找
D、重复以上过程,直到查找成功,或者因子表不存在而宣告查找失败
评价:
1).效率比线性搜索高,在最坏情况下需要比较log2N次
2).要求表必须是有序表,特别适用于已经建立就很少改动的而又需要经常查找的线性表
3).在结构上具备递归特性,可以采用递归的方式实现
代码实现:
[cpp] view
plain
int BinarySearch(int data[],int left,int right,int key){
int mid = (left + right)/2;
if(left <= right){
if(key < data[mid]){
BinarySearch(data,left,mid-1,key);
}
else if(key > data[mid]){
BinarySearch(data,mid+1,key);
}
else{
return mid;
}
}
else{
return -1;
}
}
二、
排序算法(默认为升序排序)
相关概念:
内排序:在排序过程中,若整个表都是放在内存中处理,排
序时不涉及数据的内、外存交换,称之为内排序
外排序:若排序过程中需要进行数据的内外存交换,则
称之为外排序
稳定排序:假设在待排序的序列中存在相同的元素,在经过排序
后,两个元素之间的相互次序不发生变化,则称之为稳定排序,否则为不稳定排序
1.
交换排序:
基本思想:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。
常见的有冒泡排序和快速排序
1).冒泡排序
算法过程:
A.
相邻元素两两比较,前者大于后者,彼此交换
B.
从第一对到最后一对,最大的元素沉降到最后
C.
针对未排序的部分,重复以上步骤,沉降次大的值
D.
每次扫描越来越少的元素,直至不再发生交换
评价:
A.
平均时间复杂度:O(N2);
B.
属于稳定排序
C.
对数据的有序性非常敏感(即当表越接近有序状态时,冒泡排序可能是最快的排序)
代码实现:
[cpp] view
plain
void bubble_sort(int data[],size_t size){
int i,j;
for(i = 0;i < size - 1;i++){
int ordered = 1;
for(j = 0;j < size-1-i;j++){
if(data[j+1] < data[j]){
int swap = data[j];
data[j] = data[j+1];
data[j+1] = swap;
ordered = 0;
}
}
if(ordered)
break;
}
}
2)快速排序
算法过程:
A.从待排序序列中任意挑选一个元素作为基准
B.将所有小于基准的元素放在基准之前,大于基准的元素放在基准之后,等于基准的元素任意放在基准之前或之后,这个过程叫做分组
C.以递归的方式,分别对基准之前和之后的分组继续进行分组,直到每个分组内的元素个数不多于1个
评价:
A.平均时间复杂度:O(NlogN)
B.不是稳定排序
C.对数据的有序性不敏感
代码实现:
[cpp] view
plain
void QuickSort(int data[],size_t left,size_t right){
size_t mid = (left + right)/2;
int tmp = data[mid];
size_t i = left,j = right;
while(i < j){
for(;i < mid && data[i] <= tmp;i++);//找到大于基准的值的下标
if(i < mid){
data[mid] = data[i];
mid = i;
}
for(;j > mid && data[j] >= tmp;j--);//找到小于基准的值的下标
if(j > mid){
data[mid] = data[j];
mid = j;
}
}
data[mid] = tmp;
if(mid - left > 1)
QuickSort(data,left,mid - 1);
if(right - mid > 1)
QuickSort(data,mid + 1,right);
}
2.插入排序:
基本思想:每次将一个待排序的记录按照关键字的大小插入到
前面已经排好序的子表中的适当位置,直到全部记录插入完成
1).直接插入排序
算法过程:
A.
在开始时将序列划分为两个分区,第一个分区中只有首元素,自然有序,第二个分区中包含剩下的元素
B.
取出下一个元素,对已排序序列进行从后向前扫描
C.
将大于被取出元素者后移
D.
小于或等于被取出元者者,将取出元素插入其中
E.
重复步骤B,直到处理完所有的元素
评价:
A.平均时间复杂度:O(N2)
B.属于稳定排序
C.对数据的有序性非常敏感
D.不交换只移动,效率略高于冒泡排序法
代码实现:
[cpp] view
plain
void InsertSort(int data[],size_t size){
size_t i;
for(i = 1;i < size;i++){
int inserted = data[i];
size_t j;
for(j = i;j > 0 && inserted < data[j-1];j--){
data[j] = data[j-1];
}
if(j != i)
data[j] = inserted;
}
}
2).希尔排序
算法过程:
A.选取一个小于序列长度n的正整数d1作为增量,将序列分为d个分组
B.对每个分组进行直接插入排序,使其变成有序序列
C.选取一个小于d1的正整数d2,将序列分为d2个组
D.重复步骤B和C,直到取得的增量等于1
评价:
A.平均时间复杂度:O(N1.3)[由于希尔排序的性能取决于增量选取的大小,而增量的大小选取并无定论,因此一般认为其平均时间复杂度为O(N1.3)]
B.是不稳定排序
C.排序效率通常比直接插入排序要高
D.属于就地排序
代码实现:
[cpp] view
plain
void ShellSort(int data[],size_t size){
int i,j,gap,tmp;
gap = size/2;
while(gap > 0){
for(i = gap;i <= size;i++){
tmp = data[i];
j = i - gap;
while(j >= 0 && tmp < data[j]){
data[j+gap] = data[j];
j = j - gap;
}
data[j+gap] = tmp;
}
gap /= 2;
}
}
3.选择排序
基本思想:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子表的最后,直到全部的记录排序完毕
常见的有直接选择排序和堆排序
1).直接选择排序
算法过程:
A.
在整个序列中寻找最小元素,找到之后与首元素交换
B.
在剩余序列中寻找最小元素,找到之后与次元素交换
C.
以此类推,直到剩余序列中仅包含一个元素
评价:
A.平均时间复杂度: O(N2)
B.不是稳定排序
C.对数据的有序性不敏感
D.交换次数少,优于冒泡
代码实现:
[cpp] view
plain
void select_sort(int data[],size_t size){
size_t i;
for(i = 0;i < size - 1;i++){
size_t min = i,j;
for(j = i+1;j < size;j++)
if(data[j] <= data[min])
min = j;
if(min != i){
int swap = data[i];
data[i] = data[min];
data[min] = swap;
}
}
}
4.归并排序
基本思想:多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并是直接将两个有序子表归并成一个有序表,即二路归并
算法过程:
A.
将序列中的每个元素独立进行归并(单独一个元素肯定是有序表)
B.
以此类推,不断的对序列进行归并。
C.
若序列中元素的个数为奇数个,则最后有一个子表的长度会小于其他子表,则修改子表上界并对其进行归并
评价:
A.
归并排序是一种稳定排序
B.
归并排序易于在链表上实现。对于长度为n的表,需要进行log2n次排序,每次归并的时间为O(n),其时间复杂度无论是在最好还是最坏的情况下都是O(nlog2n)
C.
归并排序中,每次都需要一个辅助向量来暂存两个有序子表来暂存结果,但在该次排序结束后便释放空间,所以总的辅助空间复杂度为O(n)。归并排序并不是就地排序
代码实现:
[cpp] view
plain
//一趟归并的算法实现
void Merge(int data[],int low,int mid,int high){
int* R1 = (int*)malloc((high - low + 1) * sizeof(int)); //分配一个辅助存储空间
int i = low,j = mid + 1,k = 0;
while(i <= mid && j <= high)
if(data[i] <= data[j]){
R1[k] = data[i];
k++;
i++;
}
else{
R1[k] = data[j];
k++;
j++;
}
//当两个子表的长度不同时,将未完的子表复制到R1中去
while(i <= mid){
R1[k] = data[i];
k++;
i++;
}
while(j <= high){
R1[k] = data[j];
k++;
j++;
}
//将整个R1数组放回到data中去,完成一次归并
for(k = 0,i = low;i <= high;k++,i++)
data[i] = R1[k];
free(R1);
}
void MergePass(int data[],int length,int n){
int i;
for(i = 0;i + 2 * length - 1 < n;i += 2 * length)
Merge(data,i,i + length - 1,i + 2 * length - 1);
//当待排序的数组元素个数为奇数个时,继续归并剩下的两个子表
if(i + length - 1 < n)
Merge(data,i,i + length - 1,n - 1);
}
void MergeSort(int data[],int n){
int length;
for(length = 1;length < n;length *= 2)
MergePass(data,length,n);
}
每一种排序方法都有自己的局限性,应当根据不同的环境选择最为合适的算法。
总结:
1.当待排序的序列个数n较少(n<=50),可以采用直接插入或直接选择排序。当记录规模较小时,直接插入排序较好。否则因为直接选择移动的记录数少于直接插入,则应选择直接选择排序。
2.若序列的状态基本有序,则应选用直接插入、冒泡或随机快速排序为宜
3.当n较大,则应采用时间复杂度为O(nlog2n)的排序方法,快速排序,归并排序。快速排序是目前基于比较的内排序方法中被认为是较好的方法。当待排序关键字是随机分布时,快速排序的平均时间最短。但这种排序是不稳定的,若需要稳定排序,则应采用归并排序。
4.若有两个有序表,要将其合并成一个新的有序表,最好的方法是归并排序
By:http://blog.csdn.net/phoenix500526/article/details/44904141
一、
二分查找
算法过程:
A、假设表中元素按升序排列
B、若中间元素与查找目标相等,则查找成功,否则利用中间元素将表划分为前后两个有序子表
C、若查找目标小于中间元素,则在前子表中查找,否则在后子表中查找
D、重复以上过程,直到查找成功,或者因子表不存在而宣告查找失败
评价:
1).效率比线性搜索高,在最坏情况下需要比较log2N次
2).要求表必须是有序表,特别适用于已经建立就很少改动的而又需要经常查找的线性表
3).在结构上具备递归特性,可以采用递归的方式实现
代码实现:
[cpp] view
plain
int BinarySearch(int data[],int left,int right,int key){
int mid = (left + right)/2;
if(left <= right){
if(key < data[mid]){
BinarySearch(data,left,mid-1,key);
}
else if(key > data[mid]){
BinarySearch(data,mid+1,key);
}
else{
return mid;
}
}
else{
return -1;
}
}
二、
排序算法(默认为升序排序)
相关概念:
内排序:在排序过程中,若整个表都是放在内存中处理,排
序时不涉及数据的内、外存交换,称之为内排序
外排序:若排序过程中需要进行数据的内外存交换,则
称之为外排序
稳定排序:假设在待排序的序列中存在相同的元素,在经过排序
后,两个元素之间的相互次序不发生变化,则称之为稳定排序,否则为不稳定排序
1.
交换排序:
基本思想:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。
常见的有冒泡排序和快速排序
1).冒泡排序
算法过程:
A.
相邻元素两两比较,前者大于后者,彼此交换
B.
从第一对到最后一对,最大的元素沉降到最后
C.
针对未排序的部分,重复以上步骤,沉降次大的值
D.
每次扫描越来越少的元素,直至不再发生交换
评价:
A.
平均时间复杂度:O(N2);
B.
属于稳定排序
C.
对数据的有序性非常敏感(即当表越接近有序状态时,冒泡排序可能是最快的排序)
代码实现:
[cpp] view
plain
void bubble_sort(int data[],size_t size){
int i,j;
for(i = 0;i < size - 1;i++){
int ordered = 1;
for(j = 0;j < size-1-i;j++){
if(data[j+1] < data[j]){
int swap = data[j];
data[j] = data[j+1];
data[j+1] = swap;
ordered = 0;
}
}
if(ordered)
break;
}
}
2)快速排序
算法过程:
A.从待排序序列中任意挑选一个元素作为基准
B.将所有小于基准的元素放在基准之前,大于基准的元素放在基准之后,等于基准的元素任意放在基准之前或之后,这个过程叫做分组
C.以递归的方式,分别对基准之前和之后的分组继续进行分组,直到每个分组内的元素个数不多于1个
评价:
A.平均时间复杂度:O(NlogN)
B.不是稳定排序
C.对数据的有序性不敏感
代码实现:
[cpp] view
plain
void QuickSort(int data[],size_t left,size_t right){
size_t mid = (left + right)/2;
int tmp = data[mid];
size_t i = left,j = right;
while(i < j){
for(;i < mid && data[i] <= tmp;i++);//找到大于基准的值的下标
if(i < mid){
data[mid] = data[i];
mid = i;
}
for(;j > mid && data[j] >= tmp;j--);//找到小于基准的值的下标
if(j > mid){
data[mid] = data[j];
mid = j;
}
}
data[mid] = tmp;
if(mid - left > 1)
QuickSort(data,left,mid - 1);
if(right - mid > 1)
QuickSort(data,mid + 1,right);
}
2.插入排序:
基本思想:每次将一个待排序的记录按照关键字的大小插入到
前面已经排好序的子表中的适当位置,直到全部记录插入完成
1).直接插入排序
算法过程:
A.
在开始时将序列划分为两个分区,第一个分区中只有首元素,自然有序,第二个分区中包含剩下的元素
B.
取出下一个元素,对已排序序列进行从后向前扫描
C.
将大于被取出元素者后移
D.
小于或等于被取出元者者,将取出元素插入其中
E.
重复步骤B,直到处理完所有的元素
评价:
A.平均时间复杂度:O(N2)
B.属于稳定排序
C.对数据的有序性非常敏感
D.不交换只移动,效率略高于冒泡排序法
代码实现:
[cpp] view
plain
void InsertSort(int data[],size_t size){
size_t i;
for(i = 1;i < size;i++){
int inserted = data[i];
size_t j;
for(j = i;j > 0 && inserted < data[j-1];j--){
data[j] = data[j-1];
}
if(j != i)
data[j] = inserted;
}
}
2).希尔排序
算法过程:
A.选取一个小于序列长度n的正整数d1作为增量,将序列分为d个分组
B.对每个分组进行直接插入排序,使其变成有序序列
C.选取一个小于d1的正整数d2,将序列分为d2个组
D.重复步骤B和C,直到取得的增量等于1
评价:
A.平均时间复杂度:O(N1.3)[由于希尔排序的性能取决于增量选取的大小,而增量的大小选取并无定论,因此一般认为其平均时间复杂度为O(N1.3)]
B.是不稳定排序
C.排序效率通常比直接插入排序要高
D.属于就地排序
代码实现:
[cpp] view
plain
void ShellSort(int data[],size_t size){
int i,j,gap,tmp;
gap = size/2;
while(gap > 0){
for(i = gap;i <= size;i++){
tmp = data[i];
j = i - gap;
while(j >= 0 && tmp < data[j]){
data[j+gap] = data[j];
j = j - gap;
}
data[j+gap] = tmp;
}
gap /= 2;
}
}
3.选择排序
基本思想:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子表的最后,直到全部的记录排序完毕
常见的有直接选择排序和堆排序
1).直接选择排序
算法过程:
A.
在整个序列中寻找最小元素,找到之后与首元素交换
B.
在剩余序列中寻找最小元素,找到之后与次元素交换
C.
以此类推,直到剩余序列中仅包含一个元素
评价:
A.平均时间复杂度: O(N2)
B.不是稳定排序
C.对数据的有序性不敏感
D.交换次数少,优于冒泡
代码实现:
[cpp] view
plain
void select_sort(int data[],size_t size){
size_t i;
for(i = 0;i < size - 1;i++){
size_t min = i,j;
for(j = i+1;j < size;j++)
if(data[j] <= data[min])
min = j;
if(min != i){
int swap = data[i];
data[i] = data[min];
data[min] = swap;
}
}
}
4.归并排序
基本思想:多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并是直接将两个有序子表归并成一个有序表,即二路归并
算法过程:
A.
将序列中的每个元素独立进行归并(单独一个元素肯定是有序表)
B.
以此类推,不断的对序列进行归并。
C.
若序列中元素的个数为奇数个,则最后有一个子表的长度会小于其他子表,则修改子表上界并对其进行归并
评价:
A.
归并排序是一种稳定排序
B.
归并排序易于在链表上实现。对于长度为n的表,需要进行log2n次排序,每次归并的时间为O(n),其时间复杂度无论是在最好还是最坏的情况下都是O(nlog2n)
C.
归并排序中,每次都需要一个辅助向量来暂存两个有序子表来暂存结果,但在该次排序结束后便释放空间,所以总的辅助空间复杂度为O(n)。归并排序并不是就地排序
代码实现:
[cpp] view
plain
//一趟归并的算法实现
void Merge(int data[],int low,int mid,int high){
int* R1 = (int*)malloc((high - low + 1) * sizeof(int)); //分配一个辅助存储空间
int i = low,j = mid + 1,k = 0;
while(i <= mid && j <= high)
if(data[i] <= data[j]){
R1[k] = data[i];
k++;
i++;
}
else{
R1[k] = data[j];
k++;
j++;
}
//当两个子表的长度不同时,将未完的子表复制到R1中去
while(i <= mid){
R1[k] = data[i];
k++;
i++;
}
while(j <= high){
R1[k] = data[j];
k++;
j++;
}
//将整个R1数组放回到data中去,完成一次归并
for(k = 0,i = low;i <= high;k++,i++)
data[i] = R1[k];
free(R1);
}
void MergePass(int data[],int length,int n){
int i;
for(i = 0;i + 2 * length - 1 < n;i += 2 * length)
Merge(data,i,i + length - 1,i + 2 * length - 1);
//当待排序的数组元素个数为奇数个时,继续归并剩下的两个子表
if(i + length - 1 < n)
Merge(data,i,i + length - 1,n - 1);
}
void MergeSort(int data[],int n){
int length;
for(length = 1;length < n;length *= 2)
MergePass(data,length,n);
}
每一种排序方法都有自己的局限性,应当根据不同的环境选择最为合适的算法。
总结:
1.当待排序的序列个数n较少(n<=50),可以采用直接插入或直接选择排序。当记录规模较小时,直接插入排序较好。否则因为直接选择移动的记录数少于直接插入,则应选择直接选择排序。
2.若序列的状态基本有序,则应选用直接插入、冒泡或随机快速排序为宜
3.当n较大,则应采用时间复杂度为O(nlog2n)的排序方法,快速排序,归并排序。快速排序是目前基于比较的内排序方法中被认为是较好的方法。当待排序关键字是随机分布时,快速排序的平均时间最短。但这种排序是不稳定的,若需要稳定排序,则应采用归并排序。
4.若有两个有序表,要将其合并成一个新的有序表,最好的方法是归并排序
By:http://blog.csdn.net/phoenix500526/article/details/44904141
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)