冒泡排序
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
以 int arr[] = { 10, 1, 2, 7, 9, 3, 4, 5, 8, 6}; 举例(升序)
步骤:
- 将 arr[0] 与arr[1]比较,因为arr[1]>arr[1],进行换值,arr[0]=1,arr[1]=10
- 将 arr[1] 与arr[2]比较,因为arr[1]>arr[2],进行换值,arr[1]=2,arr[2]=10
- 将 arr[2] 与arr[3]比较,因为arr[3]>arr[2],进行换值,arr[2]=7,arr[3]=10
- …
- 进行上述换值9次,arr[9]=10, 即可完成全部排序
int main()
{
//升序
int arr[] = { 10, 1, 2, 7, 9, 3, 4, 5, 8, 6 };
//需要循环 sizeof(arr) / sizeof(int) - 1 次才能完成任何复杂的排序
for (int i = 0; i < sizeof(arr) / sizeof(int) - 1; i++)
{
//遍历
for (int j = 0; j < sizeof(arr) / sizeof(int) - 1 - i; j++)
{
//把相邻的数都进行比较
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
return 0;
}
快速排序
快速排序是是hoare1962年年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元数序列中某元数作为基准值按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
//假设一个函数 按升序对arr数组的区间(left,right)进行排序
QuickSort_2(int *arr,int left,int right)
{
...
}
void QuickSort_1(int *arr,int left,int right)
{
if (left >= right)
{
return;
}
//按照基准值对arr数组的区间进行划分
int key = QuickSort_2(arr,leght,right)
//划分后区间成为[ left,key-1]和[key+1,right]两部分
//递归
QuickSort_1(arr,left,key-1)
//递归
QuickSort_1(arr,key+1, right)
}
上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。
将区间按照基准值划分为左右两半部分的常见方式有:
hoare版本
L找比arr[key]大的值,R找比arr[key]小的值
步骤(升序):
- 先让R先走,走到arr[R]=5
- 再让L走,走到arr[L]=7
- 交换arr[R] 和 arr[L]的值
- …
- 重复上述步骤,直到 R=L
- 最后,key=R(key=L),进行递归
代码演示:
//进行排序的函数
int PartQuickSort_2(int* arr, int left, int right)
{
//定义基准值
int key = left;
while(left < right)
{
//右取小 没有=会死循环 没 left<right会在此while中left>right
while(arr[right] >= arr[key]&&left<right)
{
--right;
}
//左取大
while(arr[left] <= arr[key]&&left<right)
{
++left;
}
//交换left 和 right值
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
//交换key 和 left(right)值
int tmp = arr[key];
arr[key] = arr[right];
arr[right] = tmp;
return right;
}
//进行递归的函数
void PartQuickSort_1(int* arr, int left, int right)
{
//终止条件
if (left >= right)
{
return;
}
//先找一次 key
int key = PartQuickSort_2( arr, left, right);
//整个数的集合为 [left,k-1] key [k+1, right]
//[left,k-1] 部分进行排序
PartQuickSort_1(arr, left, key - 1);
//[k+1, right] 部分进行排序
PartQuickSort_1(arr, key + 1, right);
}
int main()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int)-1);
return 0;
}
挖坑法
L找比 key 大的值,R找比 key 小的值
步骤(升序):
- key = arr[L], arr[0]为坑
- 先让R先走,走到arr[R]=5,让 arr[L]=5, arr[R]为坑
- 再让L走,走到arr[L]=7,让 arr[R]=7,arr[L]为坑
- …
- 重复上述步骤,直到 R=L
- 最后,key=R(key=L),进行递归
代码演示:
//进行排序的函数
int PartQuickSort_2(int* arr, int left, int right)
{
//定义基准值
int key = arr[left];
int hole = left;
while (left < right)
{
//右取小 没有=会死循环 没 left<right会在此while中left>right
while(arr[right] >= key && left < right)
{
--right;
}
//值放坑中,换坑
arr[hole] = arr[right];
hole = right;
//左取大
while(arr[left] <= key && left < right)
{
++left;
}
//值放坑中,换坑
arr[hole] = arr[left];
hole = left;
}
arr[hole] = key;
return hole;
}
//递归函数
void PartQuickSort_1(int* arr, int left, int right)
{
//终止条件
if (left >= right)
{
return;
}
//先找一次 key
int key = PartQuickSort_2(arr, left, right);
//整个数的集合为 [left,k-1] key [k+1, right]
// [left,k-1] 部分进行排序
PartQuickSort_1(arr, left, key - 1);
// [k+1, right] 部分进行排序
PartQuickSort_1(arr, key + 1, right);
}
int main()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int) - 1);
return 0;
}
前后指针版本
代码演示:
//进行排序的函数
int PartQuickSort_2(int* arr, int left, int right)
{
//定义基准值
int key = left;
int pre = left+1;
int cur = left;
while (pre <= right)
{
//pre 取小
if(arr[pre] < arr[key])
{
cur++;
int tmp = arr[pre];
arr[pre] = arr[cur];
arr[cur] = tmp;
}
++pre;
}
//交换key 和 cur值
int tmp = arr[key];
arr[key] = arr[cur];
arr[cur] = tmp;
return cur;
}
//递归函数
void PartQuickSort_1(int* arr, int left, int right)
{
//终止条件
if (left >= right)
{
return;
}
//先找一次 key
int cur = PartQuickSort_2(arr, left, right);
//整个数的集合为 [left,cur-1] cur [cur+1, right]
//[left,cur-1] 部分进行排序
PartQuickSort_1(arr, left, cur - 1);
//[cur+1, right] 部分进行排序
PartQuickSort_1(arr, cur + 1, right);
}
int main()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int) - 1);
return 0;
}
非递归版本
基本思想和递归思想一样。
这里需要用C语言实现栈,不会用C语言实现的可以参考 C语言实现栈
//排序函数
int PartQuickSort_2(int* arr, int left, int right)
{
//定义基准值
int key = left;
int pre = left + 1;
int cur = left;
while (pre <= right)
{
//pre 取小
if (arr[pre] < arr[key])
{
cur++;
int tmp = arr[pre];
arr[pre] = arr[cur];
arr[cur] = tmp;
}
++pre;
}
//交换key 和 cur值
int tmp = arr[key];
arr[key] = arr[cur];
arr[cur] = tmp;
return cur;
}
void QuickSortNoR(int* arr, int left, int right, int n)
{
//栈的初始化
SL st;
SLInit(&st);
//入栈,先进后出
SLPush(&st, left);
SLPush(&st, right);
while (!SLEmpty(&st))
{
//取左值
int left = SLTop(&st);
SLPop(&st);
//取右值
int right = SLTop(&st);
SLPop(&st);
//取key,分区间
int key = PartQuickSort_2(arr, left, right);
// [left, key-1] key [key+1,right]
//进栈
if (key + 1 < right)
{
SLPush(&st, right);
SLPush(&st, key + 1);
}
if (left < key - 1)
{
SLPush(&st, key - 1);
SLPush(&st, left);
}
}
//销栈
SLDestroy(&st);
}
快速排序优化
可以想象一下,( 排升序 )当最大值在最左边时是最复杂的情况,排序时间变长。我们不知道大中小各各的坐标,这时可以取一个相对的中间值放在最左边。
代码演示(以hoare版本为例进行改进):
void Swap(int* arr1, int *arr2)
{
int tmp = *arr1;
*arr1 = *arr2;
*arr2 = tmp;
}
void FindMid(int* arr, int left, int right)
{
//假设arr[mid]相对中间值
int mid = (left + right) / 2;
//找出正确的相对中间值
if (arr[left] < arr[mid])
{
if (arr[mid] < arr[right])
{
return;
}
else if (arr[right] < arr[left])
{
Swap(&arr[left], &arr[mid]);
return;
}
else
{
Swap(&arr[right], &arr[mid]);
return;
}
}
else //arr[mid]<=arr[left]
{
if (arr[right] > arr[left])
{
Swap(&arr[mid], &arr[left]);
return;
}
else if (arr[right] < arr[mid])
{
return;
}
else
{
Swap(&arr[right], &arr[mid]);
}
}
}
int PartQuickSort_2(int* arr, int left, int right)
{
//定义基准值
int key = left;
while(left < right)
{
//右取小 没有=会死循环 没 left<right会在此while中left>right
while(arr[right] >= arr[key]&&left<right)
{
--right;
}
//左取大
while(arr[left] <= arr[key]&&left<right)
{
++left;
}
//交换left 和 right值
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
//交换key 和 left(right)值
int tmp = arr[key];
arr[key] = arr[right];
arr[right] = tmp;
return right;
}
//进行递归的函数
void PartQuickSort_1(int* arr, int left, int right)
{
FindMid( arr, left, right);
//终止条件
if (left >= right)
{
return;
}
//先找一次 key
int key = PartQuickSort_2( arr, left, right);
//整个数的集合为 [left,k-1] key [k+1, right]
//[left,k-1] 部分进行排序
PartQuickSort_1(arr, left, key - 1);
//[k+1, right] 部分进行排序
PartQuickSort_1(arr, key + 1, right);
}
int main()
{
int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int)-1);
return 0;
}