经典排序算法

经典排序算法

1. 冒泡排序

  1. 元素进行两两比较,若第一个比第二个大,则交换它们的值;
  2. 执行比较和交换(n-1次),直到到达数组的最后一个元素,冒出的元素则为数组中的最大值;
  3. 重复1,2步骤

在这里插入图片描述

//进行冒泡操作
void Bubble(int arr[],  int n)
{
	int temp;
	for(int i = 0; i < n-1; i++)
	{
		if(arr[i] > arr[i+1])
			{
				temp = arr[i];
				arr[i] = arr[i+1];
				arr[i+1] = temp;
			}
	}
}
//冒泡排序
void BubbleSort(int arr[], int n)
{
	for(int i = n; i > 0; i--)
	{
		Bubble(arr,  i);
	}
}

2.选择排序

  1. 先在数据中找出最大或最小的元素,放到序列的起始;
  2. 然后再从余下的数据中继续寻找最大或最小的元素,依次放到排序序列中,直到所有数据样本排序完成

在这里插入图片描述

// 找到最大值所在的位置
int findmax_Pos(arr[], int n)
{
	int pos = 0;
	int max = arr[0];
	for(int i = 0; i < n; i++)
	{
		if(arr[i] > max)
		{
			max = arr[i];
			pos = i;
		}
		return pos;
	}
}
//选择排序
void selectSort(arr[], int n)
{
	int maxpos, temp;
	for(int i = n; i > 0; i-- )
	{
		maxpos = findmax_Pos(arr, i);
		temp = arr[maxpos];
		arr[maxpos] = arr[i-1];
		arr[i-1] = temp;
	}
}

3.插入排序

  1. 先将待排序序列的第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列;
  2. 然后从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置,直到所有数据都完成排序;如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。

在这里插入图片描述

void InsertSort(int a[], int n)
{
    for (int j = 1; j < n; j++)
    {
        int key = a[j]; //待排序第一个元素
        int i = j - 1;  //代表已经排过序的元素最后一个索引数
        while (i >= 0 && key < a[i])
        {
            //从后向前逐个比较已经排序过数组,如果比它小,则把后者用前者代替,
            //其实说白了就是数组逐个后移动一位,为找到合适的位置时候便于Key的插入
            a[i + 1] = a[i];
            i--;
        }
        a[i + 1] = key;//找到合适的位置了,赋值,在i索引的后面设置key值。
    }
}

4.希尔排序

希尔排序,也称递减增量排序算法,实质是分组插入排序。由 Donald Shell 于1959年提出。希尔排序是非稳定排序算法。

  1. 先将整个数据序列分割成若干子序列分别进行直接插入排序;
  2. 待整个序列中的记录基本有序时,再对全部数据进行依次直接插入排序
void Shell_Sort(int arr[], int n)
{
	for(int gap = n/2; gap>0; gap /= 2)
	{
		for(int i = 0; i < gap; i++)
		{
			for(int j = i+gap; j < n; j +=gap)
			{
				//对每个分组进行插入排序
				if(arr[j-gap]>arr[j])
				{
					int temp = a[j];
					k = j - gap;
					while (k>=0 && a[k]>temp)
					{
						a[k+gap] = a[k];
						k -= gap;
					}
					a[k] = temp;
				}
			}
		}
	}
}

5.归并排序

归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组。

  1. 把当前数组分化成n个单位为1的子数组,然后两两比较合并成单位为2的n/2个子数组;
  2. 继续进行这个过程,按照2的倍数进行子数组的比较合并,直到最终数组有序

在这里插入图片描述

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    num = len(arr) // 2
    left = merge_sort(arr[:num])
    right = merge_sort(arr[num:])

    return merge(left, right)

def merge(left, right):
    L = 0
    R = 0
    result = []
    while L < len(left) and R < len(right):
        if left[L] < right[R]:
            result.append(left[L])
            L += 1
        else:
            result.append(right[R])
            R += 1
    result += left[L:]
    result += right[R:]

    return result

6.快速排序

快速排序也是利用分治法实现的一个排序算法。快速排序和归并排序不同,它不是一半一半的分子数组,而是选择一个基准数,把比这个数小的挪到左边,把比这个数大的移到右边。然后不断对左右两部分也执行相同步骤,直到整个数组有序。

  1. 用一个基准数将数组分成两个子数组
  2. 将大于基准数的移到右边,小于的移到左边
  3. 递归的对子数组重复执行1,2,直到整个数组有序
//left 为左起始位置, right 为右终点位置
void quickSort(int arr[], int left, int right)
{
	int i, j, t, temp, 
	if (left > right)
		return;
	temp = arr[left];    // temp 保存的为基准数,可视为最左端为基准数
	i = left;
	j = right;
	while(i < j)
	{
		//先从右边开始找
		while(arr[j] >= temp && i < j)
			j--;
		// 再从左边找
		while(arr[i] <= temp && i < j)
			i++;
		// 交换右左两数在数组中的位置
		if(i < j)
		{
			t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}
	//最终将基准数归位
	a[left] = a[i];
	a[i] = temp;

	quickSort(arr, left, i-1);  //继续处理左边的,递归方式
	quickSort(arr, i+1, right); //继续处理右边的
}

7.堆排序

堆排序经常用于求一个数组中最大k个元素时。因为堆实际上是一个完全二叉树,所以用它可以用一维数组来表示。因为最大堆的第一位总为当前堆中最大值,所以每次将最大值移除后,调整堆即可获得下一个最大值,通过一遍一遍执行这个过程就可以得到前k大元素,或者使堆有序。
首先了解在一维数组中节点的下标:

  1. i节点的父节点下标 parent(i) = floor((i-1)/2)
  2. i节点的左子节点下标 left(i) = 2i + 1
  3. i节点的右子节点下标 right(i) = 2i + 2

算法步骤:

  1. 构造最大堆(Build Max Heap): 首先将当前元素放入最大堆下一个位置,然后将此元素依次和它的父节点比较,如果大于父节点就和父节点交换,直到比较到根节点。重复执行到最后一个元素。
  2. 最大堆调整(Max Heapify): 调整最大堆即将根节点移除后重新整理堆。整理方法为将根节点和最后一个节点交换,然后把堆看做n-1长度,将当前根节点逐步移动到其应该在的位置。
  3. 堆排序(HeapSort):重复执行2,直到所有根节点都已移除。
void max_heapify(int arr[], int start, int end)
{
	int dad = start;
	int son = dad * 2 +1;   //表示左子节点
	while(son <= end)      //子节点指标在范围内才作比较
	{
		// 比较两个子节点的大小,选择最大的
		if (son + 1 < = end && arr[son] < arr[son + 1] )
		{son++;}
		// 若父节点大于子节点代表调整完毕,直接跳出函数;否则交换父子内容,后再继续子节点和孙节点的比较
		if(arr[dad] > arr[son])
		{return;}
		else{
			swap(arr[dad], arr[son]);
			dad = son;
			son = dad * 2 + 1;
		}
	}
}

//堆排序(先建立一个最大堆,然后将堆顶元素和最后一个元素交换 ,接着调整除最后一个元素的满二叉树为最大堆)
void heap_sort(int arr[], int len)
{
	//初始化, i从最后一个父节点开始调整
	for( int i = len/2 -1; i >= 0; i--)
	{
		max_heapify(arr, i, len-1);
	}
	//将堆顶元素和最后一个元素交换 ,接着调整除最后一个元素的满二叉树为最大堆,以此类推
	for(int i = len - 1; i >= 0; i-- )
	{
		swap(arr[0], arr[i]);
		max_heapify(arr, 0, i-1);
	}
}

堆排序的整个过程如下图所示:
在这里插入图片描述
在这里插入图片描述

参考资料:
我理解的堆排序
经典排序算法总结与实现 --python
七种常见经典排序算法总结(C++实现)
十大经典排序算法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值