参考
冒泡排序
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
void BubbleSort_orderly(vector<int>& v) {
int len = v.size();
bool orderly = false;
for (int i = 0; i < len - 1 && !orderly; ++i) {
orderly = true;
for (int j = 0; j < len - 1 - i; ++j) { //每for一次,得到的v[len-1-i]是最大的
//例如v[len - 1]是所有元素最大的
//v[len-1-1]是所有元素中倒数第二大的
if (v[j] > v[j + 1]) { // 从小到大 //从来没有进去下面指令代表已经有序了
orderly = false; // 发生交换则仍非有序
swap(v[j], v[j + 1]);
}
}
}
}
选择排序
在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
void SelectionSort(vector<int>& v) {
int min, len = v.size();
for (int i = 0; i < len - 1; ++i) {
min = i; //第一个作为最小的
for (int j = i + 1; j < len; ++j) { //每for一次找到一个最小值
if (v[j] < v[min]) { // 标记最小的
min = j; //更新
}
}
if (i != min) // 交换到前面
swap(v[i], v[min]);
}
}
插入排序
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2~5
void InsertSort(vector<int>& v)
{
int len = v.size();
for (int i = 1; i < len; ++i) {
int temp = v[i];
for(int j = i - 1; j >= 0; --j) //循环已排序好的元素
{
if(v[j] > temp) //将排序后的序列的每一个值与当前值比较
{
v[j + 1] = v[j];
v[j] = temp;
}
else
break;
}
}
}
希尔排序
希尔排序这个名字,来源于它的发明者希尔,也称作“缩小增量排序”,是插入排序的一种更高效的改进版本。我们知道,插入排序对于大规模的乱序数组的时候效率是比较慢的,因为它每次只能将数据移动一位,希尔排序为了加快插入的速度,让数据移动的时候可以实现跳跃移动,节省了一部分的时间开支。
希尔排序:每一轮按照事先决定的间隔进行插入排序,间隔会依次缩小,最后一次一定要是1。
可能你会问为什么区间要以 gap = gap*3 + 1 去计算,其实最优的区间计算方法是没有答案的,这是一个长期未解决的问题,不过差不多都会取在二分之一到三分之一附近。
例:6 4 3 7 5 1 2
gap = gap*3 + 1 gap = gap*2 + 1(也可以)
while (h < length / 3) { while (h < length / 2) {
h = 3 * h + 1; h = 2 * h + 1;
} }
得h = 4, 得h = 3
又h = h/3; h = h/2;
得h=1
又j -= h
如果array[j] < array[j - h],将会从右到左继续比较
template<typename T>
void shell_sort(T array[], int length) {
int h = 1;
while (h < length / 3) {
h = 3 * h + 1;
}
//①或者直接用
if (length % 2 == 0)
h = length / 2;
else
h = length / 2 + 1;
while (h >= 1) {
for (int i = h; i < length; i++) {
for (int j = i; j >= h && array[j] < array[j - h]; j -= h) { //从后往前遍历
std::swap(array[j], array[j - h]);
}
}
h = h / 3;
//②或 h = h / 2;
}
}
归并排序
归并字面上的意思是合并,归并算法的核心思想是分治法,就是将一个数组一刀切两半,递归切,直到切成单个元素,然后重新组装合并,单个元素合并成小数组,两个小数组合并成大数组,直到最终合并完成,排序完毕。
template<typename T>
void merge_sort_recursive(T arr[], T reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;//len >> 1等同于除以2
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2) //小值往前排
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)//如果第一个while是后面的为小值,那么前面的值往后面排
reg[k++] = arr[start1++];
while (start2 <= end2) //如果第一个while是前面的为小值,那么排序不变
reg[k++] = arr[start2++];
for (k = start; k <= end; k++) //将排序好的数组赋值给源数组
arr[k] = reg[k];
}
快速排序
1.选取第一个数为基准
2.将比基准小的数交换到前面,比基准大的数交换到后面
3.对左右区间重复第二步,直到各区间只有一个数
void QuickSort(vector<int>& v, int low, int high) { //从小到大排序
if (low >= high) // 结束标志
return;
int first = low; // 低位下标
int last = high; // 高位下标
int key = v[first]; // 设第一个为基准
while (first < last)//确定key的下标
{
// 将比第一个小的移到前面(如果是从大到小排序,v[last] <= key)
while (first < last && v[last] >= key) //循环直到找到一个比key小的值
last--;
if (first < last)
v[first++] = v[last]; //因为first的位置已经更新,所以得++
// 将比第一个大的移到后面(如果是从大到小排序,v[last] >= key)
while (first < last && v[first] <= key) //循环直到找到一个比key大的值
first++;
if (first < last)
v[last--] = v[first]; 因为last的位置已经更新,所以得--
} //每一次循环的目的是为了得到一个固定下标的值
// 基准置位
v[first] = key; //得到的值不会再改变,下标已经固定死了
// 前半递归
QuickSort(v, low, first - 1); //first之前的递归排序
// 后半递归
QuickSort(v, first + 1, high); //first之后的递归排序
}
堆排序
堆排序顾名思义,是利用堆这种数据结构来进行排序的算法。堆是一种优先队列,两种实现,最大堆和最小堆,由于我们这里排序按升序排,所以就直接以最大堆来说吧。
我们完全可以把堆(以下全都默认为最大堆)看成一棵完全二叉树,但是位于堆顶的元素总是整棵树的最大值,每个子节点的值都比父节点小,由于堆要时刻保持这样的规则特性,所以一旦堆里面的数据发生变化,我们必须对堆重新进行一次构建。
既然堆顶元素永远都是整棵树中的最大值,那么我们将数据构建成堆后,只需要从堆顶取元素不就好了吗? 第一次取的元素,是否取的就是最大值?取完后把堆重新构建一下,然后再取堆顶的元素,是否取的就是第二大的值? 反复的取,取出来的数据也就是有序的数据。