目录
排序的定义
排序是将一个集合按照元素关键字的大小重新排列成有序序列的操作。主要目的是便于查找和筛选。
插入排序
通过将未排序部分的元素逐个按其关键字大小插入已排序部分的恰当位置,最终实现全部元素有序排列。
算法思想
首先,将原始一个无序表看成两个子表,前一个子表为有序表,后一个子表为无序表,开始将第一个元素看为有序子表,其余元素为无序子表;
每趟排序按照关键字的大小将无序表的一个元素插入到有序表的恰当位置;
直至整个数据表有序。
void InsertSort(SqList *L) //升序
{
int i,j;
for(i=2;i<=L->len;i++) //从第二个元素开始一一插入到前面
{
L->list[0]=L->list[i]; //监视哨
j=i-1;
while(L->list[0].key<L->list[j].key) //从有序表后往前查找插入位置
{
L->list[j+1]=L->list[j];
j--; //继续向前查找
}
L->list[j+1]=L->list[0]; //插入
}
}
示例
算法分析:
稳定性:稳定的。
空间复杂度:O(1)。
时间复杂度:最坏的情况为O(n*n),最好的情况为O(n)。
L->list[0]有两个作用:一是保存待插入元素L->list[i]的值,一面在后移过程中被覆盖丢失;二是作为“监视哨”。
折半插入排序
折半插入排序又称为二分插入排序,是对直接插入排序的一种改进方法。
算法思想
和直接插入排序思想类似。
void BinarySort(SqList* L) //升序
{
cout << "调用折半插入排序" << endl;
int i, j, low, high, mid;
for (i = 2; i <= L->len; i++)
{
L->list[0] = L->list[i];
low = 0;
high = i - 1;
while (low <= high)
{
mid = (low + high) / 2;
if (L->list[0].key < L->list[mid].key)
high = mid - 1;
else
low = mid + 1;
} //折半查找结束,位置为low
for (j = i - 1; j >= low; j--) //元素一一后移
L->list[j + 1] = L->list[j];
L->list[low] = L->list[0];
}
算法分析:
稳定性:稳定的。
空间复杂度:O(1)。
时间复杂度:O(n*n)。
注意:由于在有序表中查找元素插入位置时采用二分查找实现,折半插入排序只能应用于顺序存储结构。
希尔排序
设计思路:
直接插入排序在元素个数较少且基本有序时,效率很高。所以如果能实现1>减少待排序数据表元素个数,2>排序的数据表基本有序,则可以排序效率提高。
算法思想:
先取一个正整数d1(d1<n)作为第一个增量,将全部n个记录分成d1个组,把所有距离为d1倍数的记录放在同一组中,在各组内进行直接插入排序。这样一次分组和排序过程称为一趟希尔排序;
取新的增量d2<d1,重复1) 的分组和排序操作:直至所取的增量di=1为止,即所有记录放进一个组中排序为止。
示例:
void ShellSort(SqList* L, int d[]) //d数组存放每趟的增量
{
cout << "调用希尔排序" << endl;
int i, j, k, h;
k = 0;
do
{
h = d[k]; //取增量
for (int i = h + 1; i <= L->len; i++) //i为待查热元素的下标
{
L->list[0] = L->list[i];
j = i - h; //查看待插入元素的前一个元素
while (L->list[0].key < L->list[j].key && j>0)
{
L->list[j + h] = L->list[j];
j = j - h;
}
L->list[j + h] = L->list[0];
}
k++;
} while (h != 1); //当增量为1时结束
}
算法分析:
稳定性:不稳定。
空间复杂度:依赖于步长银子序列的选取。介于O(nlog2(n))和O(n*n)之间。
冒泡排序:
算法思想:
void BubbleSort(SqList* L)
{
cout << "调用冒泡排序" << endl;
int i, j, k, n = L->len;
int t;
for (i = 1; i < n; i++)
for (j = n; j > i; j--)
if (L->list[j].key < L->list[j - 1].key)
{
t = L->list[j].key;
L->list[j].key = L->list[j - 1].key;
L->list[j - 1].key = t;
}
}
示例
从图中可以看出,第四趟已经全部有序了,但是算法并没有提前结束。
解决方案:
在冒泡排序算法中加入一个flag标志变量控制在数据表已经有序的情况下提前结束排序。
flag=1,表名数据表无序,下一趟排序仍需进行。
flag=0,表明数据表有序,排序过程应结束。
void BubbleSort_1(SqList* L)
{
cout << "调用冒泡函数优化版" << endl;
int i, j, k, n = L->len, flag;
DataType t;
i = 1;
flag = 1;
while ((i < n) && (flag == 1))
{
for (j = n; j > i; j--)
if (L->list[j].key < L->list[j - 1].key)
{
flag = 1;
t = L->list[j];
L->list[j] = L->list[j - 1];
L->list[j - 1] = t;
}
i++;
}
}
算法分析
时间复杂度:O(n*n)。
空间复杂度:O(1)。
稳定性:稳定的
快速排序
算法思想:
int QuickPass(SqList* L, int low, int high)
{
int i, j;
i = low; //i从最左端开始
j = high; //j从最右端开始
L->list[0] = L->list[i]; //L->list[0]存放基准元素
while (i != j) //重复左右交替扫描,直至i和j重合
{
while ((L->list[j].key >= L->list[0].key) && (i < j)) //从右向左扫描
j--;
if (i < j) //若发现小于基准关键字的元素,将其放在左子表中并改变扫描方向
{
L->list[i] = L->list[j];
i++;
}
while ((L->list[i].key <= L->list[0].key) && (i < j)) //从左向右扫描
i++;
if (i < j) //若发现大于基准关键字的元素,将其放在右子表中并改变扫描方向
{
L->list[j] = L->list[i];
j--;
}
}
L->list[i] = L->list[0];
return i; //返回划分点位置
}
void QuickSort(SqList* L, int s, int t)
{
cout << "调用快速排序" << endl;
int i;
if (s < t) //只要排序区间中的元素超过1个,仍然继续进行快排
{
i = QuickPass(L, s, t); //对范围进行一次划分
QuickSort(L, s, i - 1); //递归调用,分别对划分得到的两个子表进行快速排序
QuickSort(L, i + 1, t);
}
}
算法分析
空间复杂度:最好O(log2(n)),最坏O(n)。
时间复杂度:最好O(nlog2(n)),最坏O(n*n)。平均情况:O(nlog2(n))。
稳定性:不稳定。
选择排序
基本选择排序
算法思想:
void SelectSort(SqList* L)
{
cout << "调用选择排序" << endl;
int i, j, k, n;
DataType temp;
n = L->len;
for (i = 1; i <= n - 1; i++)
{
k = i; //记录当前最小元素的下标
for (j = i + 1; j <= n; j++) //寻找待排序范围的最小元素
if (L->list[j].key < L->list[k].key)
k = j;
if (k != j) //将待排序范围内找到的最小元素与表前端对应位置上的元素进行交换
{
temp = L->list[i];
L->list[i] = L->list[k];
L->list[k] = temp;
}
}
}
示例
时间复杂度:O(1)。
空间复杂度:O(n*n)。
稳定性:不稳定。
其性能与初始状态无关。
堆排序
堆排序局势二叉树形选择排序法。堆分为大根堆和小根堆。
若有n个元素的排序码k1,k2,...,kn,满足如下条件:
大根堆和小根堆
调整大根堆的原理和过程如下示例
堆调整函数(用于创建初始堆及重新调整堆)
void HeapAdjust(SeqList *L,int low,int high)
{
int i,j;
i=low;
j=2*i;
L->list[0]=L->list[i];
for(;j<=high;j*=2) //自顶向下对节点进行调整
{
if(j<high&&L->list[j].key<L->list[j+1].key) //找出i号结点左右孩子较大的一个
j++;
if(L->list[0].key>=L->list[j].key) //j号结点与其双亲进行比较
break;
else
{
L->list[i]=L->list[j];
i=j; //j这个位置成为新的待定的,调整继续向下层结点进行
}
}
L->list[i]=L->list[0]; //把最初被调整的结点放在合适的位置
}
堆创建函数(堆创建的过程的过程就是,就是反复调用HeapAdjust函数,从后从前依次将以每个非叶子结点为根节点的子树调整为大顶堆的过程)
void HeapCreate(SeqList *L)
{
int i,n;
n=L->len;
for(i=n/2;i>0;i--)
{
HeapAdjust(L,i,n);
}
}
堆排序
void HeapSort(SqList* L)
{
cout << "调用堆排序" << endl;
int i, n = L->len;
HeapCreate(L); //创建最大堆
for (i = n; i > 1; i--)
{
L->list[0] = L->list[1];
L->list[1] = L->list[i];
L->list[i] = L->list[0];
HeapAdjust(L, 1, i - 1); //将表前面i-1个元素重新调整为堆
}
}
算法分析:
空间复杂度:O(1)。
时间复杂度:O(nlog2(n))。
稳定性:不稳定。
归并排序
算法思想:
首先将待排序的n个元素看作n个长度为1的有序子表,然后从第一个子表开始,对相邻的子表进行两两合并;
接着在对合并后的有序子表继续进行两两合并,重复以上过程,直至得到一长度为n的有序表。
void Merge(SeqList *sr,int s,int m,int t,SeqList *tr)
{
int i,j,k;
i=s; //i保存sr前面子表当前元素位置
j=m+1; //j保存后面子表中当前元素位置
k=s; ///k保存归并后tr对应的顺序表中的当前元素位置
while((i<=m)&&(j<=t) //若前后两个子表中的元素均未全部合并
{
if(sr->list[i].key<=sr->list[j].key) //两个子表中关键字较小元素存入tr中
tr->list[k]=sr->list[i++];
else
tr->list[k]=sr->list[j++];
k++;
}
while(i<=m) //将sr所对应的前面子表中的剩余元素放入tr对应的顺序表中
tr->list[k++]=sr->list[i++];
while(j<=t) //将sr所对应的后面子表中的剩余元素放入tr对应的顺序表中
tr->list[k++]=sr->list[j++];
}
void MergeSort(SqList* sr, SqList* tr, int s, int t)
{ //sr,tr分别指向排序前后的顺序表;s和t分别用于存放sr所指向的待排序顺序表区间的左右端点
cout << "调用堆排序" << endl;
int m;
SqList temp; //temp为暂存的辅助顺序表
if (s == t) //若到排序区间仅有一个元素
tr->list[s] = sr->list[s];
else
{
m = (s + t) / 2; //将排序区间一分为二,m保存中间位置
MergeSort(sr, &temp, s, m); //递归调用对前面的子表进行归并排序
MergeSort(sr, &temp, m + 1, t); //递归调用堆后面的子表进行归并排序
Merge(&temp, s, m, t, tr); //堆前后两个表进行合并
}
tr->len = sr->len;
}
算法分析:
时间复杂度:O(nlog2(n))。
空间复杂度:O(n)。
稳定性:稳定的。
最后给出测试代码
#include<iostream>
using namespace std;
#define MAXLEN 1000
typedef struct
{
int key;
}DataType;
typedef struct
{
int len;
DataType list[MAXLEN+1];
} SqList;
void InsertSort(SqList* L) //升序
{
cout << "调用直接插入排序" << endl;
int i, j;
for (i = 2; i <= L->len; i++) //从第二个元素开始一一插入到前面
{
L->list[0] = L->list[i]; //监视哨
j = i - 1;
while (L->list[0].key < L->list[j].key) //从有序表后往前查找插入位置
{
L->list[j + 1] = L->list[j];
j--; //继续向前查找
}
L->list[j + 1] = L->list[0]; //插入
}
}
void InitSqList(SqList* L)
{
//cout << "请输入元素个数" << endl;
//int x;
//cin >> x;
//L->len = x;
//while (x != 0)
// cin >> L->list[x--].key;
L->len = 8;
L->list[1].key = 15;
L->list[2].key = 47;
L->list[3].key = 26;
L->list[4].key = 58;
L->list[5].key = 25;
L->list[6].key = 29;
L->list[7].key = 50;
L->list[8].key = 99;
}
void Print(SqList& L)
{
int i = 1;
while (i <= L.len)
{
cout << L.list[i++].key << " ";
}
cout << endl;
}
void BinarySort(SqList* L) //升序
{
cout << "调用折半插入排序" << endl;
int i, j, low, high, mid;
for (i = 2; i <= L->len; i++)
{
L->list[0] = L->list[i];
low = 0;
high = i - 1;
while (low <= high)
{
mid = (low + high) / 2;
if (L->list[0].key < L->list[mid].key)
high = mid - 1;
else
low = mid + 1;
} //折半查找结束,位置为low
for (j = i - 1; j >= low; j--) //元素一一后移
L->list[j + 1] = L->list[j];
L->list[low] = L->list[0];
}
}
void ShellSort(SqList* L, int d[]) //d数组存放每趟的增量
{
cout << "调用希尔排序" << endl;
int i, j, k, h;
k = 0;
do
{
h = d[k]; //取增量
for (int i = h + 1; i <= L->len; i++) //i为待查热元素的下标
{
L->list[0] = L->list[i];
j = i - h; //查看待插入元素的前一个元素
while (L->list[0].key < L->list[j].key && j>0)
{
L->list[j + h] = L->list[j];
j = j - h;
}
L->list[j + h] = L->list[0];
}
k++;
} while (h != 1); //当增量为1时结束
}
void BubbleSort(SqList* L)
{
cout << "调用冒泡排序" << endl;
int i, j, k, n = L->len;
DataType t;
for (i = 1; i < n; i++)
for (j = n; j > i; j--)
if (L->list[j].key < L->list[j - 1].key)
{
t = L->list[j];
L->list[j] = L->list[j - 1];
L->list[j - 1] = t;
}
}
void BubbleSort_1(SqList* L)
{
cout << "调用冒泡函数优化版" << endl;
int i, j, k, n = L->len, flag;
DataType t;
i = 1;
flag = 1;
while ((i < n) && (flag == 1))
{
for (j = n; j > i; j--)
if (L->list[j].key < L->list[j - 1].key)
{
flag = 1;
t = L->list[j];
L->list[j] = L->list[j - 1];
L->list[j - 1] = t;
}
i++;
}
}
int QuickPass(SqList* L, int low, int high)
{
int i, j;
i = low; //i从最左端开始
j = high; //j从最右端开始
L->list[0] = L->list[i]; //L->list[0]存放基准元素
while (i != j) //重复左右交替扫描,直至i和j重合
{
while ((L->list[j].key >= L->list[0].key) && (i < j)) //从右向左扫描
j--;
if (i < j) //若发现小于基准关键字的元素,将其放在左子表中并改变扫描方向
{
L->list[i] = L->list[j];
i++;
}
while ((L->list[i].key <= L->list[0].key) && (i < j)) //从左向右扫描
i++;
if (i < j) //若发现大于基准关键字的元素,将其放在右子表中并改变扫描方向
{
L->list[j] = L->list[i];
j--;
}
}
L->list[i] = L->list[0];
return i; //返回划分点位置
}
void QuickSort(SqList* L, int s, int t)
{
cout << "调用快速排序" << endl;
int i;
if (s < t) //只要排序区间中的元素超过1个,仍然继续进行快排
{
i = QuickPass(L, s, t); //对范围进行一次划分
QuickSort(L, s, i - 1); //递归调用,分别对划分得到的两个子表进行快速排序
QuickSort(L, i + 1, t);
}
}
void SelectSort(SqList* L)
{
cout << "调用选择排序" << endl;
int i, j, k, n;
DataType temp;
n = L->len;
for (i = 1; i <= n - 1; i++)
{
k = i; //记录当前最小元素的下标
for (j = i + 1; j <= n; j++) //寻找待排序范围的最小元素
if (L->list[j].key < L->list[k].key)
k = j;
if (k != j) //将待排序范围内找到的最小元素与表前端对应位置上的元素进行交换
{
temp = L->list[i];
L->list[i] = L->list[k];
L->list[k] = temp;
}
}
}
void HeapAdjust(SqList* L, int low, int high)
{
int i, j;
i = low;
j = 2 * i;
L->list[0] = L->list[i];
for (; j <= high; j *= 2) //自顶向下对节点进行调整
{
if (j < high && L->list[j].key < L->list[j + 1].key) //找出i号结点左右孩子较大的一个
j++;
if (L->list[0].key >= L->list[j].key) //j号结点与其双亲进行比较
break;
else
{
L->list[i] = L->list[j];
i = j; //j这个位置成为新的待定的,调整继续向下层结点进行
}
}
L->list[i] = L->list[0]; //把最初被调整的结点放在合适的位置
}
void HeapCreate(SqList* L)
{
int i, n;
n = L->len;
for (i = n / 2; i > 0; i--)
{
HeapAdjust(L, i, n);
}
}
void HeapSort(SqList* L)
{
cout << "调用堆排序" << endl;
int i, n = L->len;
HeapCreate(L); //创建最大堆
for (i = n; i > 1; i--)
{
L->list[0] = L->list[1];
L->list[1] = L->list[i];
L->list[i] = L->list[0];
HeapAdjust(L, 1, i - 1); //将表前面i-1个元素重新调整为堆
}
}
void Merge(SqList* sr, int s, int m, int t, SqList* tr)
{
int i, j, k;
i = s; //i保存sr前面子表当前元素位置
j = m + 1; //j保存后面子表中当前元素位置
k = s; ///k保存归并后tr对应的顺序表中的当前元素位置
while ((i <= m) && (j <= t)) //若前后两个子表中的元素均未全部合并
{
if (sr->list[i].key <= sr->list[j].key) //两个子表中关键字较小元素存入tr中
tr->list[k] = sr->list[i++];
else
tr->list[k] = sr->list[j++];
k++;
}
while (i <= m) //将sr所对应的前面子表中的剩余元素放入tr对应的顺序表中
tr->list[k++] = sr->list[i++];
while (j <= t) //将sr所对应的后面子表中的剩余元素放入tr对应的顺序表中
tr->list[k++] = sr->list[j++];
}
void MergeSort(SqList* sr, SqList* tr, int s, int t)
{ //sr,tr分别指向排序前后的顺序表;s和t分别用于存放sr所指向的待排序顺序表区间的左右端点
cout << "调用堆排序" << endl;
int m;
SqList temp; //temp为暂存的辅助顺序表
if (s == t) //若到排序区间仅有一个元素
tr->list[s] = sr->list[s];
else
{
m = (s + t) / 2; //将排序区间一分为二,m保存中间位置
MergeSort(sr, &temp, s, m); //递归调用对前面的子表进行归并排序
MergeSort(sr, &temp, m + 1, t); //递归调用堆后面的子表进行归并排序
Merge(&temp, s, m, t, tr); //堆前后两个表进行合并
}
tr->len = sr->len;
}
int main()
{
SqList L;
InitSqList(&L);
// ===插入排序
//InsertSort(&L);
// ===折半插入排序
//BinarySort(&L);
// ===希尔排序
//int d[3] = { 5,3,1 };
//ShellSort(&L, d);
// ===冒泡排序
//BubbleSort(&L);
// ===冒泡排序优化
//BubbleSort_1(&L);
// ===快速排序
//QuickSort(&L, 1, L.len);
// ===选择排序
//SelectSort(&L);
// ===堆排序
//HeapSort(&L);
//SqList LL;
//MergeSort(&L, &LL, 0, L.len);
//Print(LL);
//Print(L);
return 0;
}