//交换
void swap(int *x, int *y) {
int tmp = *x;
*x = *y;
*y = tmp;
}
//一.交换排序
/*
1.1冒泡排序
*/
/*
(1)传统冒泡排序:
比较相邻的两个数,如果前者比后者大,则进行交换。每一轮排序结束,选出一个未排序中最大的数放到数组后面
*/
void bubbleSort(int *arr, int n) {
printf("%s\n",__func__);
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++)
{
//如果前面的数比后面大,进行交换
if (arr[j] > arr[j + 1]) { //较大的数排在后面
swap(&arr[j], &arr[j + 1]);
}
}
}
}
/*
(2)冒泡排序改进1
设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
*/
void bubbleSort1(int *arr, int n) {
printf("%s\n", __func__);
int i = n - 1; //初始时,最后位置保持不变
while (i > 0) {
int pos = 0; //每趟开始时,无记录交换
for (int j = 0; j < i; j++)
if (arr[j] > arr[j + 1]) {
pos = j; //记录交换的位置
swap(&arr[j],&arr[j + 1]);
}
i = pos; //为下一趟排序作准备
}
}
/*
(3)冒泡排序改进2
传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半
*/
void bubbleSort2(int *arr, int n) {
printf("%s\n", __func__);
//设置数组左右边界
int left = 0;
int right = n - 1;
//当左右边界未重合时,进行排序
while (left < right) {
//从左到右遍历选出最大的数放到数组右边
for (int i = left; i < right; i++)
{
if (arr[i] > arr[i + 1])
{
swap(&arr[i],&arr[i + 1]);
}
}
right--;
//从右到左遍历选出最小的数放到数组左边
for (int j = right; j > left; j--)
{
if (arr[j] < arr[j-1])
{
swap(&arr[j],&arr[j - 1]);
}
}
left++;
}
}
/*
1.2快速排序
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序
*/
//分治法把数组分成两份
int patition(int *a, int left, int right) {
int key = a[right]; //基准元素,
int j = left; //用来遍历数组
int i = j - 1; //用来指向小于基准元素的位置
//从左到右遍历数组,当遇到小于基准元素的元素时,把它和左边第一个大于基准元素的元素进行交换
for (; j < right; ++j) {
if (a[j] <= key)
swap(&a[j], &a[++i]);
}
//把基准元素放到中间
swap(&a[right], &a[++i]);
//返回数组中间位置
return i;
}
//快速排序
void quickSort(int *a, int left, int right) {
printf("%s,left:%d,right:%d\n", __func__,left,right);
if (left >= right) {
return;
}
int mid = patition(a, left, right);
quickSort(a, left, mid - 1);
quickSort(a, mid + 1, right);
}
//二.插入排序
/*
2.1 直接插入排序
*/
/*
(1)直接直接插入排序
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
*/
void insertSort(int *arr, int n) {
printf("%s\n", __func__);
for (int i = 1; i < n; i++) { //待排序从第一个元素位置开始
int key = arr[i];//预排序元素
int j = i - 1; //(i-1)元素位置之前可以认为已经被排序
//已排序序列中从后向前扫描,如果已排序元素大于该预排序元素,将已排序元素移到后一位置
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key; //找到已排序的元素中小于或者等于预排序元素的后面一个位置,并插入
}
}
/*
(2)直接插入排序改进
查找插入位置时使用二分查找的方式
*/
void insertSort2(int *arr, int n) {
printf("%s\n", __func__);
for (int i = 1; i < n; i++) {
int key = arr[i];
int left = 0;
int right = i - 1;
//二分查找
while (left <= right) {
int middle = (int)((left + right) / 2);
if (key < arr[middle]) {
right = middle - 1;
}
else {
left = middle + 1;
}
}
for (int j = i - 1; j >= left; j--) {
arr[j + 1] = arr[j];
}
arr[left] = key;
}
}
/*
2.2 希尔(shell)排序
在直接插入排序的思想下设置一个最小增量dk,刚开始dk设置为n/2。进行插入排序,随后再让dk=dk/2,再进行插入排序,直到dk为1时完成最后一次插入排序,此时数组完成排序。
*/
void insertSort(int *arr, int n, int dk) {
for (int i = 1; i < n; i++) { //待排序从第一个元素位置开始
int key = arr[i];//预排序元素
int j = i - dk; //(i-dk)元素位置之前的元素可以认为已经被排序
//已排序序列中从后向前扫描,如果已排序元素大于该预排序元素,将已排序元素移到后dk位置
while (j >= 0 && arr[j] > key) {
arr[j + dk] = arr[j];
j -= dk;
}
arr[j + dk] = key; //找到已排序的元素中小于或者等于预排序元素的后dk位置,并插入
}
}
void shellSort(int *arr, int n) {
printf("%s\n", __func__);
int dk = n/2 ; // 设置初始dk
while (dk >= 1) {
printf("dk:%d\n",dk);
insertSort(arr, n, dk);
dk /= 2;
}
}
//三.选择排序
/*
3.1 传统选择排序
首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾
*/
void selectSort(int *arr, int n) {
printf("%s\n", __func__);
for (int i = 0; i < n - 1; i++) {
int minIndex = i; //临时变量用于存放数组最小值的位置
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) { //寻找最小的数
minIndex = j; //将最小数的索引保存
}
}
if (minIndex != i) {
swap(&arr[i], &arr[minIndex]);
}
}
}
/*
3.2 堆(Heap)排序
*/
// 创建大堆顶,i为当节点,n为堆的大小
// 从第一个非叶子结点i从下至上,从右至左调整结构
// 从两个儿子节点中选出较大的来与父亲节点进行比较,如果儿子节点比父亲节点大,则进行交换
void creatHeap(int* arr, int i, int n) {
//注意数组是从0开始计数,所以左节点为2*i+1,右节点为2*i+2
for (; i >= 0; --i)
{
int left = i * 2 + 1; //左子树节点
int right = i * 2 + 2; //右子树节点
int j = 0;
//选出左右子节点中最大的
if (right < n) {
arr[left] > arr[right] ? j = left : j = right;
}
else {
j = left;
}
//大顶堆满足arr[i]>=arr[2i+1]&&arr>=arr[2i+2]
//若儿子节点比父亲节点大,则进行交换
if (arr[j] > arr[i]) {
swap(&arr[j],&arr[i]);
}
}
}
//进行堆排序,依次选出最大值放到最后面
void heapSort(int* arr, int n) {
printf("%s\n", __func__);
//初始化构造堆
creatHeap(arr, n/2 - 1, n);
//交换第一个元素和最后一个元素后,堆的大小减1
for (int j = n - 1; j >= 0; j--) {
//最后一个元素和第一个元素进行交换
swap(&arr[0],&arr[j]);
int i = j/2 - 1;
creatHeap(arr, i, j);
}
}
//四.归并排序
/*
4.1
*/
// 合并两个已排好序的数组
void merge(int a[], int left, int mid, int right)
{
int len = right - left + 1; //数组的长度
int *temp = new int[len]; // 分配个临时数组
int k = 0;
int i = left; // 前一数组的起始元素
int j = mid + 1; // 后一数组的起始元素
while (i <= mid && j <= right)
{
//选择较小的存入临时数组
temp[k++] = a[i] <= a[j] ? a[i++] : a[j++];
}
while (i <= mid)
{
temp[k++] = a[i++];
}
while (j <= right)
{
temp[k++] = a[j++];
}
for (int k = 0; k < len; k++)
{
a[left++] = temp[k];
}
delete temp;
}
// 递归实现的归并排序
void mergeSort(int a[], int left, int right)
{
if (left == right) {
return;
}
//把长度为n的输入序列分成两个长度为n/2的子序列
int mid = (int)(left + right) / 2;
//对这两个子序列分别采用归并排序
mergeSort(a, left, mid);
mergeSort(a, mid + 1, right);
//将两个排序好的子序列合并成一个最终的排序序列
merge(a, left, mid, right);
}
int main()
{
int arr[] = {10,6,5,2,3,8,7,4,9,1};
int n = sizeof(arr) / sizeof(int);
//bubbleSort(arr, n);
//bubbleSort1(arr, n);
//bubbleSort2(arr, n);
//quickSort(arr,0,n-1);
//insertSort(arr, n);
//insertSort2(arr, n);
//shellSort(arr, n);
//selectSort(arr, n);
//heapSort(arr, n);
mergeSort(arr,0, n-1);
printf("排序后的数组为:\n");
for (int j = 0; j < n; j++) {
printf("%d ", arr[j]);
}
return 0;
}