一,冒泡排序
先说结论,该排序算法在实践中没有意义,但是对初学者有较好的教学意义
该算法时间复杂度为O(N*2) 空间复杂度为O(1)
最好情况下时间复杂度为O(N)(当数据原本是有序时)
代码实现
//冒泡排序 void bulleSort(int* a,int n) { for (int i = 0; i < n - 1; i++) { int falg = 1;//给一个状态值 for (int j = 0; j < n-i-1; j++) { if (a[j] > a[j+1]) { swap(a[j], a[j+1]); falg = 0;//如果初始位置不是有序的就将状态值改为0 } } if (falg == 1)//如果状态值为1则说明已经有序了 { break; } } }
二,选择排序
时间复杂度和空间复杂度与冒泡排序一样,在实际也是没有很大意义
代码实现
void selectSort(int* a, int n) { int left = 0, right = n - 1;//定义左右边界 for (int i = 1; i <= right; i++) { int max = left, min = left; for (int j = left; j <= right; j++) { if (a[min] > a[j])//找到当前范围最小值下标 { min = j; } if (a[max] < a[j])//找到当前范围最大值下标 { max = j; } } swap(a[left], a[min]);//将最小值交换到最左边 if (max == left)//如果当前最大值的下标刚好为left,此时需要更新max { max = min; } swap(a[right], a[max]);//将最大值交换到最右边 left++; right--; } }
三,插入排序
时间复杂度:O(N*2)
空间复杂度:O(1)
最好情况下:O(N)(当数组已经是有序的时候)
void insertSort(int* a, int n) { for (int i = 0; i < n - 1; i++) { int end = i; int tem = a[end + 1]; while (end >= 0) { if (tem < a[end])//如果后面的小于前面的就交换位置 { swap(a[end], a[end + 1]); --end; } else { break; } } a[end + 1] = tem;//将tem值放入对应位置 } }
四,希尔排序
时间复杂度:O(N*1.3)
空间复杂度:O(1)
原理:先进行预排序将数组尽量排成有序状态,最后再进行插入排序时就非常快,预排序的实质其实也是插入排序,只不过每次交换的位置有所不同,主包也讲不清楚,需要具体了解的阔以去看看其他优秀的博主,O(∩_∩)O哈哈~
void shellsort(int* a, int n) { int ret = n; while (ret > 1) { ret = ret / 3 + 1; //需要进行预排序,预排序实质上就是插入排序,只不过交换的位置有所不同 for (int i = 0; i < n - ret; i+=ret) { int end = i; int tem = a[end + ret]; while (end >= 0) { if (tem < a[end])//如果后面的小于前面的就交换位置 { swap(a[end], a[end + ret]); end-= ret; } else { break; } } a[end + ret] = tem;//将tem值放入对应位置 } } }
五,堆排序
时间复杂度:O(N*logN)
空间复杂度:O(1)
原理:先建堆,排升序建大堆,排降序建小堆
通过向下调整法将数组建成堆序,向下调整法是通过父亲与较大的孩子比较,如果父亲比孩子小则交换两者位置,直至向下将孩子比较完
将数组看成是多个堆,依次比较完就变成了大堆
//向下调整法 void AdjustDown(int* a, int parent,int n) { int child = parent * 2+1; while (child < n) { if (child + 1 < n && a[child] < a[child + 1])//如果右孩子大于左孩子 {//需要注意右孩子的下标不能大于等于n,否则会造成越界 child++; } if (a[parent] < a[child]) { swap(a[parent], a[child]); parent = child; child = parent * 2 + 1; } else { break; } } } void heapSort(int* a, int n) { for (int i = n/2-1; i >=0; i--)//(n-2)/2 定位到最后一个结点的父亲节点 { AdjustDown(a, i, n);//向下调整建大堆 } for (int i = n-1; i >=0; i--) { swap(a[i], a[0]); AdjustDown(a, 0, i); } }
六,归并排序
时间复杂度:O(N*logN)
空间复杂度:O(N)
原理
归并排序采用分治法:
分解:将数组递归分成两半,直到子数组长度为1。
合并:将两个有序的子数组合并成一个有序数组。
//归并排序 void mergeSort(int* a, int begin, int end) { if (begin >= end) return; int mid = (begin + end) / 2; int begin1 = begin, end1 = mid; int begin2 = mid+1, end2 = end; mergeSort(a, begin1, end1);//左 mergeSort(a, begin2, end2);//右 // 合并逻辑 int* tmp = new int[end - begin + 1]; // 临时数组存储合并结果 int i = begin1; // 左子数组指针 int j = begin2; // 右子数组指针 int k = 0; // 临时数组指针 // 比较并合并两个有序子数组 while (i <= end1 && j <= end2) { if (a[i] <= a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } // 处理剩余元素 while (i <= end1) tmp[k++] = a[i++]; while (j <= end2) tmp[k++] = a[j++]; // 将合并结果拷贝回原数组 for (int m = 0; m < k; m++) { a[begin + m] = tmp[m]; } delete[] tmp; }
七,计数排序
时间复杂度:O(N+k)
空间复杂度:O(K)
计数排序的核心思想是通过统计元素频次直接确定元素的位置,无需比较。具体步骤如下:
1. 确定数据范围
遍历数组,找到最大值
max
和最小值min
,计算范围k = max - min + 1
。2. 统计频次
创建长度为
k
的计数数组tem[]
,统计每个元素出现的次数。例如,元素 a[i]对应
tem[a[i]-min]++
。3.将取到的值按顺序复制到原数组
//计数排序 void jishuSort(int* a, int n) { //1.找到数组中的最大最小值 int max = a[0], min = a[0]; for (int i = 0; i < n; i++) { if (a[i] > max) { max = a[i]; } if (a[i] < min) { min = a[i]; } } int k = max - min + 1; int* tem = new int[k]();//创建数组并初始化为0 for (int i = 0; i < n; i++) { tem[a[i]-min]++;//a[i]-min 将a的值映射到tem; } int j = 0; for (int i = 0; i < k; i++) { while (tem[i]--) { a[j++] = i + min; } } delete[] tem; }
八,快速排序
快排实现 在主包的另一篇文章中有讲到