🍁前言
本篇博客有两大部分:
①附上八个排序的详解版链接,方便大家跳转,查看各排序详细的算法思路和实现过程;
②八大排序的源码,复制后可直接进行排序(升序),为了汇总知识点和方便日后查阅。
目录
二、 源代码
1.?插入排序
2. 希尔排序
3. 选择排序
4. 堆排序
5. 冒泡排序
6.? 快速排序
6.1 hoare 版本
6.2 挖坑法
6.3 前后指针法
6.4 非递归版本
7. 归并排序
7.1 递归版本
7.2 非递归版本
8. 计数排序
一、 排序详解链接
下面是各种排序的详解版,大家可以直接链接跳转。
源码在下半部分,可以配合目录进行浏览。
插入排序、希尔排序、选择排序、堆排序
冒泡排序、快速排序、归并排序、计数排序
二、 源代码
1.?插入排序
插入排序算法实现:
- 设置初始值end为0,即从下标为0处开始,假设0处数据是已经排好序;用temp记录下end+1处的数据,进入循环。
- 在循环中,如果end处的值大于temp中的值,则将end+1处覆盖,即arr[end+1]=end;再将end -- ,使end向前移动。
- 如果end处的值?<=?temp的值,则跳出循环,无需进行覆盖;直接break跳出循环。
- 跳出循环后,将temp的值赋给end+1,则是将值插入到数组中。
- 设置循环条件为?end >= 0,则?end?减到?-1?时就会跳出循环,表示着遍历完了整个数组。
//插入排序
void InsertSort(int* arr, int n)
{
for (int i = 1; i < n; i++)
{
int end = i;
int temp = arr[end];
while (end >= 1)
{
if (temp < arr[end - 1])
{
arr[end] = arr[end - 1];
}
else
{
break;
}
end--;
}
arr[end] = temp;
}
}
2. 希尔排序
希尔排序是插入排序的一种,它是针对直接插入排序算法的改进。
希尔排序又称缩小增量排序,因 DL.Shell 于 1959 年提出而得名。
它通过比较相距一定间隔的元素来进行,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。
//希尔排序
void ShellSort(int* arr, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; ++i)
{
int end = i;
int temp = arr[end + gap];
while (end >= 0)
{
if (temp < arr[end])
{
arr[end + gap] = arr[end];
}
else
{
break;
}
end -= gap;
}
arr[end + gap] = temp;
}
}
}
3. 选择排序
选择排序算法原理如下:
-
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。 -
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。 -
重复第二步,直到所有元素均排序完毕。
void swap(int* r1, int* r2)
{
int temp = *r1;
*r1 = *r2;
*r2 = temp;
}
//选择排序
void SelectSort(int* a, int n)
{
for (int i = 0; i < n-1; i++)
{
int temp = i;
for (int j = i+1; j < n; j++)
{
if (a[j] < a[temp])
{
temp = j;
}
}
swap(&a[temp], &a[i]);
}
}
4. 堆排序
void swap(int* r1, int* r2)
{
int temp = *r1;
*r1 = *r2;
*r2 = temp;
}
//堆排序
void AdjustDown(int* a, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && a[child + 1] > a[child])
{
child++;
}
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapSort(int* a, int n)
{
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
5. 冒泡排序
冒泡排序算法原理如下:?
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。? -
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素会是最大的数。? -
针对所有的元素重复以上的步骤,除了最后一个。? -
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
void swap(int* r1, int* r2)
{
int temp = *r1;
*r1 = *r2;
*r2 = temp;
}
//冒泡排序
void BubbleSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)
{
int exchange = 1;
for (int j = 0; j < n - 1 - i; j++)
{
if ( a[j + 1]< a[j])
{
swap(&a[j], &a[j + 1]);
exchange = 0;
}
}
if (exchange)
break;
}
}
6.? 快速排序
快速排序原理如下:
-
从数列中挑出一个元素,称为 "基准"(pivot); -
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作; -
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
三个版本都可以直接进行排序,并附上了三数取中的优化方式。
本博客没有快排和归并的非递归写法噢。
6.1 hoare 版本
void swap(int* r1, int* r2)
{
int temp = *r1;
*r1 = *r2;
*r2 = temp;
}
//三数取中
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
return end;
else if (a[mid] > a[begin])
return begin;
else
return mid;
}
else
{
if (a[end] < a[mid])
return end;
else if (a[end] < a[begin])
return begin;
else
return mid;
}
}
//hoare 的方法
void QuickSort_hoare(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int left = begin;
int right = end;
int keyi = left;
int mid = GetMid(a, begin, end);
swap(&a[mid], &a[keyi]);
while (left < right)
{
while (left < right && a[keyi] <= a[right])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
swap(&a[left], &a[right]);
}
swap(&(a[keyi]), &(a[right]));
keyi = right;
QuickSort_hoare(a, begin, keyi - 1);
QuickSort_hoare(a, keyi + 1, end);
}
6.2 挖坑法
void swap(int* r1, int* r2)
{
int temp = *r1;
*r1 = *r2;
*r2 = temp;
}
//三数取中
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
return end;
else if (a[mid] > a[begin])
return begin;
else
return mid;
}
else
{
if (a[end] < a[mid])
return end;
else if (a[end] < a[begin])
return begin;
else
return mid;
}
}
//挖坑法
void QuickSort_dig(int* a, int begin, int end)
{
if (begin >= end)
return;
int key = a[begin];
int piti = begin;
int left = begin;
int right = end;
while (left < right)
{
while (left < right && a[right] >= key)
{
right--;
}
a[piti] = a[right];
piti = right;
while (left < right && a[left] <= key)
{
left++;
}
a[piti] = a[left];
piti = left;
}
a[piti] = key;
QuickSort_dig(a, begin, piti - 1);
QuickSort_dig(a, piti + 1, end);
}
6.3 前后指针法
void swap(int* r1, int* r2)
{
int temp = *r1;
*r1 = *r2;
*r2 = temp;
}
//三数取中
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
return end;
else if (a[mid] > a[begin])
return begin;
else
return mid;
}
else
{
if (a[end] < a[mid])
return end;
else if (a[end] < a[begin])
return begin;
else
return mid;
}
}
//前后指针法
void QuickSort_Pointer1(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int prev = begin;
int cur = begin + 1;
int keyi = begin;
int mid = GetMid(a, begin, end);
swap(&a[mid], &a[keyi]);
while (cur <= end)
{
if (a[cur] < a[keyi] && ((++prev) != cur))
{
swap(&a[cur], &a[prev]);
}
cur++;
}
swap(&a[prev], &a[keyi]);
keyi = prev;
QuickSort_Pointer1(a, begin, keyi - 1);
QuickSort_Pointer1(a, keyi + 1, end);
}
6.4 非递归版本
需要借助栈来实现,感兴趣可以点击链接去浏览。
int PartSort3(int * a, int begin, int end)
{
int prev = begin;
int cur = begin + 1;
int keyi = begin;
int mid = GetMid(a, begin, end);
swap(&a[mid], &a[keyi]);
while (cur <= end)
{
if (a[cur] < a[keyi] && ((++prev) != cur))
{
swap(&a[cur], &a[prev]);
}
cur++;
}
swap(&a[prev], &a[keyi]);
return prev;
}
void QuickSortNonR(int* a, int begin, int end)
{
ST st;
StackInit(&st);
StackPush(&st, end);
StackPush(&st, begin);
while (!StackEmpty(&st))
{
int left = StackTop(&st);
StackPop(&st);
int right = StackTop(&st);
StackPop(&st);
int keyi = PartSort3(a, left, right);
if (keyi + 1 < right)
{
StackPush(&st, right);
StackPush(&st, keyi + 1);
}
if (left < keyi - 1)
{
StackPush(&st, keyi - 1);
StackPush(&st, left);
}
}
StackDestory(&st);
}
7. 归并排序
归并排序算法原理如下:
- 把数组从中间划分成两个子数组。
- 一直递归地把子数组划分成更小的数组,直到子数组里面只有一个元素。
- 依次按照递归的返回顺序,不断合并排好序的子数组,直到最后把整个数组顺序排好。
7.1 递归版本
//归并排序
void _MergeSort(int* a, int begin, int end, int* temp)
{
if (begin >= end)
return;
int mid = (begin + end) / 2;
_MergeSort(a, begin, mid, temp);
_MergeSort(a, mid + 1, end, temp);
int begin1 = begin, end1 = mid;
int begin2 = mid + 1, end2 = end;
int i = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] <= a[begin2])
{
temp[i++] = a[begin1++];
}
else
{
temp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
temp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
temp[i++] = a[begin2++];
}
memcpy(a + begin, temp + begin, (end - begin + 1) * sizeof(int));
}
void MergeSort(int* a, int n)
{
int* temp = (int*)malloc(sizeof(int) * n);
_MergeSort(a, 0, n - 1, temp);
free(temp);
}
7.2 非递归版本
修改边界方法一:
void MergeSortNonR1(int* a, int n)
{
int* temp = (int*)malloc(sizeof(int) * n);
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
if (end1 >= n)
{
end1 = n - 1;
begin2 = n;
end2 = n - 1;
}
else if (begin2 >= n)
{
begin2 = n;
end2 = n - 1;
}
else if (end2 >= n)
{
end2 = n - 1;
}
int j = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] <= a[begin2])
{
temp[j++] = a[begin1++];
}
else
{
temp[j++] = a[begin2++];
}
}
while (begin1 <= end1)
{
temp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
temp[j++] = a[begin2++];
}
}
memcpy(a, temp, n * sizeof(int));
gap *= 2;
}
}
修改边界方法二:
void MergeSortNonR2(int* a, int n)
{
int* temp = (int*)malloc(sizeof(int) * n);
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
if (end1 >= n || begin2 >= n)
{
break;
}
else if (end2 >= n)
{
end2 = n - 1;
}
int m = end2 - begin1 + 1;
int j = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] <= a[begin2])
{
temp[j++] = a[begin1++];
}
else
{
temp[j++] = a[begin2++];
}
}
while (begin1 <= end1)
{
temp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
temp[j++] = a[begin2++];
}
memcpy(a + i, temp + i, m * sizeof(int));
}
gap *= 2;
}
free(temp);
}
8. 计数排序
计数排序算法原理如下:
- 计算出当前数据集合的范围区间range,即最大值 - 最小值。
- 开辟range大小的计数表(使用calloc),记录每个数据出现的次数。
- 统计每个数出现的次数,放到减去最小值后其在计数表中的相对位置。
- 回写数据。遍历计数表,如果表中位置不为零,则直接回写到元素组中,其值为下标 + 最小值,则可将相对位置转回为绝对位置。
void CountSort(int* a, int n)
{
int max = a[0];
int min = a[0];
for (int i = 1; i < n; i++)
{
if (max < a[i])
max = a[i];
if (min > a[i])
min = a[i];
}
int range = max - min+1;
int* temp = (int*)calloc(range, sizeof(int));
for (int i = 0; i < n; i++)
{
temp[a[i] - min]++;
}
int j = 0;
for (int i=0;i<range;i++)
{
while (temp[i]--)
{
a[j++] = i+min;
}
}
}
|