冒泡排序
从头开始比较每一对相邻的元素,如果第1个比第2个元素大,就交换它们的位置。执行完一轮之后,最末尾的那个元素就是最大的元素。
int main() {
using namespace std;
static const int MAX = 6;
int m[MAX] = {5,6,4,3,2,1};
// 外层确定交换的结束位置
for (int end = MAX; end > 0; end--) {
// 里层确定交换
for (int i = 1; i < end; ++i) {
if (m[i] < m[i - 1]) {
int temp = m[i];
m[i] = m[i-1];
m[i-1] = temp;
}
}
}
for (int j = 0; j < MAX; ++j) {
cout << m[j] << "_";
}
return 0;
}
优化一: 如果已经有序,提前结束排序
#include <iostream>
void swap(int &a, int &b);
int main() {
using namespace std;
static const int MAX = 6;
int m[MAX] = {5,6,4,3,2,1};
// 外层确定交换的结束位置
for (int end = MAX; end > 0; end--) {
// 默认有序
bool isSorted = true;
// 里层确定交换
for (int i = 1; i < end; ++i) {
if (m[i] < m[i - 1]) {
swap(m[i-1],m[i]);
// 如果进行了交换,证明序列无序
isSorted = false;
}
}
// 如果已经有序,结束排序
if (isSorted) break;
}
for (int j = 0; j < MAX; ++j) {
cout << m[j] << "_";
}
return 0;
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
优化二: 如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数
#include <iostream>
void swap(int &a, int &b);
int main() {
using namespace std;
static const int MAX = 6;
int m[MAX] = {5,6,4,3,2,1};
// 外层确定交换的结束位置
for (int end = MAX; end > 0; end--) {
// 如果初始完全有序的情况,要直接结束掉遍历,因此要确保只进行一次外层遍历,
// 即end--之后,小于等于0了
int sortEnd = 0;
// 里层确定交换
for (int i = 1; i < end; ++i) {
if (m[i] < m[i - 1]) {
swap(m[i-1],m[i]);
// 确定部分有序的位置
sortEnd = i+1;
}
}
end = sortEnd;
}
for (int j = 0; j < MAX; ++j) {
cout << m[j] << "_";
}
return 0;
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
选择排序
从序列中找出最大的元素,然后与最末尾的元素交换位置
选择排序的交换次数要远远少于冒泡排序 ,平均性能优于冒泡排序
#include <iostream>
void swap(int &a, int &b);
int main() {
using namespace std;
static const int MAX = 6;
int m[MAX] = {5,6,4,3,2,1};
for (int end = MAX; end > 0; end--) {
int maxIndex = 0;
for (int i = 1; i < end; ++i) {
if(m[i] > m[maxIndex]) {
maxIndex = i;
}
}
swap(m[maxIndex],m[end-1]);
}
for (int j = 0; j < MAX; ++j) {
cout << m[j] << "_";
}
return 0;
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
插入排序
- 在执行过程中,插入排序会将序列分为2部分
- 头部是已经排好序的,尾部是待排序的
- 从头开始扫描每一个元素
- 每当扫描到一个元素,就将它插入到头部合适的位置,使得头部数据依然保持有序
#include <iostream>
void swap(int &a, int &b);
int main() {
using namespace std;
static const int MAX = 6;
int m[MAX] = {5,6,4,3,2,1};
for (int begin = 1; begin < MAX; ++begin) {
int cur = begin;
while (cur > 0 && m[cur] < m[cur-1]) {
swap(m[cur],m[cur-1]);
cur--;
}
}
for (int j = 0; j < MAX; ++j) {
cout << m[j] << "_";
}
return 0;
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
优化一:将交换转变为挪动
int main() {
using namespace std;
int m[] = {5,6,4,3,2,1,7};
int count = sizeof(m) / sizeof(int);
for (int begin = 1; begin < count; ++begin) {
int cur = begin, temp = m[cur];
while (cur > 0 && temp < m[cur-1]) {
m[cur] = m[cur-1];
cur--;
}
m[cur] = temp;
}
for (int j = 0; j < count; ++j) {
cout << m[j] << "_";
}
return 0;
}
优化二:二分搜索查找插入位置
二分搜索插入位置,注意数组设置为左闭右开,便于数据使用
#include <iostream>
int searchIndex(int m[], int count, int v);
int main() {
using namespace std;
int m[] = {5,6,4,3,2,1,7};
int count = sizeof(m) / sizeof(int);
for (int begin = 1; begin < count; ++begin) {
// 备份当前数值
int temp = m[begin];
// 通过二分查找,找到要插入的位置
int cur = searchIndex(m,begin,temp);
// 移动其余元素
for (int i = begin; i > cur ; i--) {
m[i] = m[i-1];
}
// 将元素插入到对应的位置
m[cur] = temp;
}
for (int j = 0; j < count; ++j) {
cout << m[j] << "_";
}
return 0;
}
// 数组采用左闭右开策略,即[0,count)
int searchIndex(int m[], int count, int v) {
int begin = 0;
int end = count;
// 当begin == end 时候,就是查找到了对应的位置
while (begin < end) {
int mid = (begin + end) >> 1;
if (v < m[mid]) {
end = mid;
} else {
begin = mid + 1;
}
}
return begin;
}
归并排序
- 1945年由冯诺依曼首次提出。
- 执行流程
- 不断的将当前序列平均分割成2个子序列,直到不能再分割(序列中只剩1个元素)
- 不断地将2个子序列合并成1个有序序列,知道最终只剩下1个有序序列
#include <iostream>
void mergeSort(int array[],int leftArray[],int begin, int end);
void merge(int array[],int leftArray[], int begin, int end, int mid);
int main() {
using namespace std;
int m[] = {5,6,4,3,2,1,7};
int count = sizeof(m) / sizeof(int);
int leftArray[count >> 1];
mergeSort(m,leftArray,0,count);
for (int j = 0; j < count; ++j) {
cout << m[j] << "_";
}
return 0;
}
void mergeSort(int array[],int leftArray[],int begin, int end) {
// 如果元素数量只有一个,结束递归
if (end - begin < 2) return;
int mid = (begin + end) >> 1;
// 利用递归,进行拆分操作,递归的结束标志是只剩下唯一的元素
mergeSort(array,leftArray,begin,mid);
mergeSort(array,leftArray,mid,end);
// 进行元素的合并操作
merge(array,leftArray,begin,end,mid);
}
void merge(int array[],int leftArray[], int begin, int end, int mid) {
// li是左半部分的起点,le是左半部分的终点,因为左闭右开的性质,mid-begin也就是左半部分的元素个数,同时也是左闭右开情况下,左半部分的终点
int li = 0, le = mid - begin;
int ri = mid, re = end;
int ai = begin;
// 1. 备份左半部分数据到新的数组
for (int i = li; i < le; ++i) {
leftArray[i] = array[i+begin];
}
// 2. 判断左半部分是否完全完成,如果左半部分完成归并,意味着整个归并结束
while (li < le) {
// 如果左边元素较大,将右半部分元素放入数组
if (ri < re && array[ri] < leftArray[li] ) {
array[ai++] = array[ri++];
} else {
// 如果右办部分结束了,直接将左半部分的元素导入数组
array[ai++] = leftArray[li++];
}
}
}
快速排序
1960年由爵士东尼·霍尔提出
执行流程
- 从序列中选择一个轴点元素(pivot)
- 假设每次选择0位置的元素为轴点元素
- 利用pivot将序列分割成2个子序列
- 将小于pivot的元素放在pivot前面(左侧)
- 将大于pivot的元素放在pivot后面(右侧)
- 等于pivot的元素放哪边都可以
- 对子序列进行上述两步操作
- 直到不能再分割(子序列只剩下1个元素)
快速排序的本质:逐渐将每一个元素都转换成轴点元素
#include <iostream>
void sort(int* array,int begin, int end);
int pivotAtIndex(int* array,int begin,int end);
void swap(int &a, int &b);
int main() {
using namespace std;
int m[] = {5,6,4,1,3,7};
int num = sizeof(m) / sizeof(int);
sort(m,0,num);
for (int j = 0; j < num; ++j) {
cout << m[j] << "_";
}
return 0;
}
void sort(int* array,int begin, int end) {
// 如果只有一个元素结束
if(end-begin < 2) return;
// 确定轴点元素位置O(n)
int pivot = pivotAtIndex(array,begin,end);
sort(array,begin,pivot);
sort(array,pivot + 1,end);
}
/**
* 构造出 [begin, end) 范围的轴点元素
* @return 轴点元素的最终位置
* */
int pivotAtIndex(int* array,int begin,int end) {
// 随机选择一个元素跟begin位置进行交换 (降低出现最坏排序的概率)
swap(array[begin],array[begin + arc4random() % (end - begin)]);
// 备份轴点(begin)元素的位置
int pivot = array[begin];
// 左闭右开空间
end--;
while (begin < end) {
while (begin < end) {
// 从end开始进行遍历,因为begin的元素已经备份,避免了元素的浪费
if (pivot < array[end]) {
end--;
} else {
// begin增加后转到从begin进行遍历
array[begin++] = array[end];
break;
}
}
while (begin < end) {
if (pivot > array[begin]) {
begin ++;
} else {
array[end--] = array[begin];
break;
}
}
}
array[begin] = pivot;
return begin;
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
希尔排序
#include <iostream>
void swap(int &a, int &b);
void logArray(int *array,int count);
void sort(int *array,int arrayCount,int step);
int main() {
using namespace std;
int m[] = {5,6,4,1,3,7};
int num = sizeof(m) / sizeof(int);
int tempNumber = num;
while (tempNumber >>= 1 > 0) {
sort(m,num,tempNumber);
}
logArray(m,num);
return 0;
}
void sort(int *array,int arrayCount,int step) {
for (int col = 0; col < step; ++col) {
for (int begin = col + step; begin < arrayCount; begin += step) {
int cur = begin;
while (cur > col && array[cur] < array[cur - step]) {
swap(array[cur],array[cur-step]);
cur -= step;
}
}
}
}
void logArray(int *array,int num) {
for (int j = 0; j < num; ++j) {
std::cout << array[j] << "_";
}
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}