目录
举例
一、基本概念
- 排序(Sorting):排序就是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。
- 数据表(Data List) : 待排序的数据对象的有限集合。
- 关键字(Key):数据元素(或记录)中某个数据项的值,用它可以标识(识别)一个数据元素(或记录)。
- 主关键字(Primary Key) :若此关键字可以唯一地标识一个记录,则称此关键字为主关键字。
- 次关键字(Secondary Key) :用以识别若干记录的关键字为次关键字。
- 注意:当数据元素只有一个数据项时,其关键字即为该数据项
二、排序的分类
- 增排序和减排序: 如果排序的结果是按关键字从小到大的次序排列的,就是增排序,否则就是减排序。
- 稳定排序和不稳定排序:如果待排序的表中存在多个关键字相同的元素,采用某种排序方法排序后这些具有相同关键字的元素之间的相对次序始终保持不变,则称这种排序方法是稳定的;反之,若具有相同关键字的元素之间的相对次序发生变化,则称这种排序方法是不稳定的。
- 内部排序与外部排序:在排序中,若数据表中的所有记录的排列过程都是在内存中进行的,称为内部排序。由于待排序的记录数量太多,在排序过程中不能同时把全部记录放在内存,需要不断地通过在内存和外存之间交换数据元素来完成整个排序的过程,称为外部排序。
三、排序算法的好坏的衡量
- 时间效率 —— 排序速度(即排序所花费的全部比较次数和移动次数,一般都按平均时间复杂度进行估算;对于那些受数据表中记录的初始排列及记录数目影响较大的算法,按最好情况和最坏情况分别进行估算。 )
- 空间效率 —— 占内存辅助空间的大小(若排序算法所需的辅助空间不依赖问题的规模n,即空间复杂度是O(1) ,则称排序方法是就地排序,否则是非就地排序。)
- 稳 定 性 —— 若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
四、五类内排序
- 插入排序(直接插入排序、希尔排序)
- 交换排序(冒泡排序、快速排序)
- 选择排序(简单选择排序、堆排序)
- 归并排序
- 基数排序
1、插入排序
1. 直接插入排序
- 基本思想:当插入第i个对象时,前面的R[1], R[2], …, R[i - 1]已经排好序,此时,用R[i]的关键字与R[i - 1], R[i - 2], …的关键字顺序进行比较,找到插入位置即将R[i]插入,原来位置上的对象向后顺移。
-
演示:
-
关键代码:
template <typename T>
void Swap(T& a, T& b)//交换
{
T tmp;
tmp = a;
a = b;
b = tmp;
}
template <typename T>
void Inser(T data[])
{
for (int i = 1; i < size; i++)//i为扫描次数
{
int tmp = data[i];
for (int j = i-1; j >=0; j--)//用j定位比较的元素
{
if (tmp < data[j])
{
Swap(data[j+1], data[j]);
}
}
cout << "第" << i << "次扫描:";
print(data);
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
#define size 5
template <typename T>
void print(T data[])
{
for (int i = 0; i < size; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
template <typename T>
void Swap(T& a, T& b)//交换
{
T tmp;
tmp = a;
a = b;
b = tmp;
}
template <typename T>
void Inser(T data[])
{
for (int i = 1; i < size; i++)//i为扫描次数
{
int tmp = data[i];
for (int j = i - 1; j >= 0; j--)//用j定位比较的元素
{
if (tmp < data[j])
{
Swap(data[j + 1], data[j]);
}
}
cout << "第" << i << "次扫描:";
print(data);
}
}
int main()
{
int data[] = { 55,23,87,62,16 };
cout << "排序前的顺序:" << endl;
print(data);
Inser(data);
cout << "排序后的顺序:" << endl;
print(data);
return 0;
}
-
结果:
-
插入排序算法分析:
- 1.最坏情况和平均情况需比较(n-1)+(n-2)+....+2+1=n(n-1)/2次,时间复杂度为
- 最好情况是扫描n次,移动0次,时间复杂度为
- 2.插入排序是稳定排序法
- 3.因为只需要一个额外的空间,所有空间复杂度为最佳
2. 带哨兵的插入排序
-
举例:
-
关键代码
template <typename T>
void Insert2(T data[])
{
for (int i = 2; i < size; i++)
{
data[0] = data[i];
for (int j = i - 1; data[0] < data[j]; j--)
{
Swap(data[j + 1], data[j]);
}
cout << "第" << i-1 << "次扫描:";
print(data);
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
#define size 6
template <typename T>
void print(T data[])
{
for (int i = 1; i < size; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
template <typename T>
void Swap(T& a, T& b)//交换
{
T tmp;
tmp = a;
a = b;
b = tmp;
}
template <typename T>
void Insert2(T data[])
{
for (int i = 2; i < size; i++)
{
data[0] = data[i];
for (int j = i - 1; data[0] < data[j]; j--)
{
Swap(data[j + 1], data[j]);
}
cout << "第" << i-1 << "次扫描:";
print(data);
}
}
int main()
{
int data[size];
cout << "请依次输入5个数据:";
for (int i = 1; i < size; i++)
{
cin >> data[i];
}
cout << "排序前的顺序:" << endl;
print(data);
Insert2(data);
cout << "排序后的顺序:" << endl;
print(data);
return 0;
}
- 算法中引入附加记录data[0]有两个作用:
- 其一是进入查找循环之前,它保存了data[i]的副本,使得不至于因记录的后移而丢失data[i]中的内容;
- 其二是在for循环“监视”下标变量j是否越界,一旦越界(即j < 1),data[0]自动控制for循环的结束,从而避免了在for循环内的每一次都要检测j是否越界(即省略了循环条件j >= 1)。
- data[0]称为“监视哨”
3. 折半插入排序
- 基本思想:在已形成的有序表中折半查找,并在适当位置插入,把原来位置上的元素向后顺移
-
演示
-
关键代码
template <typename T>
void BinSort(T data[])
{
for (int i = 1; i < size; i++)//扫描次数(需要排序的元素个数)
{
T tmp = data[i];//先将data[i]保存在tmp中
int low = 0, high = i - 1;
int mid = 0;
while (low <= high)//寻找插入位置
{
mid = (low + high) / 2;
if (tmp <= data[mid])
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}//循环结束,high+1为插入位置
for (int j = i - 1; j >= high + 1; j--)//移动元素
{
data[j + 1] = data[j];
}
data[high + 1] = tmp;
cout << "第" << i << "次扫描:" ;
print(data);
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
#define size 11
template <typename T>
void print(T data[])
{
for (int i = 0; i < size; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
template <typename T>
void BinSort(T data[])
{
for (int i = 1; i < size; i++)//扫描次数(需要排序的元素个数)
{
T tmp = data[i];//先将data[i]保存在tmp中
int low = 0, high = i - 1;
int mid = 0;
while (low <= high)//寻找插入位置
{
mid = (low + high) / 2;
if (tmp <= data[mid])
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}//循环结束,high+1为插入位置
for (int j = i - 1; j >= high + 1; j--)//移动元素
{
data[j + 1] = data[j];
}
data[high + 1] = tmp;
cout << "第" << i << "次扫描:" ;
print(data);
}
}
int main()
{
//int data[] = { 3,5,7,10,16,23,29,32,54,83,96 };
int data[] = { 96,83,54,32,29,23,16,10,7,5,3 };
cout << "排序前的顺序:" << endl;
print(data);
BinSort(data);
cout << "排序后的顺序:" << endl;
print(data);
return 0;
}
-
结果
4. 折半插入排序(监视哨)
-
关键代码
template <typename T>
void BinSort(T data[])
{
for (int i = 2; i < size; i++)
{
data[0] = data[i];//下标为0的位置放即将插入的元素(哨兵)
int low = 0, high = i - 1;
while (low <= high)
{
int mid = (low + high) / 2;//折半
if (data[0]<=data[mid])
{
high = mid - 1;//插入点在低半区
}
else
{
low = mid + 1;
}
}
for (int j = i - 1; j >= low; j--)
{
data[j + 1] = data[j];
}
data[low] = data[0];
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
#define size 12
template <typename T>
void print(T data[])
{
for (int i = 1; i < size; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
template <typename T>
void BinSort(T data[])
{
for (int i = 2; i < size; i++)
{
data[0] = data[i];//下标为0的位置放即将插入的元素(哨兵)
int low = 0, high = i - 1;
while (low <= high)
{
int mid = (low + high) / 2;//折半
if (data[0]<=data[mid])
{
high = mid - 1;//插入点在低半区
}
else
{
low = mid + 1;
}
}
for (int j = i - 1; j >= low; j--)
{
data[j + 1] = data[j];
}
data[low] = data[0];
}
}
int main()
{
int data[size] ;
cout << "请依次输入"<<size-1<<"个数据元素:"<<endl;
//测试数据:96 83 54 32 29 23 16 10 7 5 3
for (int i = 1; i < size; i++)
{
cin >> data[i];
}
cout << "排序前的顺序:" << endl;
print(data);
BinSort(data);
cout << "排序后的顺序:" << endl;
print(data);
return 0;
}
折半插入排序的算法分析
- 折半查找比顺序查找快,所以折半插入排序就平均性能来说比直接插入排序要快。
- 在插入第 个对象时,需要经过次关键码比较,才能确定它应插入的位置。
- 折半插入排序是一个稳定的排序方法。
5. 希尔排序 (Shell Sort)
- 基本思想:先将整个待排序记录序列分割成为若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序
-
演示
-
举例
-
关键代码
void ShellSort(int *data, int n, int delta)//一趟希尔排序
{
for (int i = delta; i <n; i++)//用i表示扫描次数
{
int tmp = data[i];
for (int j = i-delta; j < n&&tmp<data[j]; j = j - delta)//用j定位需要比较的元素
{
swap(data[j], data[j + 1]);
}
}
}
void ShellSert(int* data, int n, int delta)
{
int k = 1;//打印计数
while (delta != 0)
{
ShellSort(data, n, delta);
cout << "第" << k++ << "次排序:";
print(data, n);
delta = delta / 2;
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
int temp = a;
a = b;
b = temp;
}
void print(int* data, int n)//打印
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
void ShellSort(int *data, int n, int delta)//一趟希尔排序
{
for (int i = delta; i <n; i++)//用i表示扫描次数
{
int tmp = data[i];
for (int j = i-delta; j < n&&tmp<data[j]; j = j - delta)//用j定位需要比较的元素
{
swap(data[j], data[j + 1]);
}
}
}
void ShellSert(int* data, int n, int delta)
{
int k = 1;//打印计数
while (delta != 0)
{
ShellSort(data, n, delta);
cout << "第" << k++ << "次排序:";
print(data, n);
delta = delta / 2;
}
}
int main()
{
int a[] = { 5,65,1,45,32,67,89,12 };
cout << "排序前:";
print(a, 8);
ShellSert(a, 8, 4);
cout << "排序后:";
print(a, 8);
return 0;
}
-
结果
希尔排序算法分析
- 希尔排序是一种不稳定的排序方法(原因:关键字相同的两个对象,在排序过程中,由于所处的分组不同,会发生位置交换)
- 因为只需要一个额外的空间,所有空间复杂度最佳
2、交换排序
1. 冒泡排序(Bubble Sort)
- 基本思想:从第一个元素开始,比较相邻元素的大小,若大小有误,则对调再进行下一个元素的比较,如此扫过依次之后就可以确保最后一个元素位于正确的顺序。接着进行第二次扫描....
-
演示
-
举例
-
关键代码
void BubbleSorta(int* data, int n)//冒泡排序
{
for (int i = n - 1; i > 0; i--)//扫描次数
{
for (int j = 0; j < i; j++)//比较,交换次数
{
if (data[j] > data[j + 1])
{
swap(data[j], data[j + 1]);
}
}
cout << "第" << n - i << "次扫描:";
print(data, n);
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
int tmp = a;
a = b;
b = tmp;
}
void print(int* data, int n)//打印
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
void BubbleSorta(int* data, int n)//冒泡排序
{
for (int i = n - 1; i > 0; i--)//扫描次数
{
for (int j = 0; j < i; j++)//比较,交换次数
{
if (data[j] > data[j + 1])
{
swap(data[j], data[j + 1]);
}
}
cout << "第" << n - i << "次扫描:";
print(data, n);
}
}
int main()
{
int a[] = { 6,5,9,7,2,8 };
cout << "排序前:";
print(a, 6);
BubbleSorta(a, 6);
cout << "排序后:";
print(a, 6);
return 0;
}
-
结果
- 改进冒泡排序(增加flag判断在此次扫描中是否发生过交换)
void BubbleSorta(int* data, int n)//冒泡排序
{
for (int i = n - 1; i > 0; i--)//扫描次数
{
int flag = 0;//flag用来判断是否执行了数据交换
for (int j = 0; j < i; j++)//比较,交换次数
{
if (data[j] > data[j + 1])
{
swap(data[j], data[j + 1]);
flag++;
}
}
cout << "第" << n - i << "次扫描:";
print(data, n);
if (flag == 0)//当执行完依次扫描判断是否执行过数据交换操作,如果没有执行过数据交换操作
//表示此时数列已经完成了排序,故可直接跳出循环
break;
}
}
冒泡排序算法分析
- n个元素的冒泡排序必须执行n-1次扫描
- 最坏情况和平均情况需比较:(n-1)+(n-2)+...+2+1=n(n-1)/2次,时间复杂度为;最好情况只需扫描一次,若发现没有进行数据交换的操作,则表示已经排序完成,所以只进行n-1次比较,时间复杂度为
- 冒泡排序算法是稳定的
- 因为只需要一个额外的空间,所有空间复杂度为最佳
2. 快速排序
- 基本思想:先在数据中找中间值,并按次中间值将所有数据分成两部分,其中小于中间值的数据放在左边,大于中间值的数据放在右边,再以同样的方式处理左右两边的数据,直到排序完为止
-
关键代码
int partition(int* data, int i, int j)
{
int tmp = data[i];
while (i < j)
{
while (i<j && data[j]>=tmp)//从右边找比基准数小的数字
{
j--;
}
if (i < j)
{
data[i] = data[j];
}
while (i < j && data[i] <= tmp)
{
i++;
}
if (i < j)
{
data[j] = data[i];
}
}
data[i] = tmp;//将tmp的值赋值给i和j重合的位置
return i;
}
void QuickSort(int* data, int i, int j)
{
if (i < j)
{
int x = partition(data, i, j);
QuickSort(data, i, x - 1);//给基准数左边数据排序
QuickSort(data, x + 1, j);//给右边排序
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
int partition(int* data, int i, int j)
{
int tmp = data[i];
while (i < j)
{
while (i<j && data[j]>=tmp)//从右边找比基准数小的数字
{
j--;
}
if (i < j)
{
data[i] = data[j];
}
while (i < j && data[i] <= tmp)
{
i++;
}
if (i < j)
{
data[j] = data[i];
}
}
data[i] = tmp;//将tmp的值赋值给i和j重合的位置
return i;
}
void QuickSort(int* data, int i, int j)
{
if (i < j)
{
int x = partition(data, i, j);
QuickSort(data, i, x - 1);//给基准数左边数据排序
QuickSort(data, x + 1, j);//给右边排序
}
}
void print(int* data,int n)
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
int main()
{
int a[] = { 35,10, 42, 3, 79, 12, 62, 18, 51, 23 };
cout << "排序前:";
print(a, 10);
QuickSort(a, 0, 9);
cout << "排序后:";
print(a, 10);
return 0;
}
-
结果
3、选择排序
- 基本原理: 从n个关键字序列中找到一个最小值,并把它放到序列首端,再从剩下的n-1个关键字中选择最小值,仍然放到这n-1个关键字的序列首端,以此类推。
1. 简单选择排序
- 基本思想:通过n - i次关键字间的比较,从n - i + 1个记录中选出关键字最小的纪录,并和第i(1<=i<=n)个记录交换。
-
关键代码
void SelectionSort(int* data, int n)
{
for (int i = 0; i <=n-1; i++)//扫描次数
{
int min = data[i];
int k = i;//用k保存最小值的下标
for (int j = i; j < n; j++)
{
if (data[j] < min)
{
min = data[j];
k = j;
}
}
swap(data[i], data[k]);
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void SelectionSort(int* data, int n)
{
for (int i = 0; i <=n-1; i++)//扫描次数
{
int min = data[i];
int k = i;//用k保存最小值的下标
for (int j = i; j < n; j++)
{
if (data[j] < min)
{
min = data[j];
k = j;
}
}
swap(data[i], data[k]);
}
}
void print(int* data, int n)
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
int main()
{
int a[] = { 55,23,87,62,16 };
cout << "排序前:";
print(a, 5);
SelectionSort(a, 5);
cout << "排序后:";
print(a, 5);
return 0;
}
2. 堆积树排序法
- 堆的定义:堆是一棵完全二叉树,其中每一个非终端结点的元素均大于等于(或小于等于)其左、右孩子结点的元素值。
- 堆排序的基本思想是:对一组待排序记录,首先把它们的关键字按堆定义排列成一个序列(称为初始建堆),堆顶元素为最大关键字的记录,将堆顶元素输出;然后对剩余的记录再建堆(调整堆),得到次最大关键字记录;如此反复进行,直到全部记录有序为止,这个过程称为堆排序。
-
演示
-
关键代码1
void HeadAdjust(int* data, int k, int n)//将以k为根的子树调整为大堆积根
{
int tmp = data[k];
int j ;
if (2 * k + 2 < n)
{
if (data[k] >= MAX(data[2 * k + 1], data[2 * k + 2]))//根结点大于其两个孩子结点
{
;
}
else if (data[2 * k + 1] > data[2 * k + 2])//左孩子结点的值大于右孩子结点
{
j = 2 * k + 1;
swap(data[k], data[2 * k + 1]);
for (int i = j; i < n; i = i * 2 + 1)
{
HeadAdjust(data, i, n);
}
}
else
{
j = 2 * k + 2;
swap(data[k], data[2 * k + 2]);
for (int i = j; i < n; i = i * 2 + 2)
{
HeadAdjust(data, i, n);
}
}
}
else if (2 * k + 1 < n)//以k为根结点的时候,只有左孩子结点的时候
{
if (data[k] < data[2 * k + 1])
{
swap(data[k], data[2 * k + 1]);
}
}
}
void BulidHeap(int* data, int n)//建立大堆积树
{
for (int i = (n - 2) / 2; i >= 0; i--)//(n-2)/2是树中第一个非叶子结点的下标
{
HeadAdjust(data, i, n);
}
}
void HeadSort(int* data, int n)//基于大堆积树排序
{
BulidHeap(data, n);//建立初始大堆积树
//print(data, n);
cout << "排序后:";
for (int i = n - 1; i > 0; i--)
{
cout << data[0] << " ";
swap(data[0], data[i]);
BulidHeap(data, i);
//print(data, i);
}
cout << data[0];
}
-
完整代码1
#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
int MAX(int& a, int& b)//求最大值
{
if (a < b)
return b;
else
return a;
}
void print(int* data, int n)
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
void HeadAdjust(int* data, int k, int n)//将以k为根的子树调整为大堆积根
{
int tmp = data[k];
int j ;
if (2 * k + 2 < n)
{
if (data[k] >= MAX(data[2 * k + 1], data[2 * k + 2]))//根结点大于其两个孩子结点
{
;
}
else if (data[2 * k + 1] > data[2 * k + 2])//左孩子结点的值大于右孩子结点
{
j = 2 * k + 1;
swap(data[k], data[2 * k + 1]);
for (int i = j; i < n; i = i * 2 + 1)
{
HeadAdjust(data, i, n);
}
}
else
{
j = 2 * k + 2;
swap(data[k], data[2 * k + 2]);
for (int i = j; i < n; i = i * 2 + 2)
{
HeadAdjust(data, i, n);
}
}
}
else if (2 * k + 1 < n)//以k为根结点的时候,只有左孩子结点的时候
{
if (data[k] < data[2 * k + 1])
{
swap(data[k], data[2 * k + 1]);
}
}
}
void BulidHeap(int* data, int n)//建立大堆积树
{
for (int i = (n - 2) / 2; i >= 0; i--)//(n-2)/2是树中第一个非叶子结点的下标
{
HeadAdjust(data, i, n);
}
}
void HeadSort(int* data, int n)//基于大堆积树排序
{
BulidHeap(data, n);//建立初始大堆积树
//print(data, n);
cout << "排序后:";
for (int i = n - 1; i > 0; i--)
{
cout << data[0] << " ";
swap(data[0], data[i]);
BulidHeap(data, i);
//print(data, i);
}
cout << data[0];
}
int main()
{
int a[] = { 53,17,78,9,45,65,87,32 };
cout << "排序前:";
print(a, 8);
BulidHeap(a, 8);
cout << "建立的大堆积树:";
print(a, 8);
cout << endl;
HeadSort(a, 8);
return 0;
}
-
结果1
-
改进关键代码2
void HeadAdjust(int* data, int k, int len)//将以k为根的子树调整大根堆
{
int tmp = data[k];//保存根结点
int j = 2 * k;//下标为j的结点是下标为k的结点的左孩子
while (j <= len)
{
if (j < len && data[j] < data[j + 1])//右孩子较大,j指向右孩子
{
j++;//j变为2*i+1
}
if (data[k] < data[j])
{
data[k] = data[j];
data[j] = tmp;
k = j;//修改k和j的值,继续向下筛选
j = 2 * k;
}
else
break;
}
}
void HeapSort(int* data, int len)//堆排序
{
for (int i = len / 2; i >= 1; i--)//循环建立初始堆
{
HeadAdjust(data, i, len);
}
for (int j = len; j >= 2; j--)
{
swap(data[1], data[j]);
HeadAdjust(data, 1, j-1);
}
}
-
完整代码2
#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void HeadAdjust(int* data, int k, int len)//将以k为根的子树调整大根堆(data数组必须从1开始存放)
{
int tmp = data[k];//保存根结点
int j = 2 * k;//下标为j的结点是下标为k的结点的左孩子
while (j <= len)
{
if (j < len && data[j] < data[j + 1])//右孩子较大,j指向右孩子
{
j++;//j变为2*i+1
}
if (data[k] < data[j])
{
data[k] = data[j];
data[j] = tmp;
k = j;//修改k和j的值,继续向下筛选
j = 2 * k;
}
else
break;
}
}
void HeapSort(int* data, int len)//堆排序
{
for (int i = len / 2; i >= 1; i--)//循环建立初始堆
{
HeadAdjust(data, i, len);
}
for (int j = len; j >= 2; j--)
{
swap(data[1], data[j]);
HeadAdjust(data, 1, j-1);
}
}
void print(int* data, int n)
{
for (int i = 1; i <= n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
int main()
{
//int a[] = { 53,17,78,9,45,65,87,32 };
int a[100];
int len;
cout << "输入元素总数:";
cin >> len;
cout << "依次输入各个数据:";
for (int i = 1; i <= len; i++)
{
cin >> a[i];
}
cout << "排序前:";
print(a, 8);
HeapSort(a, 8);
cout << "排序后:";
print(a, 8);
return 0;
}
4、归并排序
- 归并:把两个或多个已经有序的序列合并成一个
-
演示
-
关键代码1
void Merge(int *data, int low, int mid, int high)//二路归并
{
int* tmp = (int*)malloc((high - low + 2) * sizeof(int*));//辅助数组
if (tmp==NULL)
{
return;
}
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high)//在第一段和第二段均未扫描完时循环
{
if (data[i] <= data[j])
{
tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
}
else
{
tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
}
}
while (i <= mid)//将前半段剩下元素复制到data[]数组中
{
tmp[k++] = data[i++];
}
while (j <= high)//将后半段剩下元素复制到data[]数组中
{
tmp[k++] = data[j++];
}
for (i = low, k = 0; i <= high; i++, k++)
{
data[i] = tmp[k];//将tmp数组元素复制回data[]数组中
cout << tmp[k] << " ";
}
cout << endl;
free(tmp);
}
void MergeSort(int *data, int low, int high)
{
if (low < high)
{
int mid = (low + high) / 2;
MergeSort(data, low, mid); //对前半部分进行归并排序
MergeSort(data, mid + 1, high);//对后半部分进行归并排序
Merge(data, low, mid, high);//合并两段
}
}
-
完整代码1
#include<iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;
#define n 7//元素个数
void Merge(int *data, int low, int mid, int high)//二路归并
{
int* tmp = (int*)malloc((high - low + 2) * sizeof(int*));//辅助数组
if (tmp==NULL)
{
return;
}
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high)//在第一段和第二段均未扫描完时循环
{
if (data[i] <= data[j])
{
tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
}
else
{
tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
}
}
while (i <= mid)//将前半段剩下元素复制到data[]数组中
{
tmp[k++] = data[i++];
}
while (j <= high)//将后半段剩下元素复制到data[]数组中
{
tmp[k++] = data[j++];
}
for (i = low, k = 0; i <= high; i++, k++)
{
data[i] = tmp[k];//将tmp数组元素复制回data[]数组中
cout << tmp[k] << " ";
}
cout << endl;
free(tmp);
}
void MergeSort(int *data, int low, int high)
{
if (low < high)
{
int mid = (low + high) / 2;
MergeSort(data, low, mid); //对前半部分进行归并排序
MergeSort(data, mid + 1, high);//对后半部分进行归并排序
Merge(data, low, mid, high);//合并两段
}
}
void print(int* data)
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
int main()
{
int a[] = { 49,38,65,97,76,13,27 };
MergeSort(a,0,n-1);
print(a);
return 0;
}
-
关键代码2
void Merge(int data[], int n)
{
int* tmp = (int*)malloc(n * sizeof(int*));//辅助数组
if (tmp == NULL)
{
return;
}
int mid = (n - 1) / 2;
int i = 0, j = mid+1, k = 0;
while (i <= mid && j <= n-1)//在第一段和第二段均未扫描完时循环
{
if (data[i] <= data[j])
{
tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
}
else
{
tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
}
}
while (i <= mid)//将前半段剩下元素复制到data[]数组中
{
tmp[k++] = data[i++];
}
while (j <= n-1)//将后半段剩下元素复制到data[]数组中
{
tmp[k++] = data[j++];
}
for (i = 0;i<n;i++)
{
data[i] = tmp[i];//将tmp数组元素复制回data[]数组中
cout << tmp[i] << " ";
}
cout << endl;
free(tmp);
}
void MergeSort(int data[], int n)
{
if (n <= 1)
{
return;
}
if (n>1)
{
int mid = (n-1) / 2;
MergeSort(data, mid+1); //对前半部分进行归并排序
MergeSort(data+mid+1, n-(mid+1));//对后半部分进行归并排序(后半段起始位置元素下标是数组地址向后移动mid+1个位置)
Merge(data, n);//合并两段
}
}
-
完整代码2
#include<iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;
void Merge(int data[], int n)
{
int* tmp = (int*)malloc(n * sizeof(int*));//辅助数组
if (tmp == NULL)
{
return;
}
int mid = (n - 1) / 2;
int i = 0, j = mid+1, k = 0;
while (i <= mid && j <= n-1)//在第一段和第二段均未扫描完时循环
{
if (data[i] <= data[j])
{
tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
}
else
{
tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
}
}
while (i <= mid)//将前半段剩下元素复制到data[]数组中
{
tmp[k++] = data[i++];
}
while (j <= n-1)//将后半段剩下元素复制到data[]数组中
{
tmp[k++] = data[j++];
}
for (i = 0;i<n;i++)
{
data[i] = tmp[i];//将tmp数组元素复制回data[]数组中
cout << tmp[i] << " ";
}
cout << endl;
free(tmp);
}
void MergeSort(int data[], int n)
{
if (n <= 1)
{
return;
}
if (n>1)
{
int mid = (n-1) / 2;
MergeSort(data, mid+1); //对前半部分进行归并排序
MergeSort(data+mid+1, n-(mid+1));//对后半部分进行归并排序(后半段起始位置元素下标是数组地址向后移动mid+1个位置)
Merge(data, n);//合并两段
}
}
void print(int* data,int n)
{
for (int i = 0; i < n; i++)
{
cout << data[i] << " ";
}
cout << endl;
}
int main()
{
int n = 7;//元素个数
int a[] = { 49,38,65,97,76,13,27 };
MergeSort(a, n);
print(a,n);
return 0;
}
5、基数排序
- 基数排序(Radix Sort)是一种借助于多关键字排序的思想对单逻辑关键字进行排序的方法,即先将关键字分解成若干部分,然后通过对各部分关键字的分别排序,最终完成对全部记录的排序。
-
关键代码
void RadixSort(LinkQueue& Q, int num)//基数排序
{
LinkQueue QQ[10];//声明QQ[0]到QQ[9]一共10个队列,下标为0存放指定位置是0的数字
for (int i = 0; i <= 9; i++)//初始化10个队列
{
QQ[i].front =QQ[i].rear = (LinkNode*)malloc(sizeof(LinkNode));
QQ[i].front->next = NULL;
}
LinkNode* p = Q.front->next;
while (p != NULL)//分配
{
int y = digit(p->data, num);
EnQueue(QQ[y], p->data);
p = p->next;
}
/*int k = 0;
for (int i = 0; i < 9; i++)
{
LinkNode* s = QQ[i].front->next;
while (!IsEmpty(QQ[i]) && s != NULL)
{
a[k] = s->data;
s = s->next;
k++;
}
}*/
InitQueue(Q);//初始化队列
LinkNode* p2 = (LinkNode*)malloc(sizeof(LinkNode));
if (p2 == NULL)
exit(0);
p2->next = NULL;
for (int i = 0; i < 9; i++)//收集
{
LinkNode* s = QQ[i].front->next;
while (!IsEmpty(QQ[i]) && s != NULL)
{
p2->data = s->data;
EnQueue(Q, s->data);
s = s->next;
}
}
}
-
完整代码
#include<iostream>
#include<stdio.h>
#include<stdbool.h>
using namespace std;
typedef int ElemType;
typedef struct LinkNode// 链式队列结点
{
ElemType data;
struct LinkNode* next;
}LinkNode,*LinkList;
typedef struct//链式队列
{
LinkNode* front, * rear;//队列的队头和队尾指针
}LinkQueue;
void InitQueue(LinkQueue& Q)//初始化队列(带头结点)
{
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
bool IsEmpty(LinkQueue& Q)//判空
{
if (Q.front == Q.rear)
return true;
else
return false;
}
void EnQueue(LinkQueue& Q,ElemType x)//新元素入队
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
}
void EnQueue2(LinkQueue& Q)//连续多次进队
{
LinkNode* s;
cout << "请依次输入元素(以输入9999结束):";
ElemType x;
cin >> x;
while (x != 9999)
{
s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
cin >> x;
}
}
int digit(ElemType data, int num)//取第num位数字
{
int tmp;
while (num!=0)
{
tmp = data % 10;
data = data / 10;
num--;
}
return tmp;
}
void RadixSort(LinkQueue& Q, int num)//基数排序
{
LinkQueue QQ[10];//声明QQ[0]到QQ[9]一共10个队列,下标为0存放指定位置是0的数字
for (int i = 0; i <= 9; i++)//初始化10个队列
{
QQ[i].front =QQ[i].rear = (LinkNode*)malloc(sizeof(LinkNode));
QQ[i].front->next = NULL;
}
LinkNode* p = Q.front->next;
while (p != NULL)//分配
{
int y = digit(p->data, num);
EnQueue(QQ[y], p->data);
p = p->next;
}
/*int k = 0;
for (int i = 0; i < 9; i++)
{
LinkNode* s = QQ[i].front->next;
while (!IsEmpty(QQ[i]) && s != NULL)
{
a[k] = s->data;
s = s->next;
k++;
}
}*/
InitQueue(Q);//初始化队列
LinkNode* p2 = (LinkNode*)malloc(sizeof(LinkNode));
if (p2 == NULL)
exit(0);
p2->next = NULL;
for (int i = 0; i < 9; i++)//收集
{
LinkNode* s = QQ[i].front->next;
while (!IsEmpty(QQ[i]) && s != NULL)
{
p2->data = s->data;
EnQueue(Q, s->data);
s = s->next;
}
}
}
void print(LinkQueue Q)//打印
{
LinkNode* p = Q.front->next;
while (p != NULL)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
int main()
{
LinkQueue q;
InitQueue(q);//初始化链式队列
EnQueue2(q);//入队
RadixSort(q, 1);//基于个位的基数排序
print(q);
return 0;
}