#学习自用#
排序算法
冒泡排序优化
冒泡排序的本质是相邻两个元素比较大小,并交换顺序,完成一趟排序,可以把最大或者最小的数字放在序列的末尾,再进行下一趟的排序,一直进行到最后只剩两个元素进行比较。这其中可能会出现并不需要执行这么多次的情况,传统的冒泡一直会执行到最后一趟,执行了不必要的比较。
cpp
void BubbleSort(int* a, int n)
{
for (int j = n - 1; j >0 ; j--)
{
bool flag = true;
for (int i = 0; i < j; i++)
{
if (a[i] > a[i + 1])
{
swap(a[i], a[i + 1]);
flag = false;
}
}
if (flag)
break;
}
for (int i = 0; i < n; i++)
{
cout << a[i]<<' ';
}
}
核心就是判断这一趟的排序中是否发生了位置交换,如果有那么说明排序后的数列可能还是无序的,如果没有交换,那么数列就已经是有序的了,无需再进行比较。
选择排序
选择排序的思想是,默认第一个数为最小值(也可以是最大,取决于你想升序还是降序排),再遍历之后的元素,去找比默认最小还要小的数,将其设置为最小数,完成第一次变量后再交换位置。第一个数排好后就用同样的方法排第二个数。与冒泡的区别是,选择排序最多调用元素个数-1次swab。
cpp
void SelectSort(vector<int>& arr)
{
for (int i = 0; i < arr.size() - 1; i++)
{
int minsubscript = i;
for (int j = 1 + i; j < arr.size(); j++)
{
if (arr[j] < arr[minsubscript])
minsubscript=j;
}
swap(arr[i], arr[minsubscript]);
}
for (int i : arr)
{
cout << i << ' ';
}
}
插入排序
插入排序的思想是将数据分为有序区和无序区,无序区的数插入有序区,以升序为例,一个元素默认有序,如果第二个元素大于它那么有序区增加一个元素,如果比它小,就执行插入,确定要插入的位置,并把插入位以及有序区之后的元素后移,完成之后有序区增加一个元素,再向后遍历。
cpp
void InsertSort(vector<int>& arr)
{
for (int j = 1; j < arr.size(); j++)
{
for (int i = 0; i < j; i++)
{
if (arr[j] < arr[i])
{
swap(arr[j], arr[i]);
}
}
}
for (int i : arr)
cout << i << ' ';
}
希尔排序
希尔排序是插入排序的一种,是插入的改进。设定步长,将数据进行分组然后使用插入排序,使数据出现局部有序,再缩短步长重复操作,直到设置步长为1,进行最后一次插入排序。
cpp
void ShellSort(vector<int>& arr)
{
for (int step = arr.size() / 2; step > 0; step /= 2)
{
for (int start = 0; start < step; start++)
ShellInsert(arr, start, step);
}
for (int i : arr)
cout << i << ' ';
}
void ShellInsert(vector<int>& arr, int start, int step)
{
for(int j=start+step;j<arr.size();j+=step)
for (int i = start; i < j; i += step)
{
if (arr[j] < arr[i])
swap(arr[j], arr[i]);
}
}
计数排序
计数排序是对排序元素具有一定要求才能使用的排序,如果排序元素全部为非负整数,那么我们可以遍历需要排序的元素找到最大值,设置一个计数数组,其中数组下标代表元素的值,该下标位置所对应的元素表示这个值存在多少个同样的数,这样我们只需要把排序元素遍历,存入相应的计数数组位置即可完成排序。
cpp
void CountSort(vector<int>& arr)
{
int Max = *max_element(arr.begin(), arr.end());//要添加头文件algorithm,返回最大值所在地址
int* CountArr = new int[Max + 1];
memset(CountArr, 0, sizeof(int) * (Max + 1));
for (int i : arr)
CountArr[i]++;
for (int i = 0; i < Max + 1; i++)
{
while (CountArr[i])
{
cout << i << ' ';
CountArr[i]--;
}
}
delete[]CountArr;
}
计数数组记得初始化,否则会出现随机数,导致while(CountArr[i])进入死循环。
堆排序
通过构建大根堆,将最大值排在第一个父节点,再将最后一个节点与第一个父节点的值交换,结尾节点-1,重新构建大根堆,循环执行,即可完成升序排序。
cpp
void BuidHeap(vector<int>& arr, int end)
{
int Fnode = (end - 1) / 2;//本次排序最后一个父节点的下标
for (; Fnode >= 0; Fnode--)
{
int Lnode = Fnode * 2 + 1;//左节点下标
if (Lnode == end && arr[Lnode] > arr[Fnode])
swap(arr[Lnode], arr[Fnode]);
else if (Lnode < end && arr[Lnode] > arr[Lnode + 1]&& arr[Lnode] > arr[Fnode])
swap(arr[Lnode], arr[Fnode]);
if (Lnode < end && arr[Lnode] < arr[Lnode + 1])
{
Lnode += 1;
if(arr[Lnode] > arr[Fnode])
swap(arr[Lnode], arr[Fnode]);
}
}
swap(arr[0], arr[end]);
}
void HeapSort(vector<int>& arr)
{
for (int end = arr.size() - 1; end > 0; end--)//end 为本次排序的结尾下标
BuidHeap(arr, end);
for (int i : arr)
cout << i << ' ';
}
快速排序
以第一个元素为基准,设置左端点和右端点,通过与基准比较,交换元素顺序,使左边的元素小于基准,右边的元素大于基准,使用递归实现数组升序排序。
cpp
void FastSort(vector<int>& arr, int l, int r)
{
if (l >= r)return;
int i = l, j = r;
int temp = arr[l];//store basement
while (i < j)
{
while (arr[j] >= temp && i < j)j--;//这里必须限制i<j,否则j可能会减到i之后
if (arr[j] < temp)
{
arr[i] = arr[j];
i++;
}
while (arr[i] <= temp && i < j)i++;
if (arr[i] > temp)
{
arr[j] = arr[i];
j--;
}
}
arr[i] = temp;
FastSort(arr, l, i - 1);
FastSort(arr, i+1, r);
}
这里运用了递归的思想,确定基准位置后, 用同样的步骤对基准左右两边进行排序,递归必须要确立一个终止条件,这里是 l>=r,l和r两个变量用于边界表示,不能改变,所以在遍历数据的时候使用了临时变量i,j
归并排序
归并排序是利用了递归的特性,在不断2分数组元素,直至每个小组元素只剩一个时,逐层退出递归并进行排序。
cpp
void DivideMerge(vector<int>& arr, int l, int r)
{
if (l == r)return;
int mid = (l + r) / 2;
DivideMerge(arr, l, mid );
DivideMerge(arr, mid + 1, r);
MergeSort(arr, l, r, mid);
}
void MergeSort(vector<int>& arr, int l, int r, int mid)
{
int i = l, j = mid + 1;//分别指向合并的两个部分的开头,进行遍历
int* temp = new int[r - l + 1];//用于存排好顺序后的数组,这里采用的是分别遍历比较大小,赋值给对应位置
int index = 0;//临时数组下标
while (i <= mid && j <= r)
{
if (arr[i] < arr[j])
temp[index++] = arr[i++];
else
temp[index++] = arr[j++];
}
while(i <= mid)temp[index++] = arr[i++];//保证所有的数都放入的临时数组
while (j <= r)temp[index++] = arr[j++];
for (int i = 0; i < index; i++)
arr[l + i] = temp[i];
delete[]temp;
}
要注意,临时数组赋值给原来的数组时,原数组的下标为 l+i ,并非从 i 开始。