1.冒泡排序
核心:两两交换----每次遍历会选出最值到两头
//冒泡排序---下面为升序,降序array[j] < array[j + 1]
void bubbleSort(int array[], int count){
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if(array[j] > array[j + 1]) {//升序
array[j] = array[j] + array[j + 1];
array[j + 1] = array[j] - array[j + 1];
array[j] = array[j] - array[j + 1];
}
}
}
}
冒泡排序最好的时间复杂度为O(n)
冒泡排序平均和最坏的时间复杂度为O(n²)
空间复杂度为:O( n )
稳定的排序算法(不会改变相同数原有的先后顺序)
2.选择排序
核心:每次遍历会选出最值到两头-遍历次数依次递减
调用:
int number1[7] = {10, 2, 23, 11, 9, 3, 18};
selectSorts(number1, 7);
//具体实现---下面为升序,降序array[j] > array[min]
void selectSorts(int arr[],int length){
// int temp;
for (int i = 0; i < length - 1; i++) {
int min = i;
for (int j = i+1; j < length; j++) {
if (arr[j] < arr[min]){
min = j;
}
}
if (i != min) {
//不用中间变量交换----相加法
// array[i] = array[i] + array[min];
// array[min] =array[i] - array[min];
// array[i] = array[i] - array[min];
//不用中间变量交换----辗转相除法
array[i] = array[i] ^ array[min];
array[min] =array[i] ^ array[min];
array[i] = array[i] ^ array[min];
//中间变量交换
// temp = array[i];
// array[i] = array[min];
// array[min] = temp;
}
}
}
时间/空间复杂度---平均和最坏的都是O(n²)
不稳定的排序方法
3.快速排序
核心:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以[递归]进行,以此达到整个数据变成有序[序列](https://baike.baidu.com/item/%E5%BA%8F%E5%88%97/1302588)。
/*快速排序*/
void Qsort(int a[],int low ,int high)
{
if(low>=high)
return ;
printf("start:%d end:%d\n",low,high) ;
int first = low ;
int last = high ;
int key = a[low] ;
while(first
{
while(first=key)
--last ;
a[first] = a[last] ;
while(first
++first ;
a[last] = a[first] ;
}
a[first] = key ;
Qsort(a,low,first-1) ;
Qsort(a,first+1,high) ;
}
最好和平均的时间复杂度为O(nlogn)
最坏的时间复杂度为O(n²)
最优空间复杂度 longn
最差空间复杂度为:O( n )
4.直接插入排序
核心:两层for循环;外层每次先找到那个比前面值都小(升序)或都大(降序)的指,并做标记;
内层for循环将比它大的值依次后移;然后再将标记的值赋值给内层循环最小标记的下标+1出做替换
void strightINsertSort(int arr[], int count){
for (int i = 1; i < count; i ++) {
if (arr[i - 1] > arr[i]) {
int temp = arr[i];
int j;
for (j = i - 1; j >= 0 && arr[j] > temp; j --) {
arr[j+1] = arr[j];
}
arr[j + 1] = temp;
}
}
}
最好情况(初始情况就是正序)下时间复杂度是O(n)
平均情况和最坏情况时间复杂度是O(n²)
5.二分插入排序
核心:是在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,
如果小,则对前半再进行折半,否则对后半进行折半,
直到left>right,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。
void binaryInsertSort(int arr[], int count){
for (int i = 1; i < count; i ++) {
if (arr[i] < arr[i - 1]) {
int temp = arr[i];
int left = 0;
int right = i - 1;
while (left <= right) {
int center = (left + right)/2;
if (arr[center] <= temp) {
left = center + 1;
}else{
right = center - 1;
}
}
for (int j = i; j > left; j --) {
arr[j] = arr[j - 1];
}
arr[left] = temp;
}
}
}
最好的时间复杂度O(logn)。
最坏和平均时间复杂度O(n^2)。
稳定的排序算法
6.堆排序
原理动图----引用于七巧板子博主
核心: 初始化大顶堆时 是从最后一个有子节点开始往上调整最大堆。而堆顶元素(最大数)与堆最后一个数交换后,需再次调整成大顶堆,此时是从上往下调整的。
不管是初始大顶堆的从下往上调整,还是堆顶堆尾元素交换,每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换,
交换之后都可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整。
在算法中是用一个while循环来解决
//最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
void maxHeapify(int numbers[], int start, int end) {
//建立父节点指标和子节点指标
int dad = start;
int son = dad * 2 + 1;
while (son <= end) { //若子节点指标在范围内才做比较
if (son + 1 <= end && numbers[son] < numbers[son + 1]) { //先比较两个子节点大小,选择最大的
son++;
}
if (numbers[dad] > numbers[son]) {//如果父节点大于子节点代表调整完毕,直接跳出函数
return;
}else { //否则交换父子内容再继续子节点和孙节点比较
int temp = numbers[son];
numbers[son] = numbers[dad];
numbers[dad] = temp;
dad = son;
son = dad * 2 + 1;
}
}
}
/**堆排序(HeapSort):
1、首先从最后一个父节点做最大堆调整,找出最大值,此时最大值位于根节点
2、然后将最大值和已排好元素(依次找出的最大值排出的序列,如:6 3 9 23 12 8 [15 16 17 18])前一位元素交换位置,再将除已排好元素外的其他元素(6 3 9 23 12 8)做最大堆调整,找出最大值,此时最大值位于根节点
3、重复步骤2,直至结束
*/
void heapSort(int numbers[], int length) {
//1.初始化堆(子节点-->根节点),i从最後一个父节点开始做最大堆调整,调整结束后根节点得到最大值
for (int i = length / 2 - 1; i >= 0; i--) {
maxHeapify(numbers, i, length - 1);
}
//2.(根节点-->子节点)先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
for (int i = length - 1; i > 0; i--) {
int temp = numbers[0];
numbers[0] = numbers[i];
numbers[i] = temp;
maxHeapify(numbers, 0, i - 1);
}
}
最好、最坏和平均时间复杂度都为O(nlogn)。
不稳定排序算法
7.归并排序
核心:该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
若将两个有序表合并成一个有序表,称为二路[归并]
>>从上往下-->步骤
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
//合并
void merge(int a[], int start, int mid, int end) {
int *temp = (int *)malloc((end - start + 1) * sizeof(int)); // tmp是汇总2个有序区的临时区域
int i = start; // 第1个有序区的起始索引
int j = mid + 1; // 第2个有序区的起始索引
int k = 0; // 临时区域的索引
while(i <= mid && j <= end) {
if (a[i] <= a[j]) {
temp[k++] = a[i++];
}else {
temp[k++] = a[j++];
}
}
while(i <= mid) {
temp[k++] = a[i++];
}
while(j <= end) {
temp[k++] = a[j++];
}
// 将排序后的元素,全部都整合到数组a中。
for (i = 0; i < k; i++) {
a[start + i] = temp[i];
}
free(temp);
}
/*
* 归并排序(从上往下)
*
* 参数说明:
* a -- 待排序的数组
* start -- 数组的起始地址
* endi -- 数组的结束地址
*/
void mergeSortUpToDown(int a[], int start, int end) {
if(a == NULL || start >= end) {
return ;
}
int mid = (end + start) / 2;
mergeSortUpToDown(a, start, mid); //递归排序a[start...mid]
mergeSortUpToDown(a, mid + 1, end); //递归排序a[mid + 1...end]
// a[start...mid] 和 a[mid...end]是两个有序空间,
// 将它们排序成一个有序空间a[start...end]
merge(a, start, mid, end);
}
最好、最坏和平均时间复杂度均为O(nlogn)。
稳定的排序算法