区分方法:
如果按照难易程度来区分:
(4个简单排序算法中除了基数排序,剩下3个代码都是双重for循环)
4个简单:直接插入排序 简单选择排序 冒泡排序 基数排序
4个难:希尔排序 堆排序 归并排序 快速排序
如果按照稳定性来区分:
4个稳定:直接插入排序 冒泡排序 归并排序 基数排序
4个不稳定:希尔排序 简单选择排序 堆排序 快速排序
直接插入排序:
时间复杂度O(n^2) 空间复杂度O(1) 稳定性:稳定
//直接插入排序 时间复杂度O(n^2) 空间复杂度O(1) 稳定性:稳定
void Insert_Sort(int arr[], int len)
{
//for (int i = 0; i < len - 1; i++)//控制的是趟数 如果有len个值,需要len-1趟即可
for (int i = 1; i < len; i++)
{
//此时i下标指向的就是我们这一趟中的待插入值
int tmp = arr[i];
int j = i - 1;
for (; j >= 0; j--)//内部for循环控制的是 单独一趟中,对于已//排序好的序列从右至左的遍历
{
if(arr[j] > tmp)
arr[j+1] = arr[j];
else
//将值挪回去结束
break;
}
arr[j + 1] = tmp;
}
}
void Show(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
希尔排序:
时间复杂度O(n^1.5) 空间复杂度O(1) 稳定性:不稳定
//希尔排序 时间复杂度O(n^1.5) 空间复杂度O(1) 稳定性:不稳定
void Shell(int arr[], int len, int gap)//gap是当前增量大小
{
for (int i = gap; i < len; i++)//i控制的是向前插入调整的值的下标
{
//此时i下标指向的就是我们这一趟中的待插入值
int tmp = arr[i];
int j = i - gap;
for (; j >= 0; j-=gap)//内部for循环控制的是 单独一趟中,对于已//排序好的序列从右至左的遍历
{
if (arr[j] > tmp)
arr[j + gap] = arr[j];
else
//将值挪回去结束
break;
}
arr[j + gap] = tmp;
}
}
void Shell_Sort(int arr[], int len)
{
int gap[] = { 5,3,1 };//增量数组
for (int i = 0; i < sizeof(gap) / sizeof(gap[0]); i++)
{
Shell(arr, len, gap[i]);
}
}
简单选择排序:
时间复杂度O(n^2) 空间复杂度O(1) 稳定性:不稳定
void Intersect_Sort(int arr[], int len)
{
for (int i = 0; i < len - 1; i++)//控制的是趟数
{
int min = i;
for (int j = i + 1; j < len; j++)//找到这一趟中,待排序序列中最小值所在位置
{
if (arr[j] < arr[min])
{
min = j;
}
}
//和此时待排序序列第一个值进行交换
if (min != i)//arr[min] arr[i]
{
int tmp = arr[min];
arr[min] = arr[i];
arr[i] = tmp;
}
}
}
void Show(int arr[], int len)
{
for (int i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
}
冒泡排序及其优化:
时间复杂度O(n^2) 空间复杂度O(1) 稳定性:稳定
void Bubble_Sort(int arr[], int len)
{
for (int i = 0; i < len - 1; i++)//控制的是趟数
{
//j指向的是两两比较中偏左边的那个值
for (int j = 0; j + 1 <= len - 1 - i; j++)//控制的是这一趟中,对于待排序序列的从前向后的两两比较
{
if (arr[j]>arr[j+1])//如果左边 > 右边 则交换
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
//冒泡排序的优化
void Bubble_Sort2(int arr[], int len){
for (int i = 0; i < len - 1; i++)//控制的是趟数
{
bool tag = true;//标签申请在内部for循环上面
//j指向的是两两比较中偏左边的那个值
for (int j = 0; j + 1 <= len - 1 - i; j++)//控制的是这一趟中,对于待排序序列的从前向后的两两比较
{
if (arr[j] > arr[j + 1])//如果左边 > 右边 则交换
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
tag = false;
}
}
if (tag)
{
return;
}
}
}
堆排序:
选择类排序
默认升序用大顶堆,默认降序用小顶堆
二叉树子节点推父节点:(i-1)/2
二叉树父节点推子节点:2i+1,2i+2
大顶堆:
是一颗完全二叉树,并且父节点的值大于子节点的值
初始状态:将一维数组arr得到,然后将这个数组臆想成完全二叉树
第一步:
将这棵完全二叉树调整为大顶堆(第一次调整需要由内到外的调整)-->从最后一个非叶子节点构成的子树开始调整,由右向左,自下向上

根据定义可知:第一次由内到外的调整调整子树顺序为:红->绿->橙->黄->黑
第二步:
将此时大顶堆的根节点(最大值)和其最后一个节点值进行交换,然后再把最后一个节点断开连接
第三步:
第二步头尾交换之后断开尾节点,然后重新调整为大顶堆(只需要调整最外层框即可)
第四步:
重复执行2,3步,直到大顶堆只剩下一个节点停止
小顶堆:
是一颗完全二叉树,并且父节点的值小于子节点的值
排序代码实现:
1.将此时要调整的子树的根节点的值,拷贝一份给tmp(防止一会被覆盖)
2.找到当前空白格子,然后判断其有无孩子,如果有则进一步找到其较大的孩子,然后将较大孩子的值和tmp的值进行比较
3.如果较大孩子的值大于tmp的值,则向上顶(将此时较大孩子的挪动到其父节点位置)
4.如果较大孩子的值向上顶,则较大孩子位置会出现新的空白格子,重复上面过程,继续判断其是否有孩子,如果有则继续找其较大孩子
5.如果较大孩子的值并不大于tmp的值,则此时可以将tmp的值挪回去了
6.如果空白格子就没有孩子节点,则认为触底了,则此时也可以将tmp的值挪回去了
7.tmp的值挪回去之后,整体调整结束
//堆排序 时间复杂度O(nlogn) 空间复杂度O(1) 稳定性:不稳定
void Heap(int arr[], int begin, int end) {//begin为当前子树的根节点,end当前小树下最后一个节点序号
int parent = begin;
int child = 2 * parent + 1;
while (child <= end) {
//判断右孩子是否存在,如果存在和左孩子比较大小
if (child + 1 <= end && arr[child + 1] > arr[child]) {
child++;
}
//较大孩子的值与根节点比较
if (arr[child] > arr[parent]) {
int tmp = arr[parent];
arr[parent] = arr[child];
arr[child] = tmp;
parent = child;
child = 2 * parent + 1;
}
else {
break;
}
}
}
void HeapSort(int arr[], int len) {
for (int i = (len - 1 - 1) / 2; i >= 0; i--) {
Heap(arr, i, len - 1);
for (int j = len - 1; j >= 1; j++) {
int tmp = arr[0];
arr[0] = arr[j];
arr[j] = tmp;
Heap(arr, 0, len - 1);
}
}
}
归并排序:
时间复杂度O(nlogn) 空间复杂度O(n) 稳定性:稳定
void Merge(int arr[], int left, int mid, int right) {
assert(arr);
int* brr = (int*)malloc((right - left + 1) * sizeof(int));
if (brr == NULL) {
exit(EXIT_FAILURE);
}
int i = left;
int j = mid + 1;
int k = 0;//用来指向brr数组用来接受元素的格子下标
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
brr[k++] = arr[i++];
}
else {
brr[k++] = arr[j++];
}
}
while (i <= mid) {
brr[k++] = arr[i++];
}
while (j <= right) {
brr[k++] = arr[j++];
}
//这时两个组的合并结果就在brr里面,最后再将brr的数据同步还给arr[left, right]上即可
/*for (int i = left; i <= right; i++)
arr[i] = brr[i - left];*/
for (int k = 0; k <= right - left + 1 - 1; k++)
arr[left + k] = brr[k];
free(brr);
brr = NULL;
}
void Divide(int arr[], int left, int right){
int mid = (left + right) / 2;
if (left < right){
//左半边[Left, mid] 右半边[mid+1,right]
Divide(arr, left, mid);//左半边继续递归分
Divide(arr, mid + 1, right);//右半边继续递归分
Merge(arr, left, mid, right); //回退的左半边和右半边进行合并
}
}
void Merge_Sort(int arr[], int len)
{
//执行递归分函数
Divide(arr, 0, len - 1);
}
基数排序:
时间复杂度O(d(n+r)) 空间复杂度O(N) 稳定性:稳定
对所有的待排序数据从最低位(个位)开始处理,此时数据只看最低位有序,然后按十位处理,然后按百位处理
处理规则:将数据按照对应最低位的值插入到对应的队列中,再将所有队列中的值全部取出,取出顺序为0-9,放回数组中
//找到数组中最大值,返回最大值的位数
int Get_MaxNum_Figure(int arr[], int len) {
int max = arr[0];
for (int i = 1; i < len; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
if (max == 0) {
return 1;
}
int count = 0;
while (max != 0) {
max /= 10;
count++;
}
return count;
}
//根据给定的值和对应的位,将这个值的这个位是多少返回
int Get_Num_figureNum(int num, int fig) {
while (fig > 0) {
num /= 10;
fig--;
}
return num % 10;
//return (arr >> fig) & 1;
}
//按照传递进来的位,将所有数据处理一遍
#include<queue>//队列头文件
void Radix(int arr[], int len, int fig) {
//std::queue<int> qu;//申请一个队列
std::queue<int> quarr[10];//十个格子的数组,每一个格子是一个队列
for (int i = 0; i < len; i++) {
int index = Get_Num_figureNum(arr[i], fig);
quarr[index].push(arr[i]);
}
//0-9中取值到arr
int k = 0;
for (int i = 0; i <= 9; i++) {
while (!quarr[i].empty()) {
arr[k] = quarr[i].front();
quarr[i].pop();
k++;
}
}
}
void RadixSort(int arr[], int len) {
int maxnum_figure = Get_MaxNum_Figure(arr, len);
for (int i = 0; i < maxnum_figure; i++) {
Radix(arr, len, i);//i=0为个位处理...
}
}
快速排序:
平均时间复杂度O(nlogn) 最坏时间复杂度O(n^2) 空间复杂度O(logn) 稳定性:不稳定
以给定的基准值为中心,将待排序序列分成两部分,左边小于基准值,右边大于
递归实现:
//单次划分函数
int Partition(int arr[], int left, int right) {
int tmp = arr[left];
while (left < right) {
while (left < right && arr[right] >= tmp) {
right--;
}
//内部循环退出情况1:指针相遇了
if (left == right) {
break;
}
//内部循环退出情况2:在指针相遇前找到了一个小于或者等于tmp的值
arr[left] = arr[right];
while (left < right && arr[left] <= tmp) {
left++;
}
if (left == right) {
break;
}
arr[right] = arr[left];
}
arr[left] = tmp;
return left;
}
//递归函数
void Quick(int arr[], int left, int right) {
if (left >= right) {
return;
}
int par = Partition(arr, left, right);
Quick(arr, left, par - 1);
Quick(arr, par + 1, right);
}
void QuickSort(int arr[], int len) {
Quick(arr, 0, len - 1);
}
非递归实现:
//非递归,用栈实现
#include<stack>
void QuickSortNo(int arr[], int len) {
std::stack<int> st;
st.push(0);
st.push(len - 1);
while (!st.empty()) {
//取出一组左右边界
int right = st.top();
st.pop();
int left = st.top();
st.pop();
int par = Partition(arr, left, right);
//判断左右基准个数
if (par - 1 > left) {
st.push(left);
st.push(par - 1);
}
if (par + 1 < right) {
st.push(par + 1);
st.push(right);
}
}
}
优化:
三数取中法:
//三数取中法
void ThreeNumsGetMid(int arr[], int left, int right)
{
int mid = (left + right) / 2;
if (arr[left] > arr[mid])
{
int tmp = arr[left];
arr[left] = arr[mid];
arr[mid] = tmp;
}
//现在可以保证,最左端值和中间值这两个值中的较大值,在mid下标上
if (arr[mid] > arr[right])
{
int tmp = arr[mid];
arr[mid] = arr[right];
arr[right] = tmp;
}
//现在可以保证,此时最左端值和中间值和最右端值这三个值中的最大值,在right下标上
if (arr[left] < arr[mid])
{
int tmp = arr[left];
arr[left] = arr[mid];
arr[mid] = tmp;
}
//现在可以保证,三者中的不大不小的那个值,正好在left下标上
}

2万+

被折叠的 条评论
为什么被折叠?



