算法设计与分析-----排序算法

一、选择排序

  1. 插入排序的最坏和平均时间复杂度均为\frac{n(n-1)}{2}=o(n^2))
#include<iostream>
using namespace  std;
//实现选择排序
//最坏情况和平均情况时间复杂度均为O(n^2)
//原地排序,空间复杂度为O(1)
int main()
{
	int arr[10] = { 23, 6, 1, 6, 45, 20, 15, 36, 5, 24 };
	int n = sizeof(arr)/sizeof(int);
	for (int i = 0; i < n; i++)
	{
		int max = arr[0];
		int index = 0;
		for (int j = 1; j < n - i; j++)
		{
			if (max < arr[j])
			{
				max = arr[j];
				index = j;
			}
		}
		swap(arr[index], arr[n-i-1]);
	}
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	return 0;
}

二、插入排序

  1. 最坏情况的时间复杂度与选择排序相同(对一个从大到小的数组进行从小到大的排序)
  2. 平均情况时间复杂度比选择排序的代价大约少一半(\frac {n^{2}}{4}+\frac{3n}{4}-1 =O(n^2))
#include<iostream>
using namespace std;
//插入排序
int main()
{
	int arr[10] = { 23, 6, 1, 6, 45, 20, 15, 36, 5, 24 };
	int n = sizeof(arr) / sizeof(int);
	for (int i = 1; i < n; i++) //第一个元素已经被作为基准,故从第二个元素开始
	{
		int temp = arr[i];
		int j = i - 1;
		if (temp < arr[j])
		{
			while (j >= 0 && temp < arr[j])
			{
				arr[j + 1] = arr[j];
				j--;
			}
			arr[j + 1] = temp;
		}

	}
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	return 0;

三、快速排序

  1. 最坏情况的时间复杂度也为O(n),即对一个有序的数组进行排序,需要进行N次PARTITION,每次PARTITION的时间复杂度是O(n)
  2. 平均时间复杂度为。

        时间复杂度递归方程满足:A(n)=2A(n/2)+O(n)O(nlogn)由Master定理,可得A(n)=\theta(nlogn)

#include<iostream>
using namespace std;
//快速排序
int  PARTITION(int a[], int l, int r)
{
	int pivot = a[l];//基准元素
	while (l < r)
	{
		/*
		由于我们是一次一次地进行元素位置调整,故不会发生
		元素覆盖
		*/
		//整理基准元素的右边元素
		while (l<r && a[r]>=pivot)
		{
			r--;
		}
		a[l] = a[r];
		//整理基准元素的左边元素
		while (l<r && a[l]<=pivot)
		{
			l++;
		}
		a[r] = a[l];
	}
	//找到基准元素正确的位置后放入
	a[r] = pivot;
	return r;
}
void Qick_sort(int a[], int l, int r)
{
	if (l < r)
	{
		int pivot = PARTITION(a ,l, r);
		//左递归
		Qick_sort(a, l, pivot - 1);
		//右递归
		Qick_sort(a, pivot + 1, r);
	}
}
int main() 
{
	int arr[10] = { 23, 6, 1, 6, 45, 20, 15, 36, 5, 24 };
	int n = sizeof(arr) / sizeof(int);
	Qick_sort(arr, 0, n - 1);
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	return 0;
}

四、合并排序

在快速排序的基础上,递归的进行排序,不在规定基准元素pivot,而是直接等半划分,无论是最坏还是平均的时间复杂度均为O(nlogn),但牺牲了O(n)的空间复杂度。

五、堆排序

堆的定义

  1. 堆结构特性:堆节点是一颗完全二叉树。
  2. 堆偏序特性:堆节点中存储的元素满足父节点的值大于所有子节点。

堆的修复

  1. 在堆顶元素被取走后,先将堆底元素放入堆顶元素,后进行浮动修复。
  2. 堆修复的时间复杂度代价为O(logn)

堆的构建

  1. 堆构建的时间复杂度代价为O(nlogn)
#include <iostream>
#include <algorithm>
using namespace std;

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++;//成为为右节点
        //如果父节点大于子节点中的最大值代表满足堆偏序关系,直接while循环
        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) {
    //构建一个初始堆,从最后一个开始,保证了子节点的一定满足堆偏序特性。
    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);
    }
}
int main() {
    int arr[] = { 23, 6, 1, 6, 45, 20, 15, 36, 5, 24 };
    int len = (int)sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    for (int i = 0; i < len; i++)
        cout << arr[i] << ' ';
    cout << endl;
    return 0;
}

时间复杂度分析

  1. 最坏情况时间复杂度和平均时间复杂度均为O(nlogn)

六、基于比较排序的排序算法下界

  1. 比较排序的最坏情况时间复杂度的下界为\Omega(nlogn)
  2. 比较排序的平均情况时间复杂度的下界为\Omega(nlogn)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值