排序算法
七大排序,个人理解为五大排序,其中两对排序思维是一致的
这是一次自己学习和手敲排序的过程。首先上主函数
#include "sort.h"
int main()
{
int arr[15]= { 8,6,65,4,98,10,15,48,27,61,20,16,49,72,81 };
//bubble_Sort(arr, 15); //冒泡排序
//quick_Sort(arr,15); //快速排序
//insert_sort(arr, 15); //插入排序
//shell_sort(arr, 15); //希尔排序
//select_sort(arr, 15); //选择排序
//mergesort(arr, 15); //归并排序
heapsort(arr, 15); //堆排序
//输出排序后的数组
for (int i = 0; i < 15; i++)
{
cout << arr[i]<<" ,";
}
cout << endl;
getchar();
return 0;
}
1、冒泡排序
两两比较大小,每一次遍历将未排序数组中最大的一个数传递到最后。
#pragma once
#include <iostream>
using namespace std;
//冒泡 两两比较大小,每一次遍历将未排序数组中最大的一个数传递到最后
void bubble_Sort(int *arr,int arrsize)
{
bool exchange = true;
if (arrsize < 2) { cout << "数组元素少于2个" << endl; return; }
for (int i = arrsize - 1 ; i > 0 && exchange; i--)
{
exchange = false; //添加一个对未完成排序的数组是否已经有序的判断
for (int j = 0; j < i ; j++)
{
if (arr[j] <= arr[j + 1]) { continue; }
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
exchange = true;
}
//记录冒泡过程
cout << "这是第" <<arrsize-i<<"次"<< endl;
for (int ii = 0; ii < 15; ii++)
{
cout << arr[ii] << " ,";
}
cout << endl;
}
cout << "\n\n";
return;
}
2、堆排序(冒泡进阶)
/*堆排序
*/
// 交换两个元素的值。
void swap(int *a, int *b) { int temp = *b; *b = *a; *a = temp; }
// 采用循环实现heapify(元素下沉)。
// arr-待排序数组的地址,start-待heapify节点的下标,end-待排序数组最后一个元素的下标。
void heapify(int *arr, int start, int end)
{
// 确定父节点和左子节点的数组下标。
int dad = start;
int son = dad * 2 + 1;
// 如果子节点的下标没有超出范围,循环继续。
while (son <= end)
{
// 先比较两個子节点大小,选择最大的。
if ((son + 1 <= end) && (arr[son] < arr[son + 1])) son++;
// 如果父节点大于子节点代表调整完毕,直接跳出函数。
if (arr[dad] > arr[son]) return;
// 否则交换父子內容再继续子节点和孙节点比较。
swap(&arr[dad], &arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
// 采用递归实现heapify。
void heapify1(int *arr, int start, int end)
{
// 确定父节点和左子节点的数组下标。
int dad = start;
int son = dad * 2 + 1;
// 如果子节点的下标没有超出范围,循环继续。
if (son > end) return;
// 先比较两個子节点大小,选择最大的。
if ((son + 1 <= end) && (arr[son] < arr[son + 1])) son++;
// 如果父节点大于子节点代表调整完毕,直接跳出函数。
if (arr[dad] > arr[son]) return;
// 否则交换父子內容再继续子节点和孙节点比较。
swap(&arr[dad], &arr[son]);
heapify(arr, son, end);
}
void heapsort(int *arr, int len)
{
int ii;
// 初始化堆,从最后一個父节点开始调整。
for (ii = (len - 1) / 2; ii >= 0; ii--) heapify(arr, ii, len - 1);
// 把第一个元素和堆最后一个元素交换,然后重新调整,直到排序完毕。
for (ii = len - 1; ii > 0; ii--)
{
swap(&arr[0], &arr[ii]);//其实堆排序采用的就是冒泡排序的思维,每次将大顶堆的根节点后最后一个元素交换,最后一个元素不再进入堆调整
heapify(arr, 0, ii - 1);
}
}
堆排序中,heapify()和heapify1()实现的是一样的功能,只是实现的方式一个是循环,一个是递归。
冒泡和堆排序思维都是每次遍历将最大(或者最小,后面一致默认升序排序)传递到数组末尾,之后不再进入遍历循环。
3、插入排序
插入排序的思维是将第0个元素当作有序序列,从下标为1的元素开始插入有序序列中。
void insert_sort(int *arr, int arrsize)
{
if (arrsize < 2)
{
//cout << "输入数组成员小于2" << endl;
return;
}
for (int i = 1;i<arrsize;i++)
{
int temp = arr[i];
int j = i - 1;
bool exchange = false;
if (arr[j] > temp)
{
for (; j >= 0 && arr[j] > temp; j--)
{
arr[j + 1] = arr[j];
//对比于希尔排序,插入排序可能存在距离太远导致元素移动成本太高,特别是数组成员数量级比较大时。
}
arr[j+1] = temp;
exchange = true;
}
if(exchange)
{
//记录插入过程
cout << "这是第" << i << "次排序" << endl;
for (int ii = 0; ii < 15; ii++)
{
cout << arr[ii] << " ,";
}
cout << endl;
}
}
return;
}
4、希尔排序(插入排序进阶版)
插入排序可能有这种情况:待插入元素需要插入的位置距离原位置太远,导致要移动的元素太多,移动成本太高,特别是数组成员数量级比较大时。
希尔排序利用分治的思维将数组等差间隔分组,分而治之。
//每次确定步长和此步长下的单个分组进行排序
void shell_insert(int *arr, int arrlength, int istep, int groupflag)
{
for (int i = istep + groupflag; i < arrlength ; i +=istep)//步长+分组标志
{
int temp = arr[i];
int j = i - istep;
if (arr[j] > temp)//只有temp小于分组前一个数,才进行移动,否则直接移动到后一个元素
{
for (; j >= 0 && arr[j] > temp; j -= istep)//进行插入排序
{
arr[j + istep] = arr[j];
//对比于希尔排序,插入排序可能存在距离太远导致元素移动成本太高,特别是数组成员数量级比较大时。
}
arr[j + istep] = temp;
}
}
}
//确定步长和步长之后的小分组
void shell_sort(int *arr, int arrsize)
{
int istep = arrsize / 2,i;
for (;istep > 0;istep/=2)//确定步长
{
for (i = 0; i < istep; i++)//获取步长确定之后的分组标志
{
shell_insert(arr, arrsize, istep,i);
}
//记录当前步长排序之后数组序列;
cout << "这是步长" << istep << "分组排序后的数组" << endl;
for (int ii = 0; ii < 15; ii++)
{
cout << arr[ii] << " ,";
}
cout << endl;
}
}
5、选择排序
和冒泡都是选择最值,将最值放入有序序列中,但是选择排序没有相邻传递的特性。
//选择排序
void select_sort (int *arr,int arrlength)
{
int min_temp,min_flag;
bool exchange;
for (int i = 0; i < arrlength; i++)//初设定最小值
{
min_temp = arr[i];
min_flag = i;
exchange = false;
for(int j=i;j<arrlength;j++)//向数组后面遍历是否有更小的值
{
if (arr[j] < min_temp)
{
min_temp = arr[j];
min_flag = j;
exchange = true;//最小值被重置标志
}
}
if (exchange) { arr[min_flag] = arr[i]; arr[i] = min_temp; }//如果最小值被更新过,进行替换
}
}
6、快速排序
挖出一个数作为基准,大的放入基准左边,小的放入基准右边,然后在两端形成的新数组,再次分边站,直到数组小于2。
带有选择排序挖坑的思维,也有希尔排序分治的思想。
//快排 挖出一个数作为基准,大的放入基准左边,小的放入基准右边,然后在两端形成的新数组,再次分边站,直到数组小于2
void quick_Sort(int *arr1, int arr1size)//挖数,填坑,分治
{
if (arr1size < 2)
{
//cout << "输入数组成员小于2" << endl;
return;
}
int temp = arr1[0];
int left = 0, right = arr1size - 1;//左右下标
bool rightmv = true;//目前是不是右下标移动
while (left < right)
{
if (rightmv)
{
if (arr1[right] >= temp) { right--; continue; }
arr1[left] = arr1[right]; //把right的数据填进左边的坑
left++; //空出的right不动,left加一
rightmv = false;
continue;
}
if (!rightmv)
{
if (arr1[left] <= temp) { left++; continue; }
arr1[right] = arr1[left]; //把left的数据填进右边的坑
right--; //空出的left不动,right减一
rightmv = true;
continue;
}
}
arr1[left] = temp; //将中心轴的数值填入
quick_Sort(arr1, right);
quick_Sort(&arr1[right + 1], arr1size - right - 1);
}
7、归并排序
归并排序是将单个元素看作一个个有序序列,再将相邻两个有序序列归并成一个有序序列,进行递归
//归并排序
void _mergesort(int *arr, int *arrtmp, int start, int end)
{
// 如果start>=end,表示该区间的元素少于两个,递归终止。
if (start >= end) return;
int mid = start + (end - start) / 2; // 计算排序区间中间的位置。
/*核心在于理解这两句递归
使用内联的思维将两个递归函数展开
*/
int istart1 = start, iend1 = mid; // 区间左边元素的第一和最后一个元素的位置。
int istart2 = mid + 1, iend2 = end; // 区间右边元素的第一和最后一个元素的位置。
_mergesort(arr, arrtmp, istart1, iend1); // 对区间左边元素递归排序。
_mergesort(arr, arrtmp, istart2, iend2); // 对区间右边元素递归排序。
int ii = start; // 已排序数组arrtmp的计数器。
// 把区间左右两边数列合并到已排序数组arrtmp中。
while (istart1 <= iend1 && istart2 <= iend2)
arrtmp[ii++] = arr[istart1] < arr[istart2] ? arr[istart1++] : arr[istart2++];
// 把左边数列其它的元素追加到已排序数组。
while (istart1 <= iend1) arrtmp[ii++] = arr[istart1++];
// 把右边数列其它的元素追加到已排序数组。
while (istart2 <= iend2) arrtmp[ii++] = arr[istart2++];
// 把已排序数组arrtmp中的元素复制到arr中。
memcpy(arr + start, arrtmp + start, (end - start + 1) * sizeof(int));
}
// 排序主函数,arr为待排序的数组的地址,len为数组的长度。
void mergesort(int *arr, unsigned int len)
{
if (len < 2) return; // 小于两个元素不需要排序。
int *arrtmp=new int[len]; // 分配一个与待排序数组相同大小的数组。
_mergesort(arr, arrtmp, 0, len - 1); // 调用递归函数进行排序。
delete []arrtmp;
}