参考摘自:https://www.cnblogs.com/onepixel/articles/7674659.html
整理一下剑指里提到的五大排序:冒泡、选择、快速、归并、堆,其中快速,归并和堆一定要熟。
一、冒泡排序
每次比较两个数,最大的数会默认排到最后面,最小的数慢慢地“浮”到最前来。
void bubbleSort(vector<int> &arr) {
int n = arr.size() - 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i; j++) {
if(arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return;
}
时间复杂度:
最好:O(n)
最差与平均:O(n^2)
空间复杂度:O(1)
优点:稳
缺点:慢
二、插入排序
把数插入到有序序列中去
void insertionSort(vector<int> &arr) {
int n = arr.size();
for (int i = 1; i < n; i++) {
int cur = arr[i];
int preIndex = i - 1;
while(preIndex >= 0 && cur < arr[preIndex]) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = cur;
}
}
时间复杂度:
最好:O(n)
最差与平均:O(n^2)
空间复杂度:O(1)
优点:快
缺点:比较次数不一定
三、归并排序
先使得子序列先有序,再使得子序列段间有序
void mergeSort(vector<int> &arr, int low, int high) {
if(low >= high) return;
int mid = low + (high - low) / 2;
mergeSort(arr, low, mid);
mergeSort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
void merge(vector<int> &arr, int low, int mid, int high) {
vector<int> temp(high - low + 1, 0);
int i = low, j = mid + 1, k = 0;
while(i <= mid && j <= high) {
if(arr[i] <= arr[j]) temp[k++] = arr[i++];
else temp[k++] = arr[j++];
}
while(i <= mid) {
temp[k++] = arr[i++];
}
while(j <= high) {
temp[k++] = arr[j++];
}
for (int i = low,k = 0; i <= high; i++, k++) {
arr[i] = temp[k];
}
}
时间复杂度:
最好:O(nlogn)
最差与平均:O(nlogn)
空间复杂度:O(n)
优点:不受输入数据的影响
缺点:需要额外的空间
四、快速排序
选择一个基准出来,比基准大的放在左边,比基准小的放在右边
void quickSort(vector<int> &arr, int l, int r) {
if(l < r) {
int temp = arr[l], i = l, j = r;
while(i < j) {
while(i < j && arr[j] >= temp) {
j--;
}
if(i < j)
arr[i++] = arr[j];
while(i < j && arr[i] < temp) {
i++;
}
if(i < j)
arr[j--] = arr[i];
}
arr[i] = temp;
quickSort(arr, l, i);
quickSort(arr, i + 1, r);
}
}
时间复杂度:
最好与平均:O(nlogn)
最差:O(n^2)
空间复杂度:O(nlogn)
优点:极快,数据移动少
缺点:不稳定
五、堆排序
完全二叉树:假设一个二叉树有n层,那么如果第1到n-1层的每个节点都达到最大的个数:2,且第n层的排列是从左往右依次排开的,那么就称其为完全二叉树
堆:本身就是一个完全二叉树,但是需要满足一定条件,当二叉树的每个节点都大于等于它的子节点的时候,称为大顶堆,当二叉树的每个节点都小于它的子节点的时候,称为小顶堆。
关键的性质:
将堆的内容填入一个一维数组,这样通过下标就能计算出每个结点的父子节点,编号顺序从0开始,从左往右,从上至下层次遍历。a[10] = {2,8,5,10,9,12,7,14,15,13}
若一个结点的下标为k,那么它的父结点为(k-1)/2,其子节点为2k+1和2k+2
例:数字为10的节点的下标为3,父结点为1号:8,子节点为7号和8号:14,15
void adjust(vector<int> &arr, int len, int index)
{
int left = 2*index + 1;
int right = 2*index + 2;
int maxIdx = index;
if(left<len && arr[left] > arr[maxIdx]) maxIdx = left;
if(right<len && arr[right] > arr[maxIdx]) maxIdx = right;
if(maxIdx != index)
{
swap(arr[maxIdx], arr[index]);
adjust(arr, len, maxIdx);
}
}
void heapSort(vector<int> &arr, int size)
{
for(int i=size/2 - 1; i >= 0; i--)
{
adjust(arr, size, i);
}
for(int i = size - 1; i >= 1; i--)
{
swap(arr[0], arr[i]);
adjust(arr, i, 0);
}
}
时间复杂度:
最好与平均:O(nlogn)
最差:O(nlogn)
空间复杂度:O(1)
优点:效率高
缺点:不稳定,一般用于较大的序列