冒泡法
int a[10]
n=10;
for (int i = 0; i < n-1; i++)
{
bool t = 1;
for (int j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j + 1]) swap(a[j], a[j + 1]);
t = 0;
}
if (t) break; //当有一次循环不用交换的时候 就说明都拍好了
}
时间复杂度 O(N2 );
等于时不交换 所以稳定;
选择排序
for (i=0; i < 9; i++)
{
for (j=i; j < 10; j++)
{
if (a[i] < a[j + 1]) swap(&a[i], &a[j + 1]);
}
}
时间复杂度 O(N2 );
选择交换时只根据两个数的大小来 与其他数没关系 所以不稳定;
插入法
for (int i = 1; i < n; i++)
{
int tmp = a[i];
int j = i - 1;
for (; j >= 0; j--)
{
if (tmp < a[j]) a[j + 1] = a[j];
else break;
}
a[j + 1] = tmp;
}
时间复杂度 O(N2 );
若选择插在相等的元素后面 稳定
若插在相等的元素前面 不稳定;
希尔排序
之前的三种算法是逐个寻找 消除逆序对的,希尔排序将扩大寻找范围。
int gap[10] = { 929,505,209,109,41,19,5,1,0 };
int k;
for ( k = 0; gap[k] > n; k++);
for (int D = gap[k]; D>0; D=gap[k++]) //确定间隔
{
int p, i;
//确定开始
for ( p = D; p < n; p++)
{
int tmp = a[p];
for (i = p; i >= D && tmp < a[i - D]; i -= D)
a[i] = a[i - D];
a[i] = tmp;
}
}
for (int k=0,gap =f[k]; gap > 0; k++,gap=f[k])
{
for (int i = 0; i <10; i++) //这里用的是选择法;
{
for (int j=i+gap; j < 10; j += gap)
if (arr[i] > arr[j]) swap(&arr[i], &arr[j]);
}
}
希尔的复杂度与间隔的取值有关
希尔排序不稳定;
归并排序
归并排序使用分治的思想 将数组分成最小的组 然后用类似一元多项式的加法合并起来;
有递归与非递归两种思想
递归:
void MergeSort(int* arr, int N)//外部接口
{
int* TmpA;
TmpA = (int*)malloc(sizeof(int) * N);
if (TmpA)
{
Msort(arr, TmpA, 0, N - 1);
free(TmpA);
}
}
void Merge(int *arr,int *TmpA,int L,int Rightend,int R)//合并
{
int num = Rightend - L + 1;//总数
int Leftend = R - 1;//左边结束点
int tmp = L;//合并后的指针
while (L<Leftend&&R<Rightend)
{
if (arr[L] <= arr[R]) //这里决定是否稳定 相等时Left优先则稳定
TmpA[tmp++] = arr[L++];
else
TmpA[tmp++] = arr[R++];
}
if (L == Leftend)
{
while (R!=Rightend)
{
TmpA[tmp++] = arr[R++];
}
}
else while (L!=Leftend)
{
TmpA[tmp++] = arr[L++];
}
for (int i = 0; i < num; i++)
arr[Rightend--] = TmpA[tmp--];
}
void Msort(int *arr,int *TmpA,int L,int RightEnd)//递归调用
{
int center;
if (L < RightEnd)
{
center = (L + RightEnd) / 2;
Msort(arr, TmpA, L, center);
Msort(arr, TmpA, center + 1, RightEnd);
Merge(arr, TmpA, L, RightEnd, center + 1);
}
}
非递归:
void Merge_Sort(int* arr, int N)
{
int* TmpA;
int length = 1;
TmpA = (int*)malloc(sizeof(int) * N);
if (TmpA)
{
while (length<N)
{
Msort_pass(arr, TmpA, N, length);
length *= 2;
Msort_pass(TmpA, arr, N, length);
length *= 2;
free(TmpA);
}
}
}
void Merge1(int* arr, int* TmpA, int L, int R, int Rightend)//与之前的merge不同 不需要把结果导入arr中
{
int num = Rightend - L + 1;//总数
int Leftend = R - 1;//左边结束点
int tmp = L;//合并后的指针
while (L < Leftend && R < Rightend)
{
if (arr[L] <= arr[R]) //这里决定是否稳定
TmpA[tmp++] = arr[L++];
else
TmpA[tmp++] = arr[R++];
}
if (L == Leftend)
{
while (R != Rightend)
{
TmpA[tmp++] = arr[R++];
}
}
else while (L != Leftend)
{
TmpA[tmp++] = arr[L++];
}
}
void Msort_pass(int *arr,int *TmpA,int N,int length) //length表示当前最小子列的长度
{
int i;
for (i = 0; i <= N - 2 * length; i += 2 * length)//三种情况 两个length正好合并
{
Merge1(arr, TmpA, i, i + length, i + 2 * length - 1);
}
if (i + length < N) //有一个完整的length和一个散的
Merge1(arr, TmpA, i, i + length, N - 1);
else for (; i < N; i++) TmpA[i] = arr[i]; //一个不完整的length
}
时间复杂度为O(NlogN)
空间复杂度为O(N) 既另需一个等长的tmpa来做中转;
快速排序
也是用分治的思想,先选出一个pivot,再把剩下的数分成大于和小于两个子集 再放回数组中,相当于直接找到了该元素最终的位置
当分到子序列里只有一个元素的时候不再分了,最好的情况是分的两边正好是大小排好的,时间复杂度相当于是O(NlogN),最坏的情况比如从头到尾按顺序来 而且每个都需要换 就变成了O(N2) ,所以如何选pivot成了关键。其中一种方法是取两头和中间的中位数来做pivot;
取pivot
int Median3(int *arr,int L,int R)
{
int center = (L + R) / 2;
if (arr[L] > arr[center]) swap(arr + L, arr + center);
if (arr[L] > arr[R]]) swap(arr + L, arr + R);
if (arr[center] > arr[R]) swap(arr + center, arr + R);
swap(arr + center, arr + R - 1);//将pivot放到倒数第二个位置;
return arr[R - 1];
}
递归排序
用递归进行排序 所以在进行小规模数据的时候比其他的比如插入冒泡等方法更加复杂 所以加入一个阈值 cutoff(一般为100)
void Qsort(int* arr, int L, int R)
{
int cutoff = 100
if (cutoff <= R - L + 1)
{
int pivot = Median3(arr, L, R);
int low = L;
int high = R - 1;
while (1)
{
while (arr[++low] < arr[pivot]);
while (arr[--high] > arr[pivot]);
if (low < high) //判断是否遍历结束
swap(arr + low, arr + high);
else break;
}
swap(arr + low, arr + pivot);// 停止的时候low前面全都是小于pivot的 high后面全是大于 并且是从小到大排序 且pivot本身是在后面的 所以交换的时候 跟low所指的 既大于pivot的值交换
Qsort(arr, L, arr + low - 1);
Qsort(arr, arr + low + 1, R);
}
else InsertionSort(*arr, R-L+1);
}
接口问题
void QuickSort(int* arr, int N)
{
Qsort(arr, 0, N - 1);
}
稳定性
当遇到相等的值的时候听不停下来呢?
当在全是相等的情况下
停:每次都交换 把pivot停在了中间 再进行递归 时间复杂度O(NlogN);
不停:每次pivot都停在了边缘 时间复杂度和冒泡法类似 O(N2);
所以选择停下来 也就是不稳定了
计数排序
当数组元素重复的较多的时候 可以建立一个count数字来记录每个元素出现的次数
稳定性
起初遍历原数组入count时 是按照先遇先入的顺序 所以为了保证稳定性 可以先输出排在后面的数字
也就是倒序输出
所以先把count内容改成自己数值的位置 也就是之前包括自己所有的累加和
输出时有两种方法 一种是从遍历一遍原数组 每一个元素都找到其相应的位置
另一种是从后往前输出
void CountSort(int* arr, int N)
{
int min = findmin(arr, N);//在数组中找到最小
int max = findmax(arr, N);//找到最大值
int* count = (int*)malloc(sizeof(int) * (max - min + 1));//分配计数一个数组;
for (int i = 0; i < max - min + 1; i++) count[i] = 0;//为数组赋初值
for (int i = 0; i < N; i++)
count[arr[i] - min]++;
for (int i = 0, sum = 0; i < max - min + 1; i++) //此时 count[i]中 i+min是原数组元素的值 count[i]是其值的最后位置+1
{
sum += count[i];
count[i] = sum;
}
for (int i = 0, sum = 0; i < max - min + 1; i++)
{
while (count[i])
{
arr[count[i] - 1] = i + min;
count[i]--;
}
}
}
这里时间复杂度是O(N+K) 空间复杂度是O(K);
表排序
当需要排序的内容很大时 如每个元素都是一本书 这个时候对书进行复制就很复杂 可以给数编上序号 然后再对序号进行排序 最后按照序号输出
比如table[10]中是无序的十本书
A[10]中存放按顺序的编号 比如 A[0]=table[3]
物理排序
堆排序
算法一
void Heap_Sort(int* arr, int N)
{
BuildHeap(arr); //建立最小堆
for (i = 0; i < N; i++)
TmpA[i] = DeleteMin(A); //建立一个临时数组来中转数据
for (i = 0; i < N; i++)
arr[i] = TmpA[i];
}
这种方法需要 额外的临时空间
方法二
void HeapSort(int* arr, int N)
{
BuildHeap(arr); //建立最大堆
for (i = N - 1; i >= 0; i - ) {
swap(arr, arr + i); //第一个值也就是最大值跟最后一个交换
PercDown(arr, 0, i); //再调整一下
}
}