一、排序的概念及其运用
1.排序的概念
2.排序运用
3.常见的排序算法
1.插入排序
1.直接插入排序
2.希尔排序
2.选择排序
1.选择排序
2.堆排序
3.交换排序
1.冒泡排序
2.快速排序
4.归并排序
1.归并排序
二、常见排序算法的实现
1.插入排序
核心思想:在数组[0,end-1]之间已经排序完成,现加入一个新的元素array[end],与前面end个元素进行比较,插入到合适的位置中
void Swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
void PrintArray(int* array, int n)
{
assert(array);
for (int i = 0; i < n; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
// 插入排序
//传入数组的地址array,数组中的元素个数n
void InsertSort(int* array, int n)
{
assert(array);
for (int i = 0; i < n; i++)
{
int end = i;//单次排序中最后一个元素的下标
while (end > 0)
//当end不是首元素时
{
//当尾元素小于前一个元素时,两者互换
if (array[end] < array[end - 1])
{
Swap(&array[end], &array[end - 1]);
end--;
}
//当尾元素不小于前一个元素时,退出循环
else
break;
}
}
}
// 希尔排序
//传入数组的地址array,数组中的元素个数n
//希尔排序的思想:在插入排序的基础上,扩大其间隔,减少重复次数
void ShellSort(int* array, int n)
{
assert(array);
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
//gap以3的速度下降,并保证其必能取到1
for (int i = 0; i < n - gap; i++)
//i为控制的尾元素
{
int end = i + gap;
//end为某一次的尾元素,且保证end<n
//推出i<n-gap
while (end >= gap)
{
if (array[end] < array[end - gap])
{
Swap(&array[end], &array[end - gap]);
end = end - gap;
}
else
break;
}
}
}
}
2.选择排序
核心思想:遍历一遍数组,选出数组中最大/最小的元素,进行排序
void Swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
void PrintArray(int* array, int n)
{
assert(array);
for (int i = 0; i < n; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
//选择排序-排升序
//传入数组地址array,传入数据个数n(n>0)
void SelectSort(int* array, int n)
{
assert(array);
int mini = 0;
int maxi = n - 1;
int Mini = 0;
while (Mini < n / 2)
{
for (int i = Mini; i < n - Mini; i++)
{
if (array[i] < array[mini])
mini = i;
if (array[i] > array[maxi])
maxi = i;
}
Swap(&array[Mini], &array[mini]);
if (0 == maxi)
{
maxi = mini;
}
Swap(&array[n - Mini - 1], &array[maxi]);
Mini++;
}
}
// 堆排序-建大堆
//传入一个数组的地址array,数组中的元素个数n
//数组中需要排序的下标root
void AdjustDown(int* array, int n, int root)
{
assert(array);
int parent = root;
int child = parent * 2 + 1;
//当child下标在数组中时,进入循环
while (child < n)
{
//选出左右孩子中的大的那个
if (child + 1 < n && array[child + 1] > array[child])
{
child = child + 1;
}
//比较孩子和父亲,选出大的向上走
if (array[child] > array[parent])
{
Swap(&array[child], &array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
//出循环,说明已经调整好了
}
//堆排序-排升序
//传入一个数组的地址arrray,传入该数组中的元素个数n
void HeapSort(int* array, int n)
{
assert(array);
//向下调整建堆
for (int root = (n - 1 - 1) / 2; root >= 0; root--)
//n-1为最后一个数据的下标,(n-1-1)/2为其父元素的下标
{
AdjustDown(array, n, root);
}
//建好堆后进行排序
int end = n - 1;//end为最后一个数据的下标
while (end > 0)
{
Swap(&array[0], &array[end]);//交换最后一个数据与第一个数据
//选出最大数据来
AdjustDown(array, end, 0);
end--;
}
}
3.交换排序
核心思想:遍历数组,不断比较前后两个数据的大小,若前数据大于后数据,交换两个数据
//0.打印数组
//传入一个数组地址array,传入该数组中元素个数n
void PrintArray(int* array, int n)
{
assert(array);
for (int i = 0; i < n; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
//0.交换数据
//传入需交换数据的两个地址pa和pb
void Swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
//1.冒泡排序-升序(从小到大)
//传入一个数组地址array,传入该数组中元素个数n
void BubbleSort(int* array, int n)
{
assert(array);
for (int j = 1; j < n; j++)
{
//调整一个数据
for (int i = 0; i < n - j; i++)
//这里的的n-j可以视为从下标为0到下标为n-j中找到那个数据
{
if (array[i] > array[i + 1])
{
Swap(&array[i], &array[i + 1]);
}
}
}
}
//2.快速排序-递归实现
//2.1快速排序hoare版本
//传入一个数组的地址array,传入数组中起始(左边)位置的下标值left,传入数组中终止(右边)位置的下标值right
//传出排序完成后的下标值
int PartSort1(int* array, int left, int right)
{
assert(array);
//标记左边第一个数据的位置
int key = left;
//正常进行排序-left<right
while (left < right)
{
//右边先开始向左走,找小于等于key位置的数据
while (left < right && array[right] > array[key])
{
right--;
}
//出循环,说明此时1.array[right] <= array[key]
//2.right与left重合
//左边再往右走,找大于等于key位置的数据
while (left < right && array[left] <= array[key])
{
left++;
}
//出循环,说明此时1.array[left] > array[key]
//2.right与left重合
Swap(&array[left], &array[right]);
}
//出循环,说明此时right与left重合
//交换key位置和right位置中的数据,将right位置给key
Swap(&array[right], &array[key]);
return right;
}
//2.2快速排序挖坑法
//传入一个数组地址,传入需排序的起始坐标和末尾坐标
//排序一趟的做法,排好该数组的第一个数据应该在的位置
int PartSort2(int* array, int left, int right)
{
assert(array);
int key = array[left];//记录第一个数据的大小
int pit = left;//记录坑位的位置
//正常情况,左下标小于等于右下标,开始进行排序
while (left < right)
{
//从最右边的数据开始找起,当右边的数据大于key时,向左找一个数据
while (left < right && array[right] >= key)
{
right--;
}
//出循环,说明此时1.left与right重合
//2.array[right] < key
array[pit] = array[right];//把小于key的值赋值给坑位
pit = right;//更新坑位
while (left < right && array[left] <= key)
{
left++;
}
array[pit] = array[left];
pit = left;
}
//出循环,说明左下标此时等于右下标,且array[left]为一个小于key的值
array[pit] = key;
return pit;
}
//2.3快速排序前后指针法
//传入一个数组地址,传入需排序的起始坐标和末尾坐标
//排序一趟的做法,排好该数组的第一个数据应该在的位置
int PartSort3(int* array, int left, int right)
{
assert(array);
int key = array[left];
//定义两个走的不同的整数下标
int slow = left;
int fast = left;
while (fast < right)
{
fast++;//快下标走一步
if (array[fast] < key)
{
slow++;
Swap(&array[slow], &array[fast]);
}
}
Swap(&array[left], &array[slow]);
return slow;
}
//传入一个数组的地址array,传入数组起始位置的下标begin,传入数组终止位置的下标end
//快速排序,排好array数组中从begin到end下标中的数据
void QuickSort(int* array, int begin, int end)
{
assert(array);
//非正常情况(停止情况),当左下标大于等于右下标时,此时无需排序,退出排序
if (begin >= end)
{
return;
}
//正常情况(运行情况),当左下标小于右下标时,排好第一个元素在数组中的位置
int key = PartSort1(array, begin, end);
//进行递归,排好array数组中,剩余两侧的位置
QuickSort(array, begin, key - 1);
QuickSort(array, key + 1, end);
}