它的原理很简单,每次从左到右两两比较,把大的交换到后面,每次可以确保将前M个元素的最大值移动到最右边;
步骤:
void bubble_sort(vector<int> &nums)
{
for (int i = 0; i < nums.size() - 1; i++) { // times
for (int j = 0; j < nums.size() - i - 1; j++) { // position
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
改进:
可以提前结束排序 —— 若无交换
void bubble_sort(vector<int> &nums)
{
for (int i = 0; i < nums.size() - 1; i++) { // times
bool isEnd = true;
for (int j = 0; j < nums.size() - i - 1; j++) { // position
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
isEnd = false;
}
}
if(isEnd)
break; // 若未进行交换,则代表已经完成排序;故而可以停止循环了;
}
}
时间复杂度:
由于两重循环,故而为 O ( n 2 ) O(n^2) O(n2); 最好情况: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n);
稳定性:稳定
插入排序的原理是从左到右,把选出的一个数和前面的数进行比较,找到最适合它的位置放入,使前面部分有序;
步骤:
void InsertSort(int &num, int len){ // len: 长度
for(int i=1;i<len-1;++i)
{
int j = i-1;
int temp = num[i];
while(num[i] < num[j] && j>= 0)
{
num[j+1] = num[j];
j--;
}
if(j<0)
num[0] = temp;
else
num[j+1] = temp;
}
}
时间复杂度: O ( n 2 ) O(n^2) O(n2)
稳定性: 稳定
选择排序的原理是,每次都从乱序数组中找到最大(最小)值,放到当前乱序数组头部,最终使数组有序。
void selection_sort(vector<int> &nums)
{
for (int i = 0; i < nums.size(); i++) { // position
int min = i;
for (int j = i + 1; j < nums.size(); j++) {
if (nums[j] < nums[min]) {
min = j;
}
}
int temp = nums[i];
nums[i] = nums[min];
nums[min] = temp;
}
}
时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( n ) O(n) O(n)
稳定性:不稳定
希尔排序从名字上看不出来特点,因为它是以发明者命名的。它的另一个名字是“递减增量排序算法“。这个算法可以看作是插入排序的优化版,因为插入排序需要一位一位比较,然后放置到正确位置。为了提升比较的跨度,希尔排序将数组按照一定步长分成几个子数组进行排序,通过逐渐减短步长来完成最终排序
为什么这个算法能起作用:
开始的时候gap较大,子序列中的数据较少,所以最开始的时候算法运行较快,随着算法进行,gap 逐渐变小,子序列中元素的个数也就越来越多,所以排序工作可能会变慢,但是由于前面已经完成了部分排序工作,因而在很大程度上减轻了后来的工作量,于是最终总体的排序速度还是比较快的
步骤:
实例:
第一步: l e n = 6 , g a p = 3 len = 6, gap = 3 len=6,gap=3
第二轮: g a p = 1 gap = 1 gap=1
代码实现:
void shellSort(int a[], int n) //a -- 待排序的数组, n -- 数组的长度
{
int i,j,gap; // gap为步长,每次减为原来的一半。
for (gap = n / 2; gap > 0; gap /= 2)
{
// 共gap个组,对每一组都执行直接插入排序
for (i = 0 ;i < gap; i++)
{
for (j = i + gap; j < n; j += gap)
{
// 如果a[j] < a[j-gap],则寻找a[j]位置,并将后面数据的位置都后移。
if (a[j] < a[j - gap])
{
int tmp = a[j];
int k = j - gap;
while (k >= 0 && a[k] > tmp)
{
a[k + gap] = a[k];
k -= gap;
}
a[k + gap] = tmp;
}
}
}
}
}
时间复杂度:与步长相关
空间复杂度: O ( n ) O(n) O(n)
稳定性:不稳定
归并排序是采用分治法(Divide and Conquer)的一个典型例子。这个排序的特点是把一个数组打散成小数组,然后再把小数组拼凑再排序,直到最终数组有序
void merge_array(vector<int> &nums, int low, int mid, int high, vector<int> &temp)
{
int lb = low, rb = mid, tb = low;
while (lb != mid && rb != high)
if (nums[lb] < nums[rb])
temp[tb++] = nums[lb++];
else
temp[tb++] = nums[rb++];
while (lb < mid)
temp[tb++] = nums[lb++];
while (rb < high)
temp[tb++] = nums[rb++];
for (int i = low;i < high; i++)
nums[i] = temp[i];
}
void merge_sort(vector<int> &nums, int low, int high, vector<int> &temp)
{
int mid = (low + high) / 2;
if (mid != b) {
merge_sort(nums, low, mid, temp);
merge_sort(nums, mid+1, high, temp);
merge_array(nums, low, mid, high, temp);
}
}
这个实现中加了一个temp,是和原数组一样大的一个空间,用来临时存放排序后的子数组的
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn) —— 这个实现中加了一个temp,是和原数组一样大的一个空间,用来临时存放排序后的子数组的
空间复杂度: O ( n ) O(n) O(n)
稳定性:不稳定
快速排序也是利用分治法实现的一个排序算法。快速排序和归并排序不同,它不是一半一半的分子数组,而是选择一个基准数,把比这个数小的挪到左边,把比这个数大的移到右边。然后不断对左右两部分也执行相同步骤,直到整个数组有序;
void quick_sort(vector<int> &nums, int low, int high)
{
if (low < high) {
int lb = low, hb = high;
while (lb < hb) {
while (nums[hb] >= nums[low] && lb < hb)
hb--;
while (nums[lb] <= nums[low] && lb < hb)
lb++;
swap(nums[lb], nums[hb]);
}
swap(nums[low], nums[lb]);
quick_sort(nums, low, lb);
quick_sort(nums, lb + 1, high);
}
}
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)
稳定性:不稳定
堆排序经常用于求一个数组中最大k个元素时。因为堆实际上是一个完全二叉树,所以用它可以用一维数组来表示。因为最大堆的第一位总为当前堆中最大值,所以每次将最大值移除后,调整堆即可获得下一个最大值,通过一遍一遍执行这个过程就可以得到前k大元素,或者使堆有序
在了解算法之前,首先了解在一维数组中节点的下标:
步骤:
void heap_sort(vector<int> &nums)
{
int n = nums.size();
for (int i = n / 2 - 1; i >= 0; i--) { // build max heap
max_heapify(nums, i, nums.size() - 1);
}
for (int i = n - 1; i > 0; i--) { // heap sort
int temp = nums[i];
num[i] = nums[0];
num[0] = temp;
max_heapify(nums, 0, i);
}
}
void max_heapify(vector<int> &nums, int beg, int end)
{
int curr = beg;
int child = curr * 2 + 1;
while (child < end) {
if (child + 1 < end && nums[child] < nums[child + 1]) {
child++;
}
if (nums[curr] < nums[child]) {
int temp = nums[curr];
nums[curr] = nums[child];
num[child] = temp;
curr = child;
child = 2 * curr + 1;
} else {
break;
}
}
}
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)
稳定性:不稳定