本篇简单梳理了下常见的七种排序算法:冒泡排序、简单选择排序、直接插入排序、希尔排序、堆排序、归并排序、快速排序,以图例形式,解释如何进行排序。
该图为几种排序算法的复杂度、稳定性等。
1. 冒泡排序
- 思路:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
void BubbleSort(int* a, int n) //冒泡排序
{
int end = n - 1;
while (end > 0)
{
for (int i = 0; i < end; ++i)
{
if (a[i] > a[i + 1])
Swap(&a[i], &a[i + 1]);
}
--end;
}
}
2. 简单选择排序
- 思路:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
void SelectSort(int* a, int n) //选择排序
{
int begin = 0;
int end = n - 1;
int max_index, min_index; //index->指针
while (begin <= end)
{
min_index = max_index = begin;
for (int i = begin + 1; i < end; ++i)
{
if (a[i] > a[max_index])
max_index = i; //将大的元素地址给与max,方可经行下一轮比较
if (a[i] < a[min_index])
min_index = i;
Swap(&a[i], &a[max_index]); //&
if (max_index == begin) //当最大值在开始时,需要将最小地址交换给最大地址,否则最大值的位置会有冲突
{
max_index = min_index;
}
Swap(&a[i], &a[min_index]); //&
}
--end;
++begin;
}
}
3. 直接插入排序
- 思路:当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。
void InsertSort(int* a, int n) //直接插入排序
{
//思路:在已排好的序列中插入数据,大于的数往后移动覆盖,插入的数要大于前一个或者小于后一个(插入a[0]不用比较前一个)
//插入的数据的位置为a[0]-a[n-1],最多比较到n-2的位置
//时间 O(n*n):最好O(n)、最坏O(n*n), 空间 O(1)、稳定
for (int i = 0; i < n - 1; ++i)
{
int end = i;
int tmp = a[end + 1]; //要插入的数
while (end >= 0) //=0 往原序列的头插入
{
if (a[end] > tmp) //小往前插
{
a[end + 1] = a[end]; //覆盖数
--end;
}
else
{
break;
}
}
a[end + 1] = tmp; //前面已排好序,直接插入最后一个w位置
}
}
4. 希尔排序
- 思路:设置一个gap,gap的数值代表数据之间间隔gap,然后预排序,预排序完成后基本接近于排序,再进行一次插入排序。
void ShellSort(int* a, int n) //哈希排序
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1; //设置间距,+1确保进行插入排序 gap = 1 表示直接插入排序
for (int i = 0; i < n - gap; ++i) //当i走到n-1-gap进行最后比较数据交换
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + gap] = a[end];
end = end - gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
5. 堆排序
- 排升序要建大堆,排降序建小堆。
void AdujustDown(int* a, int n, int parent) //向下调整
{
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child] < a[child + 1]) //选择小的孩子
++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 - 2) / 2; i >= 0; --i) //建堆
{
AdujustDown(a, n, i);
}
int end = n - 1; //堆排序
while (end > 0)
{
Swap(&a[0], &a[end]); //*已建立大堆,将a[0]与a[end]交换,再经行堆调整
AdujustDown(a, end, 0);
--end;
}
}
6. 归并排序
- 将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序;若将两个有序表合并成一个有序表,称为二路归并。
void _MergeSort(int* a, int left, int right, int* tmp)
{
//分解
if (left == right) //子问题只左右两个数
return;
int mid = left + (right - left) / 2;
_MergeSort(a, left, mid, tmp); //区间划分[left, mid] 、[mid+1, right]
_MergeSort(a, mid + 1, right, tmp);
//合并
//左右之间有序,归并
int begin1 = left; int end1 = mid;
int begin2 = mid + 1; int end2 = right;
int i = left; //tmp
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
//拷回原数组中,开始将数据均放于临时数组中
memcpy(a + left, tmp + left, sizeof(int)*(i - left)); //原、目标、大小(right-left+1)但针对每个小区间
}
void MergeSort(int* a, int n) //归并排序
{
int* tmp = (int*)malloc(sizeof(int)*n); //合并时,先把数放到tmp数组中,经排序后再覆盖到原数组,直到最后一趟完成即排序完成
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
7. 快速排序
- 思路:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
int OneSort(int* a, int left, int right)
{
int key = a[right];
int key_index = right;
while (left < right)
{
while (left < right && a[left] <= key) //a[left] <= key,进入循环++,大于时出来与右指针所找的数经行交换
++left;
while (left < right && a[right] >= key)
--right;
Swap(&a[left], &a[right]);
if (left < right)
{
Swap(&a[left], &a[right]);
++left;
--right;
}
}
Swap(&a[left], &a[key_index]); //left = right时,选取右key,则a[left] 与 key所指的数值交换
return left;
}
void QuickSort1(int* a, int left, int right) //快速排序
{
//思路:左右指针法,在最左或最右选取一个key
//左指针向右找大于key所对应的数,右指针找小于key所对应的数值,找到左右指针对应的数值交换
//右边作key,先走左,后走右;左边为key,先走右,后走左;指针相遇时,left与右key交换、right与左key交换
if (left >= right) //判断左右区间大小
return;
int key_index = OneSort1(a, left, right); //区间分为三部分:[left, key_index-1] key [key_index + 1, right]
QuickSort1(a, left, key_index-1);
QuickSort1(a, key_index + 1, right);
}
【Yang】
- 哪里有问题,希望大家提出来,我会及时改正 o( ̄▽ ̄)ブ 。