直接插入排序(稳定)
我们认为在一组数中,可以分为三个部分,有序区,无序区和待排序区,例如下图:
蓝色部分是有序区,黄色部分表示即将要排序的,灰色是无序区,那么我们怎么在有序部分找到黄色应该插入的位置呢?
1.从后往前找。可以从蓝色部分的后面往前找,分别和黄色比较,找到相应的位置
2.二分方式找。
代码如下:
void InsertSort(int array[], int size)
{
int key;
int i, j;
for (i = 1; i < size; i++)
{
key = array[i];
for (j = i - 1; j >= 0; j--)
{
if (key >= array[j])
{
break;
}
else {
array[j + 1] = array[j];
}
}
array[j+1] = key;
}
}
时间复杂度: 最好/最坏 O(n)/O(n^2)
空间复杂度: O(1)
希尔排序(不稳定)
希尔排序也是插排的一种,在排序之前会先做预排序,例如下面这组数据
希尔排序会先分组进行插叙,例如上面的数组相同颜色的圈代表一组,先在每一组进行排序,然后不断的重复,那么这就需要考虑分组策略了:
1.分组间隔越大,排序次数越少
2.分组间隔越小,结果越有序
一般情况下:我们会把一组数据分为几组呢?一般情况gap=size/3+1;
代码:
void _InsertSort(int array[], int size, int gap)
{
for (int g = 0; g < gap; g++)
{
int key;
int i, j;
for (i = gap + g; i < size; i+=gap) {
key = array[i];
for (j = i - gap; j >= 0; j -= gap) {
if (key >= array[j]) {
break;
}
else {
array[j + gap] = array[j];
}
}
array[j + gap] = key;
}
}
}
void ShellSort(int array[], int size)
{
int gap = size;
while (1) {
gap = gap / 3 + 1;
_InsertSort(array, size, gap);
if (gap == 1)
{
break;
}
}
}
时间复杂度: 最好/平均/最差 O(n)/O(n^(1.2~1.3) ) /O(n^2)
空间复杂度: O(1)
选择排序(不稳定)
每次在序列中选出最大(最小),交换到最后,简而置之,然后循环
代码:
void Swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
void SelectSort(int array[], int size)
{
for (int i = size; i >1; i--)
{
int max = 0;
for (int j = 1; j < i; j++)
{
if (array[j] > array[max]) {
max = j;
}
}
Swap(array + max, array + i - 1);
}
}
上面这种是每次选出最大的或者最小放好位置,当然,我们也可以,每次既选出最大的,也选出最小的同时放好位置。
代码:
void Swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
void SelectSort1(int array[], int size)
{
int left = 0;
int right = size - 1;
while (left < right)
{
int min = left;
int max = left;
for (int j = left + 1; j <= right; j++)
{
if (array[j] > array[max])
{
max = j;
}
if (array[j] < array[min])
{
min = j;
}
}
Swap(array + left, array + min);
if (max == left)
{
max = min;
}
Swap(array + right, array + max);
left++;
right--;
}
}
时间复杂度:O(n^2)
空间复杂度:O(1)
堆排序(不稳定)
堆排序的思想其实就是,每次找到最大值与最后一个元素交换,然后向下调整。排序的过程其实很简单,主要在于创建大堆和堆的向下调整。
void AdjustDown(int array[], int size, int root)
{
int left = 2 * root + 1;
int right = 2 * root + 2;
if (left >= size)
{
return;
}
int max = left;
if (right<size&&array[right]>array[left])
{
max = right;
}
if (array[root] >= array[max])
{
return;
}
Swap(array + root, array + max);
AdjustDown(array, size, max);
}
void CreateHeap(int array[], int size)
{ //创建大堆
//从最后一个非叶子结点到0
//不断的进行向下调整
for (int i = size / 2 - 1; i >= 0; i--)
{
AdjustDown(array, size, i);
}
}
void HeapSort(int array[], int size)
{
CreateHeap(array, size);
for (int i = 0; i < size; i++)
{
Swap(array, array + (size - i - 1));
AdjustDown(array,size-i-1,0);
}
}
时间复杂度:O(N*logN)
空间复杂度:O(1)
冒泡排序(稳定)
冒泡排序是最常见的排序,思想也很简单,就是挨个交换,不断循环。
void BubbleSort(int array[], int size)
{
for (int i = 0; i < size - 1; i++)
{
int flag = 1;
for (int j = 0; j < size - 1 - i; j++)
{
if (array[j] > array[j + 1])
{
Swap(array + j, array + j + 1);
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
时间复杂度: 最好/平均/最坏 O(n) O(n^2) O(n^2)
空间复杂度: O(1)
快速排序(不稳定)
基本思想:分治算法
1.找一个基准值
1>找边上的
2.比基准值小的都放到基准值的左边
比基准值大的都放到基准值的右边
3.终止条件
1>小区间有序 区间长度==1
2>小区间没有数 区间长度<=0
将区间按照基准值划分为左右两半部分的常见方式有:
方法一:hoare法
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基
本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将
待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序
列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元
素都排列在相应位置上为止。
int Partition(int array[], int left, int right)
{
int begin = left;
int end = right;
while (begin < end)
{
while (begin < end&&array[begin] <= array[right])
{
begin++;
}
while (begin < end&&array[end] >= array[right])
{
end--;
}
Swap(array + begin, array + end);
}
Swap(array + begin, array + right);
return begin;
}
void _QuickSort(int array[],int left,int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition(array, left, right);
_QuickSort(array, left, div - 1);
_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
_QuickSort(array, 0, size - 1);
}
挖坑法
挖坑法其实就是提前将基准值存起来,这样基准值的位置其实就是一个坑,可以被别的元素填。挖坑法的具体过程就是将最后一个元素作为基准值,然后begin从前遍历,遇到比基准值大的就去填基准值的坑,end从后往前遍历,遇到比基准值小的再去填begin的坑。
int Partition_02(int array[], int left, int right)
{
int begin = left;
int end = right;
int priot = array[right];
while (begin < end)
{
while (begin < end&&array[begin] <= priot)
{
begin++;
}
array[end] = array[begin];
while (begin < end&&array[end] >= priot)
{
end--;
}
array[begin] = array[end];
}
array[begin] = priot;
return begin;
}
void _QuickSort(int array[],int left,int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition_01(array, left, right);
_QuickSort(array, left, div - 1);
_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
_QuickSort(array, 0, size - 1);
}
快慢指针法
快慢指针也叫前后指针,当然并不是一个从前一个从后遍历,而是一前一后遍历,两个指针都从头开始向后遍历,快的指针每次都向后移动,只有当快的指针所指的值小于基准值时,快慢指针所指的值进行交换,并且慢指针向后移动,否则,慢指针不动。
int Partition_03(int array[], int left, int right)
{
int fast = left;
int slow = left;
while (fast<right)
{
if (array[fast] < array[right])
{
Swap(array + fast, array + slow);
slow++;
fast++;
}
else {
fast++;
}
}
Swap(array + right, array + slow);
return slow;
}
void _QuickSort(int array[],int left,int right)
{
if (left == right)
{
return;
}
if (left > right)
{
return;
}
int div = Partition_03(array, left, right);
_QuickSort(array, left, div - 1);
_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
_QuickSort(array, 0, size - 1);
}
时间复杂度:O(nlogn)
空间复杂度:O(1)
归并排序
基本思想:
将待排序的元素序列分成两个长度相等的子序列,对每一个子序列排序,
然后将他们合并成一个序列。如下图:
递归方法
void Merge(int array[], int left,int mid, int right,int a[])
{//合并函数
int left_i = left;
int right_i = mid + 1;
int k = left;
while (left_i <= mid && right_i <= right) {
if (array[left_i] <=array[right_i])
{
a[k++] = array[left_i++];
}
else {
a[k++] = array[right_i++];
}
}
while (left_i <= mid)
{
a[k++] = array[left_i++];
}
while (right_i <= right)
{
a[k++] = array[right_i++];
}
for (int i = left; i <= right; i++)
{
array[i] = a[i];
}
}
void _MergeSort(int array[], int left, int right,int a[])
{//分组函数
if (left == right)
{
return;//区间长度为1
}
if (left > right)
{
return ;//区间长度为0
}
int mid = left + (right - left) / 2;
_MergeSort(array, left, mid, a);
_MergeSort(array, mid + 1, right, a);
Merge(array, left, mid, right, a);
}
void MergeSort(int array[], int size)
{//接口函数
int*a = (int *)malloc(sizeof(int)*size);//用于存放合并后的数组
_MergeSort(array, 0, size - 1, a);
free(a);
}
非递归方法
void MergeSortLoop(int array[],int size ) {
int*extra = (int *)malloc(sizeof(int)*size);//用于存放合并后的数组
for (int i = 1; i < size; i *= 2)
{
for (int j = 0; j < size; j = j + 2 * i)
{
int left = j;
int mid = j + i;
int right = mid + i;
if (mid >= size) {
continue;
}
if (right > size) {
right = size;
}
Merge(array, left,mid-1, right-1, extra);
}
}
free(extra);
}
这里代码不唯一,看个人区间分配,我用的是左闭右闭的区间。
时间复杂度:O(n)
空间复杂度:O(n)