要被排序的元素中,可能会有两个或多个数据相等的情况,如果原数组a[i]=a[j]且i
内排序是指排序过程中,要被排序的元素全部被存放在内存里。
外排序是由于待排序数据过多,则把数据放在磁盘中,排序数据须通过磁盘和内存的数据传输后,才能进入算法运行。
我的理解里,内排序就是不占用额外内存,外排序就是得占用额外的内存。
1)两两比较相邻元素a[i]和a[i+1],如果a[i]>a[i+1],则交换a[i]和a[i+1]的位置
2)对剩下的n-1个元素,再两两进行比较,按同样规则交换它们的位置,经过n-2次边角,将最大值交换到a(n-1)的位置
3)继续上述操作,经过n-1的“冒泡处理”,每次进行n-i次比较
看过动图之后,你会发现,较大的元素会经交换而一层层上浮至顶端,就跟小鱼吐泡泡一点点冒出水面一样。
由于冒泡排序是相邻两个元素进行比较,所以如果两个元素相等,是不会交换的,所以说冒泡排序比较稳定。
时间复杂度:O(N^2),适用于待排序数据数量较小的情况,这样算法写起来也简单鸭~
/*time complexity O(N^2)*/
void bubble_Sort(int a[], int n) {
for (int i = n - 1; i > 0; i--) {
for (int j = 0; j < i; j++)
if (a[j] > a[j + 1])
swap(a[j], a[j + 1]);
}
}
我还记得最开始学C语言,讲到冒泡排序的方法,我花了很长的时间才理解它的原理(可能因为我是笨蛋哈哈哈),但是选择排序的原理就看起来很简单。
第一次从所有待排序的数据中找出最大的元素,放在起始位置,之后再继续从剩余元素中寻找最大元素,重复此操作,直至数据全部被排序。
/*select the largest element from the unsorted array,
and then put it at the starting position,then find the
largest element from the rest numbers, repeat is continously*/
/*time complexity O(N^2)*/
void Select_sort(int a[], int n) {
int min = 0, minindex;
/*the outer loop ,to traverse*/
for (int i = n - 1; i >= 1; i--) {
minindex = i;
/*the inner loop,to find the largest element to swap*/
for (int j = 0; j < i; j++)
if (a[j] <= a[minindex])
minindex = j;
swap(a[i], a[minindex]);
}
}
由于是从整个序列中选最大最小,之后和两端元素交换,所以说,选择排序是一种不稳定的算法。
时间复杂度:O(N^2),同样适用于元素个数较少的序列排序。
将待排序序列分为两部分:有序部分和无序部分,依次拿出一个无需部分中的元素放进有序部分,直至插完所有元素。
/*time complexity O(N^2)*/
void Insertion_Sort(int a[], int n) {
/*divide the array into two parts,the sorted part and the unsorted part*/
/*the outer loop,to traverse*/
for (int i = 1; i < n; i++) {
for (int j = i - 1; j >= 0 && a[j] > a[j + 1]; j--)
/*the inner loop,to traverse from the first unsorted one,
and then find the element that is smaller ,then swap*/
swap(a[j], a[j + 1]);
}
}
可以看出插入排序是必须元素不相等时才能移动插入,所以插入排序是稳定的(直接插入排序算法是稳定的,这个算法还有很多改进的版本,其他的稳定性俺就不知道了).
时间复杂度:O(N^2),适用于元素个数不多且元素基本有序。
排序方法至此跳出O(n^2)的时间复杂度,逐渐向更高速的方向发展。
希尔排序可以说是插入排序的升级版,它又被称为缩小增量排序。
原理:如果待排序序列有n个元素,则先取一个increment(<=n),当作间隔数量,将n个元素划分成(n-increment)个序列,分别对这几个序列进行插入排序,之后不断缩小increment,重复组内元素插入排序操作,最终实现increment缩小至1.
那么这个Increment的值该如何选取呢?至今为止没人找到一种防止四海皆准的增量序列,但大量研究表明,
这个序列效果不错,但必须保证最后的increment=1.
其时间复杂度为 O(N^(3/2)),终于突破了O(N ^2)
由于数据是跳跃式的移动,所以希尔排序也是以一种不稳定的排序算法。
迄今由于不认识increment,又去补了一下英语
/*time complexity O(N^(3/2))*/
void Shell_sort(int a[], int n) {
int increment = n;
int temp;
while (increment > 1) {
increment = increment / 3 + 1;
for (int i = increment; i < n; i++) {
if (a[i - increment] > a[i]) {
temp = a[i];
int j = i - increment;
while (j >= 0 && a[j] > temp) {
a[j + increment] = a[j];
j -= increment;
}
a[j + increment] = temp;
}
}
}
}
原理:归并,顾名思义,就是合并的意思。在这个算法之中,假定待排序数据有n个元素,则把这n个元素看作n个有序的子序列,每个子有序序列长度为1,然后双双合并,得到长度为2的有序子序列,重复上述操作,直至得到长度为n的有序序列。
(这玩意儿的代码俺不会写,粘了大佬的哈哈哈)
#include
#include
using namespace std;
void merge(int *data, int start, int end, int *result) {
int left_length = (end - start + 1) / 2 + 1;
int left_index = start;
int right_index = start + left_length;
int result_index = start;
while (left_index < start + left_length &&
right_index < end + 1) // store data into new array
{
if (data[left_index] <= data[right_index])
result[result_index++] = data[left_index++];
else
result[result_index++] = data[right_index++];
}
while (left_index < start + left_length)
result[result_index++] = data[left_index++];
while (right_index < end + 1)
result[result_index++] = data[right_index++];
}
void merge_sort(int *data, int start, int end, int *result) {
if (1 == end - start) // last only two elements
{
if (data[start] > data[end]) {
int temp = data[start];
data[start] = data[end];
data[end] = temp;
}
return;
} else if (end == start)
return; // last one element then there is no need to sort;
else {
// continue to divide the interval
merge_sort(data, start, (end - start + 1) / 2 + start, result);
merge_sort(data, (end - start + 1) / 2 + start + 1, end, result);
// start to merge sorted data
merge(data, start, end, result);
for (int i = start; i <= end; ++i) {
data[i] = result[i];
}
}
}
// example
int main() {
int data[] = {1, 8, 7, 9, 6, 5, 3, 4, 2, 10};
int length = 10;
int result[length];
cout << "before sorted:" << '\n';
for (int i = 0; i < length; i++)
cout << data[i] << ' ';
cout << '\n' << "after sorted:" << '\n';
merge_sort(data, 0, length - 1, result);
for (int i = 0; i < length; i++)
cout << result[i] << ' ';
return 0;
}
堆排序还在整理中~
快排篇幅比较大,另开了一篇,专门整理这个
快速排序 QuickSort
上述算法大多用到了swap这个函数,我之前又在这个函数上犯过很多错误,所以总结一下这个函数的写法。
#include
#include
using namespace std;
/*传指针法*/
void swap1(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
/*传引用法*/
void swap2(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
/*位运算法*/
void swap3(int *a, int *b) {
*a = *a ^ *b;
*b = *b ^ *a;
*a = *a ^ *b;
}
int main() {
int a = 5, b = 6;
swap3(&a, &b);
cout << a << " " << b << endl;
}
关于第三种位运算法,详见位运算整理(C/C++)实现