排序:
排序基本上分为两种,
一种是内部排序法(Internal Sorting)
另一种是外部排序法(External Sorting)
内部排序:是将数据存储在内存中,然后进行排序。
外部排序:因为需要排序的数据太大,无法全部存储在内存时所运行的排序算法,在排序过程中使用外部存储设备。
以下主要对常见的内部排序算法进行总结
1)冒泡排序法
使用交换方式进行排序,
/*
功能:对字符数组进行排序
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
/*
功能:冒泡排序
*/
void bubble_sort(char* string, int count)
{
int i, j;
char temp;
for(j = count; j > 1; j--)
{
for(i = 0; i < j - 1; i++)
{
if(string[i + 1] < string[i])
{
temp = string[i + 1];
string[i + 1] = string[i];
string[i] = temp;
}
}
printf("第[%d]次排序:【%s】\n", count - j + 1, string);
}
}
/*
功能:冒泡排序的改进算法,即在检查到没有元素交换的时候停止运行。
*/
void bubble_sort_modified(char* string, int count)
{
int i, j, done;
char temp;
done = 0;
for(j = count; j > 1 && !done; j--)
{
done = 1;
for(i = 0; i < j - 1; i++)
{
if(string[i + 1] < string[i])
{
temp = string[i + 1];
string[i + 1] = string[i];
string[i] = temp;
done = 0;
}
}
if(done)
break;
printf("第[%d]次排序:【%s】\n", count - j + 1, string);
}
}
/*主函数*/
int main(int argc, char** argv)
{
char string[MAX];
int count;
printf("输入将要查找的字符串 ==> ");
gets(string);
count = strlen(string);
printf("\n输入的字符串为:【%s】\n\n", string);
// bubble_sort(string, count);
bubble_sort_modified(string, count);
printf("\n排序后的字符串:【%s】\n\n", string);
system("pause");
return 0;
}
从最坏的情况来分析冒泡排序算法的复杂度,如果输入的元素完全是相反的顺序的话,且问题的规模是n,那么一共需要n-1次的外层循环,内层循环根据外层循环递减,次数依次为(n-1), (n-2), (n-3), ... 2, 1。总和为n(n-1)/2次的比较和交换。
因此,冒泡排序算法的时间复杂度为O(n^2),空间复杂度为O(1)。
2)选择排序法
选择排序算法(Selection Sort):是从排序的元素中选出最小的一个元素,然后和第一个元素交换,然后从剩下的元素中选择出最小的元素和第二元素交换,重复这种操作。指到最后一个元素为止。
/*
功能:对字符数组进行排序
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
/*
功能:选择排序
*/
void select_sort(char* string, int count)
{
int pos;
int i, j;
char temp;
for(i = 0; i < count - 1; i++)
{
pos = i;
temp = string[pos];
for(j = i + 1; j < count; j++)
{
if(string[j] < temp)
{
pos = j;
temp = string[j];
}
}
string[pos] = string[i];
string[i] = temp;
printf("第[%d]次排序:【%s】\n", i + 1, string);
}
}
/*主函数*/
int main(int argc, char** argv)
{
char string[MAX];
int count;
printf("输入将要排序的字符串 ==> ");
gets(string);
printf("\n输入的字符串为:【%s】\n", string);
count = strlen(string);
select_sort(string, count);
printf("\n排好序后的字符串:【%s】\n", string);
system("pause");
return 0;
}
选择排序和冒泡排序算法一样,第一层循环需要运行n - 1次,n是全部需要排序的元素个数。第二层循环依次需要运行的次序:n-1, n-2,n-3,... ,2,1,0,总计为n(n-1)/2次,所以该算法的时间复杂度为:O(n^2)。空间复杂度为:O(1)。
3)插入排序法
插入排序法(insertion sort):就是先将待排序元素的前两个元素排序,然后将第三个元素插入已经排序号的两个元素中,这样前三个元素是排好序的。接着将第四个元素插入,重复操作指到所有的元素都排序好。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
/*
功能:插入排序
*/
void insert(char* string, int count)
{
int i, j;
char temp;
for(i = 1; i < count; i++)
{
temp = string[i];
j = i - 1;
for( ; j >= 0 && temp < string[j]; j--)
{
string[j + 1] = string[j];
}
string[j + 1] = temp;
printf("第[%d]次排序后:【%s】\n", i, string);
}
}
int main(int argc, char** argv)
{
char string[MAX];
int count;
printf("输入将要排序的字符串 ==> ");
gets(string);
printf("\n未排序的字符串:【%s】\n\n", string);
count = strlen(string);
insert(string, count);
printf("\n排序后的输出结果:【%s】\n\n", string);
system("pause");
return 0;
}
插入排序算法第一层循环需要插入n-1个元素,第二层循环将运行1,2,3,...,n-2,n-1次,合计为n(n-1)/2,所以其算法复杂度为O(n^2)。空间复杂度为O(1)。
4)希尔排序法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
/*
功能:shell排序
*/
void shell_sort(char* string, int count)
{
int pos; //处理位置
int offset; //位移量
int i, j;
char temp;
i = 0;
offset = count / 2; //创建位移量
while(offset != 0)
{
i++;
for(j = offset; j < count; j++)
{
temp = string[j];
pos = j - offset;
while(temp < string[pos] && pos >= 0 && j < count)
{
string[pos + offset] = string[pos];
pos = pos - offset;
}
string[pos + offset] = temp;
}
printf("第[%d]次排序后:【%s】\n", i, string);
offset = offset / 2;
}
}
/*
功能:shell排序的改进版,即用户可以自己设定处理增量
*/
void shell_sort_modified(char* string, int count)
{
int offset_by_user[6] = {11, 7, 5, 3, 2, 1}; //设置处理增量,用户可根据需要自行设置。
int pos; //处理位置
int offset; //位移量
int i, j;
char temp;
i = 0;
offset = count / 2; //创建位移量
for(i = 0; i < 6; i++)
{
offset = offset_by_user[i];
for(j = offset; j < count; j++)
{
temp = string[j];
pos = j - offset;
while(temp < string[pos] && pos >= 0 && j < count)
{
string[pos + offset] = string[pos];
pos = pos - offset;
}
string[pos + offset] = temp;
}
printf("第[%d]次排序后:【%s】\n", i + 1, string);
}
}
/*
主函数
*/
int main(int argc, char** argv)
{
char string[MAX];
int count;
printf("输入将要排序的字符串 ==> ");
gets(string);
printf("\n未排序的字符串:【%s】\n\n", string);
count = strlen(string);
//shell_sort(string, count);
shell_sort_modified(string, count);
printf("\n输出排序后的字符串:【%s】\n\n", string);
system("pause");
return 0;
}
希尔排序算法的时间复杂度为:O(n(logn)^2), n为要需要的元素个数。
5)快速排序法
快速排序法(Quick Sort):被公认为最佳的排序法,快速排序法和冒泡排序法一样都是使用交换方式进行排序,但是通过分割的技巧,快速排序法名的运行效率远远超过了冒泡排序法。
快速排序法和希尔排序法一样是将数据分割,其分割方法是选择一个元素为分割标准,将数据分为两半,其中欧一般是比标准值大的元素,另一半是比标准小或相等的元素。接着对每一半元素使用相同方法分割直到分割的部分无法分割为止,这时所有的元素就已经完成排序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
/*
功能:快速排序
*/
int partition(char* string, int low, int high)
{
char key = string[low];
while(low < high)
{
while(low < high && string[high] >= key)
{
high--;
}
string[low] = string[high];
while(low < high && string[low] <= key)
{
low++;
}
string[high] = string[low];
}
string[low] = key;
return low;
}
void _quick_sort(char* string, int low, int high)
{
int position = partition(string, low, high);
if(position - 1 > low)
{
_quick_sort(string, low, position - 1);
}
if(position + 1 < high)
{
_quick_sort(string, position + 1, high);
}
}
void quick_sort(char* string, int count)
{
_quick_sort(string, 0, count - 1);
}
//主函数
int main(int argc, char** argv)
{
char string[MAX];
int count;
printf("输入将要排序的字符串 ==>");
gets(string);
printf("\n未排序前的字符串为:【%s】\n", string);
count = strlen(string);
quick_sort(string, count);
printf("\n排序后的字符串为:【%s】\n", string);
system("pause");
return 0;
}
快速排序算法的时间复杂度为:O(nlog(n))。
6)二叉查找树排序法
二叉排序树(Binary Sort Tree, 又记BST),是具有如下性质的二叉树:
a)若它的左子树非空,则左子树上所有结点的值均小于它的根结点的值。
b)若它的右子树非空,则右子树上所有结点的值均大于它的根结点的值。
c)它的左、右子树本身又各是一颗二叉排序树。
/*
功能:二叉查找树实现排序
*/
#include <stdio.h>
#include <stdlib.h>
/*-------------------------------------*/
/*功能:创建二叉查找树 */
/*------------------------------------ */
void create_btree(int *btree, int *data, int length)
{
int level; //树的层次
int i;
btree[1] = data[1]; //创建二叉查找树的根节点
for(i = 2; i <= length; i++)
{
level = 1;
while(btree[level] != 0)
{
if(data[i] > btree[level])
level = level * 2 + 1;
else
level = level * 2;
}
btree[level] = data[i];
}
}
/*-------------------------------*/
/*功能:二叉树中序遍历 */
/*------------------------------*/
void inorder(int* btree, int pos)
{
if(btree[pos] != 0 && pos < 16)
{
inorder(btree, pos * 2 + 1);
printf("[%d]", btree[pos]);
inorder(btree, pos * 2);
}
}
int main()
{
int btree[16];
int data[10] = {0, 5, 6, 4, 8, 2, 3, 7, 1, 9};
int i;
for(i = 1; i < 16; i++)
btree[i] = 0;
create_btree(btree, data, 9);
printf("二叉查找树的内容为:");
for(i = 1; i < 16; i++)
printf("[%d]", btree[i]);
printf("\n输出的排序结果:");
inorder(btree, 1);
printf("\n");
system("pause");
return 0;
}
该算法的时间复杂度为:O(nlogn)。
7)堆排序法
堆排序必须解决两个问题:
(1) 如何将原始记录序列构造成一个堆,即建立初始堆。
(2) 输出堆顶元素后,如何将剩余记录调整成一个新堆。
因此,堆排序的关键是构造初始堆,其实构造初始堆的过程就是将待排元素序列对应的完全二叉树调整形成一个堆,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10
/*
功能:初始化堆
*/
void init_heap(int r[], int l, int m)
{
int i, j;
int x;
i = l; j = 2 * i;
x = r[i];
while(j <= m)
{
if(j < m && r[j] < r[j + 1])
j++;
if(x < r[j])
{
r[i] = r[j];
i = j;
j = 2 * i;
}
else
j = m + 1;
}
r[i] = x;
}
/*
功能:堆排序算法
*/
void heap_sort(int r[], int n)
{
int i, k;
int x;
for(i = n / 2; i > 0; i--)
init_heap(r, i, n);
for(i = n; i > 1; i--)
{
x = r[1];
r[1] = r[i];
r[i] = x;
init_heap(r, 1, i - 1);
}
}
void print_num(int r[])
{
int i;
for(i = 1; i < MAX + 1; i++)
{
printf("%d\n", r[i]);
}
}
/*主函数*/
int main(int argc, char** argv)
{
int num[MAX + 1] = {0, 36, 24, 48, 12, 65, 25, 43, 59, 76, 34};
printf("未排序的数据为:");
print_num(num);
printf("\n");
heap_sort(num, MAX);
printf("排序后的数据为:");
print_num(num);
system("pause");
return 0;
}
从上面介绍的堆排序算法可知,堆排序所需的比较次数是建立初始堆与重新建堆所需的比较次数之和,其平均时间复杂度和最坏的时间复杂度均为O(n logn),n为需要排序的元素个数。
它是一种不稳定的排序方法。
==============================================================================================================================
各排序算法的对比:
排序方法 | 平均时间 | 最坏情况 | 辅助时间 | 稳定性 |
冒泡排序 | O(n^2) | O(n^2) | O(1) | Yes |
直接插入排序 | O(n^2) | O(n^2) | O(1) | Yes |
折半插入排序 | O(n^2) | O(n^2) | O(1) | Yes |
希尔排序 | O(n^1.3) | O(n^2) | O(1) | No |
快速排序 | O(nlogn) | O(n^2) | O(logn) | No |
选择排序 | O(n^2) | O(n^2) | O(1) | Yes |
堆排序 | O(nlogn) | O(nlogn) | O(1) | No |