冒泡排序
动图演示:
void BubbleSort(int* arr, int n)//升序
{
for (int i = 0; i < n-1; ++i)//n个数需要冒泡冒n-1趟
{
int flag = 0;//每趟开始之前flag置零
for (int j = 0; j < n-1-i; j++)
{
if (arr[j]>arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 1;
}
}
if (flag == 0)//数组有序,无需再冒泡。
break;
}
}
测试段
#include"sort.h"
void Print(int*a,int n)
{
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}
void TestSort()
{
int arr[] = { 98, 88, 22, 999, 456, 12, 33, 77, 123 };
int n = sizeof(arr) / sizeof(int);
BubbleSort(arr, n);
Print(arr, n);
}
int main()
{
TestSort();
system("pause");
return 0;
}
快速排序
动图演示:
方法一——前找大,后找小:
对于 98, 88, 22, 999, 456, 12, 33, 77, 123这个数组,我们可以定义两个变量begin从头开始找比key大的数、end从未开始找比key小的数。
当arr[begin]比key大,arr[end]比key小时,交换arr[begin]和arr[end]。
这样下来相遇的位置以前的数都比key小,相遇点之后的数都比 key要大。
int Partion1(int*arr,int left,int right)//前begin找大,后end找小
{
if (right - left>2)
MidThree(arr, left, right);
int key = arr[right];
int begin = left, end = right;
while (begin < end)
{
while (begin < end&&arr[begin] <= key)
++begin;
while (begin<end&&arr[end]>=key)
--end;
//arr[begin]一定是比key大的数
//arr[end]一定是比key小的数
if (begin<end)
Swap(&arr[begin], &arr[end]);//小数放前边,大数放后边
}
//到此begin==end,而且arr[begin]>key;
Swap(&arr[begin], &arr[right]);
return begin;
}
方法二——前后指针法:cur找小与++prev换
0——prev位置 比key值小
prev——cur 比key值大
cur 找比key小的数,找到之后与++prev交换
起始位置,prev在cur前一个位置,cur遇到不比key小的数继续往后走,停下位置数值一定比key值要小,prev!=cur时候,prev位置处于cur走过的路上,肯定是不小于key的数,交换cur与prev的值。将大数换到后面,小数换到前边。
此时prev位置的数比key小,如此保证prev以前的数值都比key值要小,当cur再次找到比key小的数时候,++prev位置肯定是比key值大的数,再交换。
int Partion2(int* arr, int left, int right)//前后指针法
{
if (right-left>2)
MidThree(arr, left, right);
int key = arr[right];
int cur = left, prev = left - 1;
while (cur < right)
{
if (arr[cur] < key&&++prev != cur)//cur找小与prev换
Swap(&arr[cur], &arr[prev]);
++cur;
}
Swap(&arr[++prev], &arr[right]);
return prev;
}
方法三——挖坑法:
因为确定基数key之后,key这个位置就可以加以利用,我始终保持这个key所在的坑位是在数组最右端,找到一个大数与key位置交换后,数组长度减一,仍然再把坑位换回最右端,以保证每次换大数到最右端。
int Partion3(int*arr,int left,int right)
{
if (right - left>2)
MidThree(arr, left, right);
int key = arr[right];
//找大来填坑
int start = left;
int hole = right;//三数取中法是置坑位在最右端
while (start < hole)
{
while (start < hole&&arr[start] <= key)
++start;//start找到比key大的数
if (start < hole)
{
Swap(&arr[start], &arr[hole]);//大数入坑
--hole;
}
if (start<hole)
Swap(&arr[start], &arr[hole]);//仍然将坑位置于最右端
}
return hole;
}
是不是有点像搜索二叉树的特性,key相当于根节点。
这个key很关键,他最好能是这个数组的中位数。
这里优化的方法称之为三数取中法,key取下标为left、right、或者数组中间值mid三数中处于中间的的那个数。
//快速排序
void MidThree(int*arr,int left,int right)//三数取中位数放在数组最有端
{
int mid = (right + left)>>1;
if (arr[left] > arr[right])
Swap(&arr[left], &arr[right]);//先确保arr[left]<arr[right]
if (arr[mid] < arr[left])//arr[left]是中位数
Swap(&arr[left], &arr[right]);
else if (arr[mid] < arr[right])//arr[mid]是中位数
Swap(&arr[mid], &arr[right]);
}
void QuickSort(int* arr,int left,int right)//快速排序
{
if (left < right)
{
int div = Partion3(arr, left, right);
//这里可以分别调用Partion1、partion2、partion3测试
QuickSort(arr, 0, div-1);
QuickSort(arr,div, right);
}
}
重难点:
快排非递归:
需用用到的栈的相关实现在这里:
栈和队列的实现:
https://blog.csdn.net/vickers_xiaowei/article/details/80016979
//非递归快排
void QuickSort(int* arr, int left, int right)
{
Stack s;
StackInit(&s, 20);
if (right - left > 2)
MidThree(arr, left, right);
int key = arr[right];
int keyindex = right;
int begin = left, end = right;
while (begin < end || !StackEmpty(&s))
{
int mright = end;//这里记录下在进行移动的数组的end和begin
//在产生相遇点,产生新的取件后,判断新的两段区间是否还需要入栈时候用
int mleft = begin;
while (begin < end)
{
while (begin < end&&arr[begin] <= key)
++begin;
while (begin < end&&arr[end] >= key)
--end;
//arr[begin]一定是比key大的数
//arr[end]一定是比key小的数
if (begin<end)
Swap(&arr[begin], &arr[end]);//小数放前边,大数放后边
//到此begin==end,而且arr[begin]>key;
}
if (begin != keyindex)
Swap(&arr[begin], &arr[keyindex]);
if (begin-mleft>1)//前半部分入栈条件是前半部分不少于1
{
StackPush(&s, 0);
StackPush(&s, begin - 1);
}
if (keyindex - begin>1)//后半部分入栈的条件是后半部分不少于1
{
StackPush(&s, begin);
StackPush(&s, mright);
}
if (!StackEmpty(&s))//如果栈不为空取两个数,作为新的区间
{
end = StackTop(&s);
StackPop(&s);
begin = StackTop(&s);
StackPop(&s);
}
if (end - begin > 2)
MidThree(arr, begin, end);
key = arr[end];//更新关键码key
keyindex = end;
}
}
有关其它排序:
排序一——直接插入排序and希尔排序
https://blog.csdn.net/vickers_xiaowei/article/details/80613544
排序二——冒泡排序、快速排序三种递归实现and快排非递归
https://blog.csdn.net/vickers_xiaowei/article/details/80646873
排序三——选择排序and堆排序
https://blog.csdn.net/vickers_xiaowei/article/details/80646990
排序四——归并排序和排序结:
https://blog.csdn.net/vickers_xiaowei/article/details/80646992