一、排序的稳定性
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的,否则称为不稳定的。
二、插入排序
1.直接插入排序
void InsertSort(int* a, int n)
{
for (int i = 0; i < n - 1; i++)
{
int end = i;
int temp = a[i + 1];
while (end>=0)
{
if (a[end]>temp)
{
a[end+1]=a[end];
end--;
}
else
break;
}
a[end + 1] = temp;
}
}
2.希尔排序
void ShellSort(int* a, int n)
{
// gap > 1 预排序
// gap == 1 直接插入排序
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
//gap = gap / 2;
// 间隔为gap的多组并排
for (int i = 0; i < n - gap; ++i)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
三、选择排序
1.直接选择排序
这里单趟找出了最大和最小,分别放到数组最外两个位置,注意begin和max重合的场景
void SelectSort(int* a, int n)
{
int begin = 0, end = n - 1;
while (begin < end)
{
// 选出 [begin, end]
int mini = begin, maxi = begin;
for (int i = begin; i <= end; ++i)
{
if (a[i] > a[maxi])
maxi = i;
if (a[i] < a[mini])
mini = i;
}
Swap(&a[begin], &a[mini]);
// 如果begin和maxi重叠,需要修正一下新的maxi的位置
if (begin == maxi)
{
maxi = mini;
}
Swap(&a[end], &a[maxi]);
++begin;
--end;
}
}
2.堆排序
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
if (child+1 < n && a[child+1] > a[child])
{
++child;
}
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
// 堆排序 O(N*logN)
void HeapSort(int* a, int n)
{
// 建大堆 O(N)
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
// O(N*logN)
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
四、交换排序
1.冒泡排序
void BubbleSort(int* a, int n)
{
for (int end = n; end > 0; --end)
{
int exchange = 0;
for (int i = 1; i < end; ++i)
{
if (a[i - 1] > a[i])
{
Swap(&a[i - 1], &a[i]);
exchange = 1;
}
}
if (exchange == 0)
{
break;
}
}
}
2.快速排序
//三数取中
int GetMidIndex(int* a, int left, int right)
{
//int mid = (left + right) / 2;
int mid = left + (right - left) / 2;
if (a[left] < a[mid])
{
if (a[mid] < a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return right;
}
else
{
return left;
}
}
else //(a[left] > a[mid])
{
if (a[mid] > a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return left;
}
else
{
return right;
}
}
}
//挖坑法
int PartSort(int* a, int left, int right)
{
int midi = GetMidIndex(a, left, right);
Swap(&a[left], &a[midi]);
int key = a[left];
int hole = left;
while (left < right)
{
// 右边去找小,填到左边的坑里面
while (left < right && a[right] >= key)
{
--right;
}
// 把右边找的小的,填到左边的坑,自己形成新的坑
a[hole] = a[right];
hole = right;
// 左边去找大,填到右边的坑里面
while (left < right && a[left] <= key)
{
++left;
}
// 把左边找的大的,填到右边的坑,自己形成新的坑
a[hole] = a[left];
hole = left;
}
a[hole] = key;
return hole;
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
int keyi = PartSort(a, left, right);
//PrintArray(a+left,right-left + 1);
// [left, keyi-1] keyi [keyi+1, right]
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
三数取中是为了避免有序的情况,快速排序复杂度分析
快速排序非递归
思路:
在栈中存区间下标
void QuickSortNonR(int* a, int left, int right) {
stack<int> st;
st.push(left);
st.push(right);
while (!st.empty()) {
int end = st.top();
st.pop();
int begin = st.top();
st.pop();
int div = PartSort(a, begin, end);
if (begin < div - 1) {
st.push(begin);
st.push(div - 1);
}
if (div + 1 < end) {
st.push(div+1);
st.push(end);
}
}
}
五、归并排序
递归版本:
void _MergeSort(int* a, int left, int right, int* tmp)
{
if (left >= right)
return;
int mid = (right + left) / 2;
// [left, mid][mid+1, right]
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid+1, right, tmp);
// 归并
int begin1 = left, end1 = mid;
int begin2 = mid+1, end2 = right;
int index = left;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while(begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
// 归并后的结果,拷贝回到原数组
for (int i = left; i <= right; ++i)
{
a[i] = tmp[i];
}
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
非递归:
难点:如何避免越界
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
int groupNum = 1;
while (groupNum < n)
{
for (int i = 0; i < n; i += 2 * groupNum)
{
// [begin1][end1] [begin2,end2]
// 归并
int begin1 = i, end1 = i + groupNum - 1;
int begin2 = i + groupNum, end2 = i + groupNum * 2 - 1;
int index = begin1;
// 数组数据个数,并不一定是按整数倍,所以划分的分组可能越界或者不存在
// 1、[begin2,end2] 不存在, 修正为一个不存在的区间
if (begin2 >= n)
{
begin2 = n + 1;
end2 = n;
}
// 2、end1越界,修正一下
if (end1 >= n)
{
end1 = n - 1;
}
// 3、end2越界,需要修正后归并
if (end2 >= n)
{
end2 = n - 1;
}
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
}
// 拷贝回原数组
for (int i = 0; i < n; ++i)
{
a[i] = tmp[i];
}
groupNum *= 2;
//PrintArray(a, n);
}
free(tmp);
}
六、非比较排序
计数排序
void CountSort(int* a, int n)
{
int min = a[0], max = a[0];
for (int i = 1; i < n; ++i)
{
if (a[i] < min)
{
min = a[i];
}
if (a[i] > max)
{
max = a[i];
}
}
int range = max - min + 1;
int* count = (int*)calloc(range, sizeof(int));
// 统计次数
for (int i = 0; i < n; ++i)
{
count[a[i] - min]++;
}
// 根据count数组排序
int i = 0;
for (int j = 0; j < range; ++j)
{
while (count[j]--)
{
a[i++] = j + min;
}
}
}