1.冒泡排序
原理:
②对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
③针对所有的元素重复以上的步骤,除了最后一个。
④持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
平均时间复杂度为:
代码如下:
//简单理解:重复“从序列右边开始比较相邻两个数字的大小,再根据结果交换两个数字的位置”
int* bubbling_sort(int arr[], int n)//从小到大
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n-1-i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
}
}
}
return arr;
}
2.选择排序
原理:
①首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
②再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
③重复第二步,直到所有元素均排序完毕。
平均时间复杂度:
代码如下:
//简单理解:重复“从待排序的数值中寻找最小值,将其与序列最左边的数字进行交换”。
int *select_sort(int arr[], int n)
{
int min = 0;
for (int i = 0; i < n; i++)
{
min = i;
for (int j = i+1; j < n; j++)
{
if (arr[j] < arr[min])
{
min = j;
}
}
if (min != i)
{
int tmp = arr[i];
arr[i] = arr[min];
arr[min] = tmp;
}
}
return arr;
}
3.插入排序
原理:
①在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。 ②如此反复循环,直到全部排好顺序。
平均时间复杂度:O(n^2)
代码如下:
//简单理解:重复“从序列左端开始依次对数据进行排序的算法”。左侧数据为已排序,右侧数据为未排序。右侧取值值与左侧对比,并插入相应位置。
int *insert_sort(int arr[], int n)
{
for (int i = 0; i < n-1; i++)
{
if (arr[i] > arr[i+1])
{
int tmp = arr[i+1];
arr[i+1] = arr[i];
arr[i] = tmp;
for (int k = i-1; k >= 0; k--)
{
if (arr[k+1] < arr[k])
{
int tmp = arr[k];
arr[k] = arr[k+1];
arr[k+1] = tmp;
}
}
}
}
return arr;
}
4.堆排序
原理:
①将无序序列构建成一个堆,根据升序降序需求选择最大堆或最小堆;
②将堆顶元素与末尾元素交换,将最大(小)元素"沉"到数组末端;
③重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
最大堆和最小堆:
当父结点的键值总是大于或等于任何一个子结点的键值时称为最大堆。
当父结点的键值总是小于或等于任何一子节点的键值时最小堆。
平均时间复杂度:O(NlogN)
代码如下:
//简单理解:(一开始需要将数据存进堆里)从降序序列的堆中取出数据时,会从最大的数据开始取,所以将取出的数据反序输出,排序就完成了。
void heap_adjust(int arr[], int mid, int n)
{
for (int i = mid; i > 0; i--)//找出最大值,并放在根节点
{
if (i * 2 < n && arr[i - 1] < arr[i * 2])//结点与其子右结点进行判断
{
int tmp = arr[i - 1];
arr[i - 1] = arr[i * 2];
arr[i * 2] = tmp;
}
if (arr[i - 1] < arr[i * 2 - 1])//结点与其子左结点进行判断
{
int tmp = arr[i - 1];
arr[i - 1] = arr[i * 2 - 1];
arr[i * 2 - 1] = tmp;
}
}
}
int * heap_sort(int arr[], int n)
{
for (int i = n; i > 1; i--)
{
heap_adjust(arr, i / 2, i);
int tmp = arr[0]; //把根节点放在末尾 从小到大排序
arr[0] = arr[i - 1];
arr[i - 1] = tmp;
}
return arr;
}
5.归并排序
原理:
①申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
②设定两个指针,最初位置分别为两个已经排序序列的起始位置
③比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
④重复步骤3直到某一指针超出序列尾,将另一序列剩下的所有元素直接复制到合并序列尾
平均时间复杂度:O(nlogn)
代码如下:
//简单理解:把序列分成长度相同的两个子序列,当无法继续往下分时,也就是每一个子序列只有一个数据,就对子序列进行归并
void merge(int arr[], int start,int mid, int end)
{
int left = mid - start + 1;//左边数量
int right = end - mid;//右边数量
int l_arr[100];
int r_arr[100];
//复制数据到两个数组中,通过memcpy()函数
memcpy(l_arr, arr + start, sizeof(int)*left);
memcpy(r_arr, arr + mid + 1, sizeof(int)*right);
int i = 0, j = 0, k = start;
while (i < left && j < right)
{
arr[k++] = l_arr[i] < r_arr[j] ? l_arr[i++] : r_arr[j++];//两者两者进行判断,并输出最小值
}
while (i < left)//判断前面部分,如果j先超出,需要更换,反之i,则不用,j本身就在实时坐标上,因此不用更换
{
arr[k++] = l_arr[i++];
}
}
void merge_sort(int *arr,int start,int end)
{
if (start < end)
{
//归
int mid = (end + start) / 2;
merge_sort(arr, start, mid);
merge_sort(arr, mid + 1, end);
//并
merge(arr, start, mid, end);
}
}
6.快速排序
原理:
②将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
③然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
④ 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
平均时间复杂度:O(nlogn)
代码如下:
//简单理解:首先在序列中随机选择一个基准值:
//[比基准值小的数值] 基准值 [比基准值大的数值],再次对[]进行快速排序即可
void fast_sort(int arr[], int low,int high)
{
if (low >= high)
return;
int i = low;
int j = high+1;
int key = arr[low];
while (1)
{
while (arr[++i] < key)//从左往右寻找 找出比它大的值
{
if (i == high)
{
break;
}
}
while (arr[--j] > key) //从右向左寻找 找出比它小的值
{
if (j == low)
{
break;
}
}
if (i >= j) break;
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//把中间值放在中间
int tmp = arr[low];
arr[low] = arr[j];
arr[j] = tmp;
fast_sort(arr, low, j-1);
fast_sort(arr, j+1, high);
}