冒泡排序
冒泡排序算是第一个接触的排序有很强的教学意义
冒泡思想是遍历i-1趟,每一趟又要遍历n-i-1遍,每一遍会找出当前最大数向后交换
//冒泡排序
void BubbSort(int* a, int n)
{
for (int i = 0; i < n; i++)
{
bool flag = false;
for (int j =1;j<n-i;j++)
{
if (a[j] < a[j -1])
{
Swap(&a[j], &a[j - 1]);
flag = true;
}
}
if (!flag)
return;
}
}
时间复杂度
最好情况是O(n)
最坏情况是O(n^2)
平均情况是O(n^2)
空间复杂度
O(1)
选择排序
选择排序思想在元素中找出最大和最小元素然后交换到左右两边,交换完毕后左右两边缩小,继续前面步骤,直至左右两边会和循环结束
//选择排序
void SeleSort(int* a, int n)
{
int left = 0, right =n-1;
while (left < right)
{
int min = left, max = left;
for (int i = left; i <= right; i++)
{
if (a[i] < a[min])
min = i;
else if (a[i] > a[max])
max = i;
}
Swap(&a[min], &a[left]);
if (max == left)
{
max = min;
}
Swap(&a[max], &a[right]);
left++;
right--;
}
}
时间复杂度
最好情况是 O(n^2)
最坏情况是 O(n^2)
平均情况是 O(n^2)
空间复杂度
O(1)
插入排序
插入排序思想把第一个元素当做有序,插入元素与前一个元素比较,插入元素找到插入位置,原元素向后移动
//插入排序
void InsertSort(int* a, int n)
{
for (int i = 1; i < n; i++)
{
int end = i-1;
int tmp = a[i];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + 1] = a[end];
end--;
}
else
break;
}
a[end + 1] = tmp;
}
}
时间复杂度
平均情况是O(n^2)
但是元素越有序的情况下插入排序的时间消耗会随之减少,所以它会比冒泡选择排序等更优
空间复杂度
o(1)
希尔排序
希尔排序是插入排序的优化版 每gap个元素分为一组每组进行预排序,gap越小数据越有序,当gap为1时排序完成
//希尔排序
void ShellSort(int* a, int n)
{
//以一组为gap个元素进行分组
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0; i < n-gap; i++)
{
int end = i ;
int tmp = a[end+gap];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + gap] = a[end];
end-=gap;
}
else
break;
}
a[end + gap] = tmp;
}
}
}
时间复杂度
最好情况是O(n^1.3)
最坏情况是O(n^2)
希尔排序的时间复杂度是和gap有关的但是gap的计算方法有很多并且计算起来很困难
空间复杂度
O(1)
堆排序
堆是二叉树的顺序结构,在使用前要先建堆,升序建大堆,降序建小堆
void HeaPjustDown(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)
{
HeaPjustDown(a, n, i);
}
int j = n - 1;
while (j > 0)
{
Swap(&a[j], &a[0]);
HeaPjustDown(a, j, 0);
j--;
}
}
时间复杂度
最好情况是O(NlogN)
最坏情况是O(NlogN)
空间复杂度
O(1)
快速排序
hoare版本
任意取元素作基准值,左右两边元素对基准值进行比较直到左右相遇,基准值的位置找到,以基准值分割数据,分而治之
//hoare
int hoare(int* a, int begin, int end)
{
int left = begin;
int right = end;
int keyi = begin;
//单趟
while (left < right)
{
//右边先走找小
while (left<right&&a[right] >= a[keyi])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
return left;
}
挖坑法
是hoare版本的迭代,更容易理解,将基准值“取出”,比基准值大或小的元素填上它的位置直至左右下标相遇,基准值回填找到位置,同样分割数据,分而治之
//挖坑法
int hole(int* a, int begin, int end)
{
int keyi = a[begin];
int sub = begin;
int left = begin;
int right = end;
while (left < right)
{
while(left<right&&a[right]>=keyi)
{
right--;
}
a[sub] = a[right];
sub = right;
while (left < right && a[left] <= keyi)
{
left++;
}
a[sub] = a[left];
sub = left;
}
a[left] = keyi;
return left;
}
前后指针
//前后指针法
int partSort3(int* a, int begin, int end)
{
int cur = begin + 1;
int prev = begin;
int keyi = begin;
while (cur <= end)
{
if (a[cur] <= a[keyi]&&++prev!=cur)
{
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[prev], &a[keyi]);
return prev;
}
三数取中优化
可通过三数取中进一步优化快排防止数据比较有序的特殊情况下排序速度的降低
//三数取中
int midindex(int* a, int left, int right)
{
int mid =(rand()%(right-left));
if (a[left] < a[mid])
{
if (a[mid] < a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return right;
}
else
{
return left;
}
}
else
{
if (a[mid] > a[right])
{
return mid;
}
else if (a[left] > a[right])
{
return right;
}
else
{
return left;
}
}
}
快排非递归
快排非递归需要用到栈后进先出的规则
//栈
//初始化
void STinit(ST* pst)
{
assert(pst);
pst->a =NULL;
pst->top = 0;
pst->capacity = 0;
}
//销毁
void STDestroy(ST* pst)
{
assert(pst);
assert(pst->a);
free(pst->a);
pst->capacity=pst->top = 0;
}
//入栈
void STPush(ST* pst,STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapa=pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* newend = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapa);
if (newend == NULL)
{
perror("realloc fail");
return;
}
pst->capacity = newcapa;
pst->a = newend;
}
pst->a[pst->top++] = x;
}
//出栈
void STPop(ST* pst)
{
assert(pst);
assert(pst->a);
pst->top--;
}
//判空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
//返回长度
int STSize(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->top;
}
//返回栈顶元素
STDataType STtop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
//快排非递归
void QuickSortNonR(int* a, int begin, int end)
{
ST St;
STinit(&St);
STPush(&St, end);
STPush(&St, begin);
while (!STEmpty(&St))
{
int left = STtop(&St);
STPop(&St);
int right = STtop(&St);
STPop(&St);
int key = partSort3(a, left, right);
if (key + 1 < right)
{
STPush(&St, right);
STPush(&St, key + 1);
}
if (left < key-1)
{
STPush(&St, key-1);
STPush(&St, left);
}
}
STDestroy(&St);
}
时间复杂度
最好情况是O(NlogN)
最坏情况是O(N^2)
空间复杂度
最好情况是O(NlogN)
最坏情况是O(N^2)
归并排序
归并排序采用分治法
//归并排序
void _MergeSort(int* a, int* tmp, int begin, int end)
{
if (begin == end)
{
return;
}
//小区间优化减少递归次数
if (end - begin + 1 < 10)
{
insertsort(a + begin, end - begin + 1);
}
//分割
int mid = (begin + end) / 2;
//后序
_MergeSort(a, tmp, begin, mid);
_MergeSort(a, tmp, mid + 1, end);
//左区间
int begin1 = begin, end1 = mid;
//右区间
int begin2 = mid + 1, end2 = end;
//数组下标
int i = begin;
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 + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
归并非递归
//归并排序非递归
void MergeShortNonR(int* a, int* tmp, int begin, int n)
{
//循环实现
//每组归并的个数
int gap = 1;
//下标
int j = 0;
while (gap < n)
{
//i是一组一组的跳跃
for (int i = 0; i < n; i += 2 * gap)
{
//分割每组数据
//左区间
int begin1 = i, end1 = i + gap - 1;
//右区间
int begin2 = i + gap - 1, end2 = i + 2 * gap - 1;
//begin2越界,end2越界
if (end1 >= n || begin2 >= n)
{
break;
}
//修正
if (end2 >= n)
{
end2 = n - 1;
}
while (begin1 <= end1 && begin2 <= end2)
{
//左区间小于右区间
if (a[begin1] < a[begin2])
{
tmp[j++] = a[begin1++];
}
else
{
tmp[j++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[j++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[j++] = a[begin2++];
}
//归并一组,拷贝一组
memcpy(a+i, tmp+i, sizeof(int) * (end2-i+1));
}
gap *= 2;
}
}
时间复杂度
最好情况是O(NlogN)
最坏情况是O(NlogN)
空间复杂度
O(N)