1、插入排序
主要思想:从第一个元素开始往后走,只不过每次比较从后往前比较。
具体实现:记录走到的元素的值,给前面排好序的元素比较,遇见大的向后搬移,直到遇见小的,将该值放在小值的后面
void InsertSort(int array[], int size)
{
for (int i = 0; i < size; i++){
int last = array[i];
int j;
for (j = i - 1; j >= 0; j--){
if (array[j] < last){
break;
}
array[j + 1] = array[j];
}
array[j + 1] = last;
}
}
2、希尔排序
主要思想:将元素按gap = (gap / 3) + 1(gap = size)的分配规则,分成gap份,每次将隔gap大小的元素进行比较排序
具体实现:在直接插入排序的基础上,只不过每次给和gap相等数的元素个数,直到gap等于1
void InsertSortWithGap(int *array, int size, int gap)
{
for(int i = gap; i < size; i++){
int last = array[i];
int j;
for(j = i - gap; j >= 0; j -= gap){
if(array[j] < last){
break;
}
array[j + gap] = array[j];
}
array[j + gap] = last;
}
}
void ShellSort(int *array, int size)
{
int gap = size;
while(gap != 1){
gap = (gap / 3) + 1;
InsertSortWithGap(array, size ,gap);
}
}
3、冒泡排序
主要思想:size个数只需要比较size-1次,每次将大的数向后移
具体实现:两两相比,将最大的数往后移,循环一次,移动次数减一(因为后面的数已经是大数)
void BubbleSort(int *array, int size)
{
for(int i = 1; i < size; i++){
int count = 0;
for(int j = 0; j < size - i; j++){
if(array[j] > array[j + 1]){
int tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
count++;
}
}
if(count == 0){
break;
}
}
}
4、选择排序(找最大的)
主要思想:每次选元素中的最大值,将其放在最后
具体实现:假设一开始下标0是最大数的下标(max = 0),依次以后面的数进行比较,遇到大于其值的数时,将下标赋值给max,最后将最大值与size-i位置的数进行交换
void SelectSort(int *array, int size)
{
for(int i = 1; i < size; i++){
int max = 0;
for(int j = 1; j <= size -i; j++){
if(array[j] >= array[max]){
max = j;
}
}
//最大数在max下标下
int tmp = array[max];
array[max] = array[size - i];
array[size - i] = tmp;
}
}
升级版的选择排序(每次选出最大的和最小的)
void SelectSortOP(int *array, int size)
{
int minSpace = 0;
int maxSpace = size - 1;
while(minSpace < maxSpace){
int min = minSpace;
int max = minSpace;
for(int i = minSpace + 1; i <= maxSpace; i++){
if(array[i] < array[min]){
min = i;
}
if(array[i] > array[max]){
max = i;
}
}
int tmp1 = array[min];
array[min] = array[minSpace];
array[minSpace] = tmp1;
if(max = minSpace){
max = min;
}
int tmp2 = array[max];
array[max] = array[maxSpace];
array[maxSpace] = tmp2;
minSpace++;
maxSpace--;
}
}
5.堆排序
//root表示要开始调整的结点下标
//建大堆
void AdjustDown(int *array, int size, int root)
{
int left = 2 * root + 1;
int right = 2 * root + 2;
while (left < size){
//找两个孩子中大的一个
//只有一种情况,大的一个是右孩子
//有右孩子并且右孩子的值大于左孩子的值
int max = left;
if (right < size && array[right]>array[left]){
max = right;
}
if (array[root] >= array[max]){
break;
}
int tmp = array[root];
array[root] = array[max];
array[max] = tmp;
root = max;
left = 2 * root + 1;
right = 2 * root + 2;
}
}
//建堆
void CreateHeap(int *array, int size)
{
//从最后一个非叶子结点开始向下调整
for (int i = (size - 2) / 2; i >= 0; i--){
AdjustDown(array, size, i);
}
}
//堆排序
//时间复杂度O(n*logn)
//空间复杂度O(1)
//不稳定
//数据不敏感
void HeapSort(int *array, int size)
{
//建大堆
CreateHeap(array, size);
//选择
for (int i = 1; i < size; i++){
int tmp = array[0];
array[0] = array[size - i];
array[size - i] = tmp;
AdjustDown(array, size - i, 0);
}
}
6.快速排序
//快速排序
void Swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
//如何把小的放左、大的放右
//方法:1.hover
//不能保证left一定是0
int Partition_1(int array[], int left, int right) {
int begin = left; //不要写成begin = 0;
int end = right; //end不能是right - 1,反例{1, 2, 3, 4}
while (begin < right&& end > left){
//基准值在右边,先走左边
//否则反例{1, 7, 8, 4}
while (begin < right && array[begin] <= array[right]){
begin++;
}
//意味着array[begin] > array[right]
//{7,8,4}
while (end > begin && array[end] >= array[right]){
end--;
}
//意味着array[end] < array[right]
Swap(array + begin, array + end);
}
//意味着区间本分为3份,分别是{小,大,基准值}
Swap(array + begin, array + right);
//返回当前基准值所在下标
return begin;
}
//方法2:挖坑
int Partition_2(int array[], int left, int right) {
int begin = left;
int end = right;
int pivot = array[right];
//{1,4,2,3}
while (begin < right && end > left){
while (begin < right && array[begin] <= pivot){
begin++;
}
array[end] = array[begin];
while (end > begin && array[end] >= pivot){
end--;
}
array[begin] = array[end];
}
array[begin] = pivot;
return begin;
}
//方法3:前后下标
int Partition_3(int array[], int left, int right) {
int div = left; //div记录第一个比基准值大的数的下标
for (int cur = left; cur < right; cur++){
if (array[cur] < array[right]){
//遇到有小于基准值的将小于基准值的数和div位置的值进行交换
//div向后移一位
Swap(array + cur, array + div);
div++;
}
}
Swap(array + div, array + right);
return div;
}
//递归的方法
void _QuickSort(int array[], int left, int right) {
//区间内只有一个数和区间内没有数
if (left == right || left > right){
return;
}
//基准值是array[right]
int div; //用来保存最终基准值所在的下标
div = Partition_1(array, left, right); //遍历array[left, right]
//把小的放左,大的放右
//返回最后基准值所在的下标
//区间别分成三份:
//[left, div - 1]比基准值小
//[div ,div]基准值 已经在最终位置
//[div + 1, right]比基准值大
_QuickSort(array, left, div - 1);
_QuickSort(array, div + 1, right);
}
//非递归的方法,用栈实现
//基本上所有的递归都可以使用栈来实现
void QuickSortNor(int array[], int size)
{
std::stack stack;
stack.push(size - 1); //right
stack.push(0); //left
//判断栈中是否还有需要排序的范围
while (!stack.empty()){
int left = stack.top(); stack.pop();
int right = stack.top(); stack.pop();
//left>=right说明调用栈中的区间中没有元素或者元素只有一个
if (left >= right){
continue;
}
else{
int div = Partition_1(array, left, right);
//圧栈,根据你要先排序的顺序,先压右半部分,再压左半部分
//排序顺序就是先左后右
//[div+1,right]
stack.push(right);
stack.push(div + 1);
//[left,div-1]
stack.push(div - 1);
stack.push(left);
}
}
}
//步骤:
// 1.选基准值
// 1>选最右(左)
// 2>三数取中:从最右、最左和中间这三个元素中随机选取一个作为基准值
// 3>随机:从数组中随机选取
// 2.Partition
// 1>Hover 2>挖坑 3>前后下标
// 3.分治算法,直到size <= 1
// 1>递归 2>非递归
//时间复杂度:O(n) * 二叉树的高度
//空间复杂度:二叉树的高度(O(log(n)) ~ O(n))
//稳定性: 不稳定
//快速排序
void QuickSort(int array[], int size)
{
_QuickSort(array, 0, size - 1);
//QuickSortNor(array, size);
}
7.归并排序
//合并两个有序数组
void Merge(int array[], int left, int mid, int right, int *extra)
{
int size = right - left;
int left_index = left;
int right_index = mid;
int extra_index = 0;
//判断左右元素的大小,将其按从小到大的顺序放在新空间extra中
while (left_index < mid && right_index < right){
if (array[left_index] <= array[right_index]){
extra[extra_index] = array[left_index];
left_index++;
}
else{
extra[extra_index] = array[right_index];
right_index++;
}
extra_index++;
}
//将剩余元素直接连接到extra的后面
while (left_index < mid){
extra[extra_index++] = array[left_index++];
}
while (right_index < right){
extra[extra_index++] = array[right_index++];
}
//将extra中有序的元素,还原到原来数组中
for (int i = 0; i < size; i++){
array[left + i] = extra[i];
}
free(extra);
}
void _MergeSort(int array[], int left, int right, int* extra)
{
//终止条件
if (right <= left + 1){
//区间内的数是小于等于1的
return;
}
int mid = left + (right - left) / 2;
//[left, mid) [mid, right)
_MergeSort(array, left, mid, extra);
_MergeSort(array, mid, right, extra);
//左右区间都有序
Merge(array, left, mid, right, extra);
}
//时间复杂度:最好|平均|最好 O(n*log(n))
//空间复杂度:O(n)
//稳定性:稳定的
//外部排序(支持大数据排序)
//归并排序(递归)
void MergeSort(int array[], int size)
{
int *extra = (int *)malloc(sizeof(int)*size);
_MergeSort(array, 0, size, extra);
free(extra);
}
//非递归
void MergeSortNor(int array[], int size)
{
int *extra = (int *)malloc(sizeof(int) * size);
for (int i = 1; i < size; i = i * 2){
for (int j = 0; j < size; j = j + 2 * i){
int left, mid, right;
left = j;
mid = j + i;
right = mid + i;
//走到最后mid已经是数组的大小
if (mid >= size){
//没有右边区间[mid, right)
continue;
}
//最后一次排序的的有区间的范围应该是[mid, size)
if (right > size){
right = size;
}
Merge(array, left, mid, right, extra);
}
}
free(extra);
}
七大排序相互比较: