插入排序
直接插入排序
void insertsort1(int* a, int n)
{
assert(a);
int i = 0;
int end = 0;
int tmp = 0;
for (i = 0; i < n-1; ++i)
{
end = i;
tmp = a[end + 1];
while (end >= 0)
{
if (a[end] <= tmp)
{
break;
}
else
{
a[end + 1] = a[end];
--end;
}
a[end+1] = tmp;
}
}
}
希尔排序
void shellsort1(int* a, int n)
{
assert(a);
int gap = n;
int end = 0;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; ++i)
{
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;
}
}
}
选择排序
选择排序
在给出的未排序的一群数中找出最小的或最大的放在首位置或末位置,然后再在剩余的数里继续按上步骤寻找。如:1 3 5 2 4
void selectsort1(int* a, int n)//最好情况已有序交换0次,最差情况无序交换n-1次;总共比较n*(n-1)次
{
assert(a);
int i = 0;
int j = 0;
int min = 0;
for (i = 0; i < n - 1; ++i)//循环n次
{
min = i;
for (j = i + 1; j < n; ++j)
{
if (a[min]>a[j])
{
min = j;
}
}
if (min != i)
{
swap(a[i], a[min]);
}
}
}
可以对选择排序进行优化,一边找大一边找小:
void selectsort2(int* a, int n)//优化,循环n/2次
{
assert(a);
int i = 0, j = 0;
int min = 0, max = 0;
int begin = 0, end = n - 1;
while (begin < end)
{
min = begin;
max = begin;
for (int i = begin; i <= end; ++i)
{
if (a[min]>a[i])
{
min = i;
}
if (a[max] < a[i])
{
max = i;
}
}
//begin找大,end找小
swap(a[max], a[end]);
if (end == min)
{
min = max;
}
swap(a[min], a[begin]);
begin++;
end--;
}
}
堆排序
升序建大堆,降序建小堆
void createdown(int* a, int n, int root)
{
int parent = root;
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 heapsort1(int* a, int n)
{
assert(a);
for (int i = (n - 2) / 2; i >= 0; --i)
{
createdown(a, n, i);
}
int end = n-1;
while (end > 0)
{
swap(a[0], a[end]);
createdown(a, end, 0);
end--;
}
}
交换排序
冒泡排序
每一趟将相邻的两个数进行比较,按照升序或降序的要求将需要的大数或小数放在后面。如1 3 5 2 4
void bubblesort1(int* a, int n)
{
int i = 0, j = 0;
for (i = 0; i < n - 1; ++i)//排的趟数
{
for (j = i; j < n - i - 1; ++j)
{
if (a[j]>a[j + 1])
{
swap(a[j], a[j + 1]);
}
}
}
}
由上述例子可以看出,第二趟结束之后就已经是有序的了,不需要再排了,所以可给个标记进行优化,减少循环的次数,提高效率。
优化:
void bubblesort1(int* a, int n)
{
int i = 0, j = 0;
int flag = 0;
for (i = 0; i < n - 1; ++i)//排的趟数
{
for (j = 0; j < n - i - 1; ++j)
{
if (a[j]>a[j + 1])
{
swap(a[j], a[j + 1]);
flag = 1;
}
}
if (flag = 0)
{
break;
}
}
}
因为循环要花费的时间比比较要多,冒泡排序经过的循环明显比选择排序多,所以冒泡排序的速度没有选择排序快。
快速排序
(递归)
快速排序就是先找出一个关键字,将比它大的当一边,比它小的放在另一边,然后再根据此方法排左右两边的数。如1 3 5 2 4
int _quicksort(int* a, int l, int r)
{
assert(a);
int begin = l, end = r;
int key = a[end];
while (begin < end)
{
if (begin<end&&a[begin] <= key)//begin找大
{
++begin;
}
a[end] = a[begin];
if (begin<end&&a[end] >= key)
{
--end;
}
a[begin] = a[end];
}
a[begin] = key;
return begin;
}
void quicksort2(int* a, int n)//挖坑法
{
assert(a);
int left = 0;
int right = n - 1;
if (left >= right)
{
return;
}
int div = _quicksort(a, left, right);
quicksort(a, left,div-1);
quicksort(a, div + 1, right);
}
(非递归:利用栈)
void quicksortNor(int* a, int n)//非递归利用栈
{
assert(a);
std::stack<int> s;
int left = 0;
int right = n - 1;
if (left < right)
{
s.push(left);
s.push(right);
}
while (!s.empty())
{
right = s.top();
s.pop();
left = s.top();
s.pop();
int div = partsort1(a, left, right);
if (left < div - 1)
{
s.push(left);
s.push(div - 1);
}
if (div + 1 < right)
{
s.push(div + 1);
s.push(right);
}
}
}
归并排序:
//归并排序
void _MergeSort(int* a, int left, int right, int* tmp)
{
assert(a);
if (left >= right)//若数组只有一个元素,则直接返回
{
return;
}
//划分区间[left,mid] [mid+1,right]
int mid = left + ((right - left) >> 1);
//让两个自子区间有序
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
//归并
int index = left;
int begin1 = left, end1 = mid, begin2 = mid + 1, end2 = right;
while (begin1 <= end1&&begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else//(a[begin1]>a[begin2])
{
tmp[index++] = a[begin2++];
}
}
//判断哪个子序列没走完,就将子序列没走完的直接拷到tmp后
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//拷贝tmp到原数组a
index = left;
while (index <= right)
{
a[index] = tmp[index];
++index;
}
}
void MergeSort(int* a, int n)
{
assert(a);
int* tmp = new int[n];//创建一个数组变量临时存放数组
_MergeSort(a, 0, n - 1, tmp);
delete[] tmp;//释放tmp防止抛异常
}
void TestMergeSort()
{
int a[] = { 4, 5, 2, 1, 3 };
MergeSort(a, sizeof(a) / sizeof(int));
PrintfSort(a, sizeof(a) / sizeof(int));
}
稳定性
在一组数中有多个相等的数,如Ri=Rj,且Ri在Rj的前面;经过排序后,Ri的相对位置还是在Rj的前面,则说明是稳定的。
排序算法的比较
|
类别 |
方法
|
时间复杂度 |
空间复杂度 |
稳定性 |
||
|
最好情况 |
最差情况 |
一般情况 |
||||
|
插入排序 |
直接插入排序 |
O(n) |
O(n^2) |
O(n^2) |
O(1) |
稳定 |
|
希尔排序 |
O(n) |
O(n^2) |
O(n^1.3) |
O(1) |
不稳定 |
|
|
选择排序 |
选择排序 |
O(n^2) |
O(n^2) |
O(n^2) |
O(1) |
不稳定 |
|
堆排序 |
O(n*lgn) |
O(n*lgn) |
O(n*lgn) |
O(1) |
不稳定 |
|
|
交换排序 |
冒泡排序 |
O(n) |
O(n^2) |
O(n^2) |
O(1) |
稳定 |
|
快速排序 |
O(n*lgn) |
O(n^2) |
O(n*lgn) |
O(lgn) |
不稳定 |
|
|
归并排序 |
归并排序 |
O(n*lgn) |
O(n*lgn) |
O(n*lgn) |
O(n) |
稳定 |