1. 冒泡排序
从左到右不断交换相邻逆序的元素,在一轮循环之后,最大的元素放在最右边。
一轮循环如果没有进行交换,说明已经有序,可以直接退出。
时间复杂度: 最好O(n);最坏O(n2 );平均O(n2 )
空间复杂度:O(1)
稳定。
2. 选择排序
选择最小的元素与第一个元素互换位置。再从剩下的数组中选择最小的元素,与第二个元素交换。不断重复。
时间复杂度:O(n2 )
空间复杂度:O(1)
不稳定。
3. 插入排序
将第一个元素视为有序。对于无序数据,将其与有序序列从后向前扫描,找到左侧相应位置插入。
时间复杂度: 最好O(n);最坏O(n2 );平均O(n2 )
空间复杂度:O(1)
稳定。
4. 希尔排序
在插入排序上的改进。希尔增量:{n/2,n/2/2…1}。将序列分成 增量 个组,对每组进行插入排序。然后选择下一个增量,分组进行插入排序。直到增量为1 。
对于插入排序,每一次交换使得逆序-1;而希尔排序每次逆序->1;因为比插入排序高效。
时间复杂度:最好O(n);最坏O(n2 );平均O(n1.3 )
空间复杂度:O(1)。
不稳定
5. 归并排序 *
分治和递归思想。
将输入序列对半分;对两个子序列分别采用归并排序;将两个排序号的子序列合并成一个最终的序列。
时间复杂度:O(nlog n)
空间复杂度:O(n)。
稳定。
归并排序思想延伸:小和问题,逆序对问题。
void merge(vector<int> &arr,int left,int mid,int right)
{
int n1 = mid-left+1;
int n2 = right-mid;
int* L = new int[n1];
int* R = new int[n2];
for(int i=0;i<n1;i++)
{
L[i]=arr[left+i];
}
for(int i=0;i<n2;i++)
{
R[i]=arr[mid+1+i];
}
for(int i=0,j=0,p = left; p<=right; p++)
{
if (i>=n1)
{
arr[p]=R[j++];
}
else if (j>=n2)
{
arr[p]=L[i++];
}
else if(L[i] <= R[j])
{
arr[p] =L[i++];
}
else{
arr[p]=R[j++];
}
}
delete []L;
delete []R;
}
void merge_sort(vector<int> &arr,int left,int right)
{
if(left<right)
{
int p = (left+right)/2;
merge_sort(arr, left, p);
merge_sort(arr, p+1, right);
merge(arr,left,p,right);
}
return ;
}
6. 快速排序 *
通过一个切分元素对数组进行切分,将比基准小的元素放左边,大的放右边。用递归将两侧数列分别进行快排。
切分:去a[1]作为切分元素,两个指针,一个从左向右扫描直到找到一个大于等于它的元素,一个从右向左扫描找到一个比他小的元素,交换。不断进行着过程,直到两个指针相遇,将切分元素与右指针元素交换。
时间复杂度:O(nlog n),最差O(n2 )
空间复杂度:O(log n)。
不稳定
void quicksort(int left,int right, vector<int> & arr)
{
if(left<right)
{
int i=left;
int j=right;
int base = arr[left];
while (i<j) {
while(i<j && arr[j]>=base) j--;
if(i<j) arr[i++]=arr[j];
while(i<j && arr[i]<=base) i++;
if(i<j) arr[j--]=arr[i];
}
arr[i]=base;
quicksort(left, i-1, arr);
quicksort(i+1, right, arr);
}
}
7. 堆排序*
将初始化序列构建成大顶堆;将堆顶元素与最后一个元素交换;交换后进行下沉操作调整堆为大顶堆;不断操作上述过程,直到完成。
大顶堆:父节点比子节点大。
调整堆:上浮和下沉。
时间复杂度:O(nlog n)
空间复杂度:O(1)。
不稳定