冒泡排序
void bubble_sort(int arr[], int length)
{
for(int i = 0; i < length; ++i) //遍历数组,每次结束将最大元素依次放到最后
{
for(int j = 0; j < length - i - 1; ++j) //开始冒泡,如果相邻前一个元素大于后一个就交换
{
if(arr[j] > arr[j + 1])
{
//int temp = arr[j];
//arr[j] = arr[j + 1];
//arr[j + 1] = temp;
//异或运算符实现交换两数 ##注意两数不能相等##
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
}
| 时间复杂度 |
空间复杂度 |
稳定性(能否保持两个符合条件的元素间的位置关系) |
| O(N²) |
O(1) |
√ |
选择排序
void select_sort(int arr[], int length)
{
for(int i = 0; i < length; i++) //循环遍历数组,每次遍历中的最小元素放到前面
{
int k = i; //初始化k,用于记录最小元素下标
//每次遍历都选择将最小值的下标传递给k
for (int j = i + 1; j < length; j++)
{
if (arr[k] > arr[j])
k = j;
}
//遍历完成后,将最小元素与当前数组最前面元素交换
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
| 时间复杂度 |
空间复杂度 |
稳定性 |
| O(N²) |
O(1) |
√ |
插入排序
void insert_sort(int arr[], int length)
{
for(int i = 0; i < length; i++)
{
int key = arr[i]; //记录待插入值
int j = i - 1;
while(j >= 0 && (arr[j] > key))
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
最外层的每次遍历都是取当前下标的元素作为待插入的值,然后依次与前面的数组中的元素进行比较,决定当前元素的插入位置。
| 时间复杂度 |
空间复杂度 |
稳定性 |
| O(N²) |
O(1) |
√ |
快速排序
void quick_sort(int arr[], int left, int right)
{
if(left >= right)
return;
int base = arr[right]; //随机选择基准可以降低出现最坏情况的概率,这里总选择最后一个元素作为基准
int left_guard = left;
int right_guard = right;
while (left_guard <= right_guard)
{
//左右哨兵碰头或数组中只有一个元素,结束循环
if(left_guard == right_guard)
break;
//左侧小于基准就向后走一步
while(arr[left_guard] < base && left_guard < right_guard) //选择右侧作为基准时,一定要左侧先开始走
++left_guard;
//右侧大于基准就向前走一步
while(arr[right_guard] >= base && right_guard > left_guard)
--right_guard;
//当左哨兵所在元素大于基准,右哨兵所在元素小于基准时,两个while结束,交换元素
int temp = arr[left_guard];
arr[left_guard] = arr[right_guard];
arr[right_guard] = temp;
}
//将基准放到中间
//由于基准在右侧,左哨兵先走,所以两哨兵碰头时所指向的元素一定是大于基准的
arr[right] = arr[right_guard];
arr[right_guard] = base; //至此数组分为两个区域,左侧小于等于基准,右侧大于基准
//对子区域继续快排
quick_sort(arr, left, left_guard - 1); //左子域
quick_sort(arr, right_guard + 1, right); //右子域
}
快排是通用排序里相对较好的排序算法,使用到了“二分法”这种分治的思想,大大加速了排序的时间,后面的归并排序也体现了分治的思想。
| 时间复杂度 |
空间复杂度 |
稳定性 |
| O(N*log(N)) |
O(logN) |
× |
归并排序
void merge(int arr[], int left, int mid, int right)
{
if(left >= right)
return;
const int SIZE = right - left + 1;
int arr_copy[10];
int lbegin = left;
int lend = mid;
int rbegin = mid + 1;
int rend = right; //两子数组范围
int num = 0;
while(lbegin <= lend && rbegin <= rend)
arr_copy[num++] = arr[lbegin] <= arr[rbegin]? arr[lbegin++]:arr[rbegin++];
while(lbegin <= lend) //这个while和下个while将两数组中其中一个剩下的元素继续放入临时数组
arr_copy[num++] = arr[lbegin++];
while(rbegin <= rend)
arr_copy[num++] = arr[rbegin++];
for(int i = 0; i < SIZE; ++i) //将临时数组中有序的元素依次拷贝到原数组
arr[left + i] = arr_copy[i];
}
void merge_sort(int arr[], int left, int right)
{
if(left >= right)
return;
int mid = left + (right - left)/2; //数组从中间分成两部分
merge_sort(arr, left, mid); //使左子数组有序
merge_sort(arr, mid + 1, right); //使右子数组有序
merge(arr, left, mid, right); //合并左右两有序数组
}
归并排序和快排具有相同的时间复杂度和空间复杂度,并且都体现了分治的思想,不过,归并排序的优势不仅在排序算法本身上,这种划分的方法更可以扩展到并行计算上,从而可以借助多个线程达到更快的排序速度。
| 时间复杂度 |
空间复杂度 |
稳定性 |
| O(N*log(N)) |
O(logN) |
√ |
堆排序
void heap_sort(int arr[], int length)
{
//初始化堆
for (int i = length / 2 - 1; i >= 0; i--) //堆最后父节点是length/2 -1
heapify(arr, i, length - 1);
//将首项与末尾交换,再对前面数组继续堆化
for(int i = length - 1; i > 0; i--)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr, 0, i - 1);
}
}
void heapify(int arr[], int begin, int end) //大根堆化 从begin位置开始向下堆化
{
int headnode = begin; //父节点
int subnode = headnode * 2 + 1;
while(subnode <= end)
{
if((subnode + 1) <= end && arr[subnode + 1] > arr[subnode]) //选择子节点中较大的
subnode++;
if(arr[subnode] > arr[headnode]) //如果子节点大于父节点就交换
{
int temp = arr[subnode];
arr[subnode] = arr[headnode];
arr[headnode] = temp;
//如果有,继续比较孙子
headnode = subnode;
subnode = headnode * 2 + 1;
}
else //父最大就直接返回
return;
}
}
堆排序是二叉堆的一种应用,形成大根堆或者小根堆的过程,天然保证了根是最大或最小的,排序的过程也就是不断将数组进行堆化的过程。
.
| 时间复杂度 |
空间复杂度 |
稳定性 |
| O(N*log(N)) |
O(1) |
× |
桶排序
int maxbit(int arr[], int length) //计算最高位位数
{
int maxbt = arr[0];
for(int i = 1; i < length; i++)
{
if(maxbt < arr[i])
maxbt = arr[i];
}
int d = 1;
while ((maxbt / d) != 0)
{
d *= 10;
}
d /= 10;
return d;
}
void radix_sort(int arr[],int length)
{
int d = maxbit(arr, length);
std::queue<int> bocket[10];
for(int i = 1; i <= d; i *= 10)
{
for(int j = 0; j < length; j++)
bocket[arr[j] % i].push(arr[j]);
for(int j = 0; j < length; j++)
{
int num = 0;
while(!bocket[j].empty())
{
arr[num++] = bocket[j].front();
bocket[j].pop();
}
}
}
}
桶排序区别与通用的排序方法,它不是根据元素的比较进行排序的,而是根据要排序元素的某些特性设计出来的。比如上述这个排序方法就是按照数字的各个数位上的值放入不同的桶中,执行常数轮后,再拿出来的数组就已经是排好序的了。这种非比较的特性决定了桶排序不是一种通用的排序算法,但是如果能找到要排序元素的某个顺序性质后,桶排序无疑是能得到极快的排序速度。
| 时间复杂度 |
空间复杂度 |
稳定性 |
| O(N) |
O(N) |
- |