网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
}
}
测试代码如下所示:
int arr[8] = {25, 22, 12, 8, 9, 11, 30 ,40};
bubbleSort(arr, 8);
cout<<"ascend: "<<endl;
for (int i = 0; i < 8; ++i) {
cout<<i<<": "<<arr[i]<<endl;
}
bubbleSort(arr, 8, false);
cout<<"descend: "<<endl;
for (int i = 0; i < 8; ++i) {
cout<<i<<": "<<arr[i]<<endl;
}
打印如下所示:
![](https://img-blog.csdnimg.cn/08fa12d515e9453bac65dc84db5bd7d4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6K-66LCm,size_15,color_FFFFFF,t_70,g_se,x_16)
**总结:**
选择排序是不稳定的排序算法,例如 **[3, 4, 3, 1, 5]**这个数组,第一次交换,第一个3和1交换位置,此时原来两个3的相对位置发生了变化,所以无法保证值相等的元素的相对位置不变,
## **3.冒泡排序**
重复len次冒泡,每次冒泡都是**(int j=len-1; j>i; j--)**从数组末尾往前进行冒泡,如果**arr[j-1]>arr[j]**,则**将arr[j]与arr[j-1]**交换,让值小的冒泡上去.
代码如下所示:
template < typename T >
static void bubbleSort(T arr[], int len, bool ascend = true)
{
bool isSwap = true;
for(int i=0; (i<len) && isSwap; i++) // 每次冒泡时,则判断上次是否发生过交换,如果交换过,那说明还不是个有序的表
{
isSwap = false; // 每次冒泡的时候复位 “交换标志位”
for(int j=len-1; j>i; j--)
{
if( ascend ? ( arr[j-1] > arr[j] ) : (arr[j-1] < arr[j] ) ) // 如果是升序,arr[j-1] > arr[j],则将arr[j]从末尾冒泡冒上去
{
mySwap(arr[j], arr[j-1]);
isSwap = true; // “交换标志位” 设置为true
}
}
}
}
**PS:** 由于mySwap()函数和测试代码已经写过了,后面就不再重复写了。
**总结:**
冒泡排序是稳定的排序算法,因为每次都是冒泡相邻比较,比如升序,如果后者>=前者,是不会进行交换
## **4.插入排序**
将一个数组分为两个部分,**左边是排序好的,右边是未排序的.**
然后每次提取一个右边的数据出来,然后轮询左边(排序好的),如果找到合适的位置(前者小于它且后者大于它),则插入进去.如果未找到,则插入在左边排序好的末尾位置.
**比如16,8,21,10,49,由于左边默认会有一个数值(一个数值无需判断比较),所以无需判断arr[0]:**
>
> **16 | 8,21,10,49 ( |****左边是排序好的,****右边是等待排序的 )**
>
>
> **第1****次,****获取arr[1],****由于8****小于16,****插入在16****前面,****所以插入后:**
>
>
> **8,16 | 21,10,49**
>
>
> **第2****次,****获取arr[2],****向前轮询,****由于21>16,****所以停止向前遍历,** **并且位置没改变过,****所以无需操作:**
>
>
> **8,16,21 | 10,49**
>
>
> **第3****次,****获取arr[3],** **向前轮询,****由于10<21,****则21****向后挪位,****再继续向前轮询,****由于10<16,****则16****向后挪位,****最后10>=8,****则插入在8****后面:**
>
>
> **8, 10,16,21 | 49**
>
>
> **第4****次,****获取arr[4],****由于49>21,****所以停止向前遍历,** **并且位置没改变过,所以无需操作最终为:**
>
>
> **8, 10,16,21 ,49**
>
>
>
代码如下所示:
template < typename T >
static void insertSort(T arr[], int len, bool ascend = true)
{
for(int i=1; i<len; i++) // 循环取出右边未排序的
{
int k = i;
T temp = arr[i]; // 把值取出来,后面会进行挪位操作,会覆盖掉arr[i]原来的内容
// 如果ascend为true,则表示升序,那么当后者大于前者时,则停止for轮询,因为左边是有序序列
for(int j=i-1; (j>=0) && (ascend ? (arr[j]>temp) : (arr[j]<temp)); j--)
{
arr[j+1] = arr[j]; // 进行先后挪位操作
k = j; // 更新要插入的位置
}
if( k != i ) // 如果未进行挪位,那么k==i,则不需要重复插入
{
arr[k] = temp;
}
}
}
**总结:**
插入排序是稳定的排序算法,例如 **[4,3, 3, 1, 5]**这个数组,由于左边的3是提前插入的**[** 3, 4 **|** 3, 1, 5**]**,而后续的3由于>=前面的3,所以插入在后面
## **5.希尔排序**
希尔排序是插入排序的升级版.并且比插入排序快.
希尔排序则是将一个插入排序,比如初始**步长间隔gap**为3,那么将会分成3个小组进行插入排序,每个小组插入排序好后,再次将步长间隔gap较少一部分再次进行插入排序,直到最后步长偏移为1后,进行整个插入排序.一般gap步长都是以 len/3+1来计算.以后每次以 gap/3+1来缩小。
**PS: 也可以设置gap固定增量从 n/2 开始,以后每次缩小到原来的一半。**
**比如下图所示:**
![](https://img-blog.csdnimg.cn/c729e29a1d2f41c9951040ec5f0c3e30.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6K-66LCm,size_20,color_FFFFFF,t_70,g_se,x_16)
**代码如下所示****:**
template < typename T >
static void shellSort(T arr[], int len, bool ascend = true)
{
int gap = len;
while (gap > 1) {
gap = gap / 3 + 1; // 加一能够保证gap最后一次为1.
for(int offset = 0 ; offset < gap; ++offset) { // 对每组进行插入排序
for(int i=gap-offset; i<len; i+=gap) // 循环取出右边未排序的
{
int k = i;
T temp = arr[i]; // 把值取出来,后面会进行挪位操作,会覆盖掉arr[i]原来的内容
// 假如ascend为true,则表示升序,那么当后者大于前者时,则停止for轮询
for(int j=i-gap; (j>=0) && (ascend ? (arr[j]>temp) : (arr[j]<temp)); j-=gap)
{
arr[j+gap] = arr[j]; // 进行先后挪位操作
k = j; // 更新要插入的位置
}
if( k != i ) // 如果未进行挪位,那么k==i,则不需要重复插入
{
arr[k] = temp;
}
}
}
}
}
**总结:**
希尔排序是不稳定的排序算法,因为希尔排序通过分组方法,可能会将最后的值跳过中间重复的值排序到前面
## **6.归并排序**
归并是将两个或多个有序序列合并成一个新的有序序列。归并方法有多种,一次对两个有序记录序列进行归并,称为**二路归并排序**,也有**三路归并排序**及**多路归并排序**。本代码是**二路归并排序**.
**思路如下所示:**
通过递归的方式将一个无序序列,对半拆解、直到不能拆解时,则进行两路比较排序(归并)到一个辅助空间数组中,归并完成后则将辅助数组中内容拷贝回去,返回上一级递归再进行两路比较排序(归并)和拷贝.直到最后则整个序列归并完成.
**如下图所示(红色数字表示递归拆解和归并的先后顺序):**
![](https://img-blog.csdnimg.cn/f962265eaffb49b48f1a90c20819c959.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6K-66LCm,size_20,color_FFFFFF,t_70,g_se,x_16)
代码如下所示:
template < typename T >
static void merge(T arr[], int l, int mid, int r, bool ascend = true) // 进行比较归并
{
int n = r - l + 1;
T* helper = new T[n]; // 创建一个辅助空间,用来临时存储
int i = 0;
int left = l;
int right = mid+1;
while (left <= mid && right <= r) {
// 如果ascend = true, 则判断左边如果大于右边的,则存放右边的
if (ascend ? arr[left] > arr[right] : arr[left] < arr[right])
helper[i++] = arr[right++];
else
helper[i++] = arr[left++];
}
// 将某一侧剩余的存放进去
while (left <= mid)
helper[i++] = arr[left++];
while (right <= r)
helper[i++] = arr[right++];
// 最后将排序好的替换到原数组
for (i = 0; i < n; ++i) {
arr[l + i] = helper[i];
}
delete [] helper;
}
template < typename T >
static void mergeSort(T arr[], int l, int r, bool ascend = true) // 递归拆解
{
if (l >= r) // 不能再进行拆解了,直接退出
return;
int mid = (l+r)/2;
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
(l+r)/2;
[外链图片转存中…(img-2an5SLWS-1715544750615)]
[外链图片转存中…(img-qBUQ3G1i-1715544750616)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新