一.冒泡排序
冒泡排序的思想在于将一个数组里面最左侧的元素向旁边的元素进行比较,如果旁边的元素比他更小,则进行交换,然后再让旁边的元素和旁边的元素进行比较,直到最后一位,那么最后一位就是最大的元素,这个时候我们在除开最后一位元素再重复这个过程,这样自然就得到了一个升序的数组,这就是冒泡排序的思想,使用两次循环就可以实现
void swap(int* a, int* b)
{
int c = *a;
*a = *b;
*b = c;
}
void bubbleSort(int* an, int n)
{
for(int a = 0; a < n ; a++)
for (int i = 0; i < n-a-1; i++)
{
if (an[i] > an[i + 1])
swap(&an[i], &an[i + 1]);
}
}
-
时间复杂度:
-
最好情况:O(n)(当数组已经有序时,只需要进行一次遍历)
-
最坏情况:O(n^2)(当数组完全逆序时,需要进行 n-1 次遍历,每次遍历比较 n-1 次)
-
平均情况:O(n^2)
-
-
空间复杂度:O(1)(只需要一个额外的变量来交换元素)
-
稳定性:稳定(相等的元素在排序后相对位置不变)
二.插入排序
插入排序的思想 就是将数组分为两个区间,一个区间是排序好的,一个区间是没有排序好的,然后将后一个区间的数插入到前一个区间的数组里面,这样依次进行,直到后一个区间的数组长度为0,就使得整个区间都是有序的,我实现这算法的第一步是先搞了个后移函数,因为我知道插入的数肯定会使得前面的数组发生变化,然后设置了一个比较大小的函数,这样判断他会在什么时候进行插入,最后再从数组的第二个元素进行循环,直到数组结束。
void delay(int* an, int n)
{
while (n)
{
an[n ] = an[n-1];
n--;
}
}
void insertSort(int* an, int n)
{
for (int i = 1; i < n; i++)
{
for (int b = 0; b < i; b++)
{
if (an[i] < an[b])
{
int a = an[i];
delay(&an[b], i - b);
an[b] = a;
}
}
}
}
在我这里的寻找插入的点其实可以优化,使用二分查找才是合适的
-
时间复杂度:
-
最好情况:O(n)(当数组已经有序时,每次插入不需要移动元素)
-
最坏情况:O(n^2)(当数组完全逆序时,每次插入需要移动 i 个元素,总移动次数为 1 + 2 + ... + (n-1) = n(n-1)/2)
-
平均情况:O(n^2)
-
-
空间复杂度:O(1)(只需要一个额外的变量来存储待插入的元素)
-
稳定性:稳定(相等的元素在排序后相对位置不变)
三.选择排序
选择排序就是将数组也分为两个数组,前面一个是排序好的,后面一个是没有排序的,然后找出后面一个数组里最小的一个数,放入前一个数组的末尾,这样前一个数组还是有序的,然后一直这样,直到后一个数组的长度为0,结束排序,这样就得到一个升序的数组,我首先是会写一个找出最小值的函数,然后将这个最小值和无序数组的第一个元素进行交换,然后将无序数组减少第一个元素,然后进行循环,这样就结束了
void choseSort(int* an, int n)
{
for (int b = 0; b < n; b++)
{
int z = b;
for (int i = b; i < n; i++)
{
if (an[i] < an[z])
{
z = i;
}
}
swap(&an[b], &an[z]);
}
}
-
时间复杂度:
-
最好情况:O(n^2)(无论数组是否有序,都需要进行 n-1 次选择和交换)
-
最坏情况:O(n^2)
-
平均情况:O(n^2)
-
-
空间复杂度:O(1)(只需要一个额外的变量来存储最小元素的索引)
-
稳定性:不稳定(相等的元素在排序后相对位置可能改变)