排序学习:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序(自用

目录

 1.插入排序

2.希尔排序

3.选择排序

4.冒泡排序

5.堆排序

6.快排


1.插入排序

步骤:

1.从第一个元素开始,该元素可以认为已经被排序
2.取下一个元素tem,从已排序的元素序列从后往前扫描
3.如果该元素大于tem,则将该元素移到下一位(tem用来存储要被覆盖的元素)

4.重复步骤3,直到找到已排序元素中小于等于tem的元素
5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置
6.重复步骤2~5

void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; ++i)
	{
		//记录有序序列最后一个元素的下标
		int end = i;
		//待插入的元素
		int tem = arr[end + 1];
		//单趟排
		while (end >= 0)
		{
			//比插入的数大就向后移
			if (tem < arr[end])
			{
				arr[end + 1] = arr[end];
				end--;
			}
			//比插入的数小,跳出循环
			else
			{
				break;
			}
		}
		//tem放到比插入的数小的数的后面
		arr[end  + 1] = tem;
		//代码执行到此位置有两种情况:
		//1.待插入元素找到应插入位置(break跳出循环到此)
		//2.待插入元素比当前有序序列中的所有元素都小(while循环结束后到此)
	}
}

 时间复杂度:最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序
      最好情况下为O(N),此时待排序列为升序,或者说接近升序。
空间复杂度:O(1)

 2.希尔排序

步骤

1.先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量(一般直接/2),重复上述操作…
2.当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。

//希尔排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap>1)
	{
		//每次对gap折半操作
		gap = gap / 2;
		//单趟排序
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tem = arr[end + gap];
			while (end >= 0)
			{
				if (tem < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tem;
		}
	}
}

时间复杂度平均:O(N^1.3)
空间复杂度:O(1)

 3.选择排序

步骤:

从第一个开始,选择未排序中最小的放到未排序的最前面成为有序的一部分

要提升效率的话,在一趟选择的时候同时选出最大值和最小值将其分别放在末尾和开头 

//选择排序
void swap(int* a, int* b)
{
	int tem = *a;
	*a = *b;
	*b = tem;
}
void SelectSort(int* arr, int n)
{
	//保存参与单趟排序的第一个数和最后一个数的下标
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		//保存最大值的下标
		int maxi = begin;
		//保存最小值的下标
		int mini = begin;
		//找出最大值和最小值的下标
		for (int i = begin; i <= end; ++i)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}
		//最小值放在序列开头
		swap(&arr[mini], &arr[begin]);
		//防止最大的数在begin位置被换走
		if (begin == maxi)
		{
			maxi = mini;
		}
		//最大值放在序列结尾
		swap(&arr[maxi], &arr[end]);
		++begin;
		--end;
	}
}

 时间复杂度:最坏情况:O(N^2)
      最好情况:O(N^2)
空间复杂度:O(1)

 4.冒泡排序

步骤

左边大于右边就交换

//冒泡排序
void BubbleSort(int* arr, int n)
{
	int end = n;
	while (end)
	{
		int flag = 0;
		for (int i = 1; i < end; ++i)
		{
			if (arr[i - 1] > arr[i])
			{
				int tem = arr[i];
				arr[i] = arr[i - 1];
				arr[i - 1] = tem;
				flag = 1;
			}
		}
		if (flag == 0)
		{
			break;
		}
		--end;
	}
}

时间复杂度:最坏情况:O(N^2)
      最好情况:O(N)
空间复杂度:O(1)

 5.堆排序

a.分为大根堆(所有的父节点大于子节点)和小根堆(相反),堆可看作完全二叉树

b.关键函数:swap(交换函数),新增函数(即建立堆的函数1),直接建堆

c.父节点i 则左儿子为i*2,右儿子为i*2+1

d.还可记忆:删除堆顶元素/任意函数

swap函数:

void swap(int array[], int x, int y) {
    int key  = array[x];
    array[x] = array[y];
    array[y] = key;
}

新增元素:

     当我们需要往二叉堆中新增元素x时,我们先把x放到堆数组的末尾,然后通过向上调整的方式,使堆重新满足堆性质,新增操作的时间复杂度为O(logn)n是堆中元素个数。

int heap[N], n; //n为堆中元素个数
heap[++n] = x;
up(n);
-----up函数-----
void up(int k){ //这个例子里堆是小根堆
    while (k>1){
        if (heap[k]<heap[k/2]){
        	swap(heap[k],heap[k/2]);
	k /= 2;
        } else break;
    }
}

 删除堆顶元素:
      删除堆顶元素时,先把堆顶元素和堆数组末尾元素交换,然后让堆元素总数减1,最后把堆顶元素向下调整。时间复杂度O(logn),n是堆中元素个数。


heap[1] = heap[n--];
down(1);
-----down函数-----
void down(int k){
    int s = k*2; //取左儿子
    while (k <= n){
            if (s<n && heap[s]>heap[s+1]) s++; //如果右儿子较小,s改成右儿子
            if (heap[s]<heap[k]) {
	     swap(heap[s],heap[k]);
                  k = s; s = k*2;
            } else break;
    }
}

删除任意元素

      如果我们需要删除下标为p的元素,我们交换p和堆数组末尾的元素,此时交换后的元素即有可能需要往上调整,也有可能需要往下调整,需要分别尝试进行。

heap[p] = heap[n--];

up(p); down(p);

 根据现有的堆建立

for (int k = n / 2; k > 0; k--) { //从距离叶子最近的节点开始
        while (k * 2 < n) 
        {
            int s = k * 2;        // a[k] 的左儿子下标
            if (s<n && a[s]>a[s+1]) s++       // 选择两个儿子中较小的
            if (a[s] > a[k]) { //左右儿子都大于父节点,退出while循环,否则交换
                break;
            } 
            swap(a[s],a[k]);
            k = s;
        }
    }

另外建一个heap数组

    int m = 0;
    for (int i=0;i<n;++i){
        heap[++m] = a[i];
        up(m);
    }

6.快排

6.1  左右指针法

//快速排序   hoare版本(左右指针法)
void QuickSort(int* arr, int begin, int end)
{
	//只有一个数或区间不存在
	if (begin >= end)
		return;
	int left = begin;
	int right = end;
	//选左边为key
	int keyi = begin;
	while (begin < end)
	{
		//右边选小   等号防止和key值相等    防止顺序begin和end越界
		while (arr[end] >= arr[keyi] && begin < end)
		{
			--end;
		}
		//左边选大
		while (arr[begin] <= arr[keyi] && begin < end)
		{
			++begin;
		}
		//小的换到右边,大的换到左边
		swap(&arr[begin], &arr[end]);
	}
	swap(&arr[keyi], &arr[end]);
	keyi = end;
	//目前状况   [left,keyi-1]keyi[keyi+1,right]
	QuickSort(arr, left, keyi - 1);
	QuickSort(arr,keyi + 1,right);
}

 时间复杂度

快速排序的过程类似于二叉树其高度为logN,每层约有N个数,如下图所示:

在这里插入图片描述

6.2 挖坑发(递归)

原理与左右指针差不多

//快速排序法  挖坑法
void QuickSort1(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	int left = begin,right = end;
	int key = arr[begin];
	while (begin < end)
	{
		//找小
		while (arr[end] >= key && begin < end)
		{
			--end;
		}
		//小的放到左边的坑里
		arr[begin] = arr[end];
		//找大
		while (arr[begin] <= key && begin < end)
		{
			++begin;
		}
		//大的放到右边的坑里
		arr[end] = arr[begin];
	}
	arr[begin] = key;
	int keyi = begin;
	//[left,keyi-1]keyi[keyi+1,right]
	QuickSort1(arr, left, keyi - 1);
	QuickSort1(arr, keyi + 1, right);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值