一百个人眼中一百个我,我既是天使又是恶魔
生活的路上总会遇到形形色色的人
他们对我们的评价都各有千秋
就像优秀的人看到看到努力的人,会感觉他很自律 很拼
而一些一无所有还想着躺平的人,会感觉他很卷 很作
我们要做的并不是因为那些躺平人的眼光而改变自我的目标
而是悄悄的努力成为别人的梦想
1.冒泡排序
2.快速排序
3.归并排序
1、冒泡排序
升序排序:
升序:每一趟比较,都会让对比的数中的最大数进行归位
降序:每一趟比较,都会让对比的数中的最小数进行归位
思考:要是本就有序或者接近有序,还要对比n-1趟吗?(要是这样还要对比n-1趟也太麻烦了吧!)
解决办法:
我们可以用一个变量记录,要是一趟中没有进行交换,就默认为0,要是发生交换了就为1。然后判断这个变量是否为0,要是为0跳出比较循环,否则继续执行循环比较。
代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> //交换 void Swap(int* p1,
int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //冒泡排序 void BubbleSort(int*
a, int n) { for (int j = 0; j < n; j++) { int exchange = 0; for (int i = 1; i <
n - j; i++) { if (a[i - 1] > a[i]) { Swap(&a[i - 1], &a[i]); exchange =
1;//避免不必要的排序 } } if (exchange == 0) { break; } } } void PrintArray(int* a, int
n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } int
main() { int a[] = { 7, 3, 2, 6, 8, 1, 9}; int size = sizeof(a) / sizeof(int);
BubbleSort(a, size); PrintArray(a, size); return 0; }
冒泡排序的特性总结:
* 冒泡排序是一种非常容易理解的排序
* 时间复杂度:O(N^2)
* 空间复杂度:O(1)
* 稳定性:稳定
2、快速排序
2.1挖坑法
注:一般把第一个或最后一个数当做关键字,向左找比关键字key小的值,向右找比关键字key大的值。
思路:首先把第一个数当做关键字赋值给key,然后第一个数的位置就是一个坑,从end向左找找比关键字小的数,找到之后把他放到坑里,然后它所在的位置变成一个新坑,然后在从begin向右找比关键字大的数放进新坑继续形成一个新坑,依次这样直到end和begin和pivot指向同一个位置把key放进这个地方,这一趟排序会使一个数归位。然后用同样的方式让这个数左边的数有序,右边的数有序,整个数组就有序了。
思考:为什么要加begin < end ?
因为当begin = end时,key值也就归位了,然后key前已经小于key值,key后已经大于key值。所以不需要跨越查找。
考虑:快速排序什么情况下最坏了?当它有序时,时间复杂度为O(N^2)。
那我们怎么解决这种问题了???
利用三数取中法。
三数取中法思路:对比第一个下标的数和中间下标的数与最后下标的数,返回中间数(不是最大也不是最小)。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> //交换 void Swap(int* p1,
int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //三数取中法 int GetMidIndex(int*
a, int left, int right) { int mid = (left + right) >> 1; if (a[left] < a[mid])
{ if (a[mid] < a[right]) { return mid; } else if (a[left] > a[right]) { return
left; } else { return right; } } else // a[left] > a[mid] { if (a[mid] >
a[right]) { return mid; } else if (a[left] < a[right]) { return left; } else {
return right; } } } //快速排序之挖坑法 void QuickSort(int* a,int left,int right) { if
(left >= right) { return; } int index = GetMidIndex(a, left, right);
Swap(&a[left], &a[index]); int begin = left; int end = right; int key =
a[begin]; int pivot = begin; while (begin < end) { while (begin < end && a[end]
>= key) { end--; } a[pivot] = a[end]; pivot = end; while (begin < end &&
a[begin] <= key) { begin++; } a[pivot] = a[begin]; pivot = begin; } a[pivot] =
key; QuickSort(a, left, pivot - 1); QuickSort(a, pivot + 1, right); } void
PrintArray(int* a, int n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]);
} printf("\n"); } int main() { int a[] = { 7, 3, 2, 6, 8, 1, 9}; int size =
sizeof(a) / sizeof(int); QuickSort(a, 0, size - 1); PrintArray(a, size); return
0; }
2.2左右指针法
思路:向左找比关键字小的,向右找比关键字大的,找到之后交换
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> //交换 void Swap(int* p1,
int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //三数取中法 int GetMidIndex(int*
a, int left, int right) { int mid = (left + right) >> 1; if (a[left] < a[mid])
{ if (a[mid] < a[right]) { return mid; } else if (a[left] > a[right]) { return
left; } else { return right; } } else // a[left] > a[mid] { if (a[mid] >
a[right]) { return mid; } else if (a[left] < a[right]) { return left; } else {
return right; } } } //快速排序之左右指针法 void QuickSort2(int* a, int lefti, int righti)
{ if (lefti >= righti) { return; } int index = GetMidIndex(a, lefti, righti);
Swap(&a[lefti], &a[index]); int left = lefti; int right = righti; int key =
left; while (left < right) { while (left < right && a[right] >= a[key]) {
--right; } while (left < right && a[left] <= a[key]) { ++left; } Swap(&a[left],
&a[right]); } Swap(&a[key], &a[left]); QuickSort2(a, lefti, left-1);
QuickSort2(a, left+1, righti); } void PrintArray(int* a, int n) { for (int i =
0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } int main() { int a[] =
{ 7, 3, 2, 6, 8, 1, 9}; int size = sizeof(a) / sizeof(int); QuickSort2(a, 0,
size - 1); PrintArray(a, size); return 0; }
2.3前后指针法
思路:front在前面找比key大的值,当找到了after++,然后交换两个值 (小的往前推,大的往后推)
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> //交换 void Swap(int* p1,
int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } //三数取中法 int GetMidIndex(int*
a, int left, int right) { int mid = (left + right) >> 1; if (a[left] < a[mid])
{ if (a[mid] < a[right]) { return mid; } else if (a[left] > a[right]) { return
left; } else { return right; } } else // a[left] > a[mid] { if (a[mid] >
a[right]) { return mid; } else if (a[left] < a[right]) { return left; } else {
return right; } } } //快速排序之前后指针法 void QuickSort3(int* a, int lefti, int righti)
{ if (lefti >= righti) { return; } int index = GetMidIndex(a, lefti, righti);
Swap(&a[lefti], &a[index]); int after = lefti; int front = lefti + 1; int key =
lefti; while (front <= righti) { if (a[front] < a[key]) { after++;
Swap(&a[front], &a[after]); } front++; } Swap(&a[after], &a[key]);
QuickSort3(a, lefti, after - 1); QuickSort3(a, after + 1, righti); } void
PrintArray(int* a, int n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]);
} printf("\n"); } int main() { int a[] = { 7, 3, 2, 6, 8, 1, 9}; int size =
sizeof(a) / sizeof(int); QuickSort3(a, 0, size - 1); PrintArray(a, size);
return 0; }
快速排序的特性总结:
* 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
* 时间复杂度:O(N*logN)
* 空间复杂度:O(logN)
* 稳定性:不稳定
3、归并排序
基本思想: 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法 (Divide and
Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
#include<stdlib.h> #include<stdio.h> //归并排序 void _MergeSort(int* a, int left,
int right, int* tmp) { if (left >= right) return; int mid = (left + right) >>
1; // 假设 [left, mid] [mid+1, right]有序,那么我们就可以归并了 _MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp); // 归并 int begin1 = left, end1 = mid; int
begin2 = mid + 1, end2 = right; int index = left; while (begin1 <= end1 &&
begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[index++] = a[begin1++]; }
else { tmp[index++] = a[begin2++]; } } while (begin1 <= end1) { tmp[index++] =
a[begin1++]; } while (begin2 <= end2) { tmp[index++] = a[begin2++]; } // 拷贝回去
for (int i = left; i <= right; ++i) { a[i] = tmp[i]; } } void MergeSort(int* a,
int n) { int* tmp = (int*)malloc(sizeof(int)*n); _MergeSort(a, 0, n - 1, tmp);
free(tmp); } void PrintArray(int* a, int n) { for (int i = 0; i < n; i++) {
printf("%d ", a[i]); } printf("\n"); } int main() { MergeSort(a, size - 1);
PrintArray(a, size); return 0; }
归并排序的特性总结:
* 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问 题。
* 时间复杂度:O(N*logN)
* 空间复杂度:O(N)
* 稳定性:稳定
排序算法复杂度及稳定性分析