面试系列之排序算法总结(C/C++版本)

冒泡排序

/*
冒泡排序是一种流行但低效的算法,
它的作用是反复交换相邻的未按次序排列的元素
*/

#include <stdio.h>

void Exchange(int &a,int &b)
{
    // 使用位运算,秀一秀自己的实力
    a = a^b;
    b = a^b;
    a = a^b;
}

void BubbleSort(int a[],int len)
{
    // 为了保证代码的鲁棒性,我们首先对异常值验证
    if(a == NULL || len <= 0)
        return;
    // 冒泡排序核心代码
    for(int i = 0; i < len; i++)
    {
        for(int j = len-1; j > i; j--)
        {
            if(a[j] < a[j-1])
                exchange(a[j],a[j-1]);
        }
    }
}

时间复杂度O(n²),空间复杂度O(1)

选择排序

/* 
简单选择排序 
每次经过 n-i 次比较,从序列中选出i之后的最小元素放在第 i 个位置
*/ 
void SelectSort(int a[],int len)
{
    // 为了保证代码的鲁棒性,我们首先对异常值验证
    if(a == NULL || len <= 0)
        return;
    // 选择排序核心代码
    for(int i = 0; i < len; i++)
    {
        int min_index = i;
        for(int j = i+1; j < len; j++)
        {
            // 每轮迭代,目的是找出i后面最小值得下标
            if(a[i] > a[j])
                min_index = j;
        }
        if(min_index != i)
            exchange(a[i],a[min_index]);
    }
}

时间复杂度O(n²),空间复杂度O(1)

插入排序

/*
    插入排序
    a[0...i]有序,
    选择a[i+1]放入到 a[0...i] 合适位置,
    使a[0...i+1]有序
*/
void InsertSort(int a[], int len)
{
    for(int i = 1; i < len; i++)
    {
        int j = i-1;
        int key = a[i];
        while(j >=0 && a[j] > key)
        {
            a[j+1] = a[j];
            j--;
        }
        a[j+1] = key;
    }
}

时间复杂度O(n²),空间复杂度O(1)

快速排序

/*
快速排序通常是实际应用中最好的选择:
因为它平均性能非常好,它的期望时间复杂度是O(nlogn),而且O(nlogn)隐含常数因子非常小
另外它能够进行原址排序  
*/
int Partition(int a[], int p, int r)
{
	int i = p;
	for(int j = p; j < r; j++)
	{
		if(a[j] < a[r])
		{
			exchange(a[i],a[j]);
			i++;
		}
	}
	exchange(a[i],a[r]); 
	return i;
}

void QuickSort(int a[],int p,int r)
{
	if(p < r)
	{
		int q = Partition(a,p,r);
		QuickSort(a,p,q-1);
		QuickSort(a,q+1,r);
	}
}

快速排序最坏的时间复杂度是O(n^2),但平均时间复杂度是O(nlogn)

平均空间复杂度是O(nlogn),最坏空间复杂度是O(n)

归并排序

void merge(int a[], int p, int q, int r)
{
	int n1 = q-p+1;
	int n2 = r-q;
	int L[n1+1],R[n2+1];
	int i,j,k;
	for(i = 0; i < n1; i++)
	{
		L[i] = a[p+i];
	}
	for(i = 0; i < n2; i++)
	{
		R[i] = a[q+i+1];//因为下标是从0开始,这里应该+1
	}
	L[n1] = 0x7fffffff;
	R[n2] = 0x7fffffff;
	i = 0, j = 0;
	for(k = p; k <= r; k++)
	{
		if(L[i] <= R[j])
			a[k] = L[i++];
		else
			a[k] = R[j++];
	}
}

void mergeSort(int a[],int p,int r)
{
	if(p < r)
	{
		int q = (p+r)/2;
		mergeSort(a,p,q);
		mergeSort(a,q+1,r);
		merge(a,p,q,r);
	}
}

从这个递归树可以看出,第一层时间代价为cn,第二层时间代价为cn/2+cn/2=cn.....每一层代价都是cn,总共有logn+1层。所以总的时间代价为cn*(logn+1).时间复杂度是o(nlogn).

空间复杂度O(n)

堆排序

堆是完全二叉树的结构,因此对于一个有n个节点的堆,高度为O(logn)。

最大堆:堆中的最大元素存放在根节点的位置。

           除了根节点,其他每个节点的值最多与其父节点的值一样大。也就是任意一个子树中包含的所有节点的值都不大于树根节点的值。

堆中节点的位置编号都是确定的,根节点编号为1,每一层从左到右依次编号。由堆是完全二叉树,可以知道当堆中某个节点的编号为i时,如果这个节点有左右子树,那么左子树的节点编号为2*i,右子树的节点编号为2*i+1(当然这是在根节点编号为1的情况时)。

并且有n个节点的堆中叶子节点的编号为从n/2+1~n。因为假设节点n/2+1不是叶子节点,那么它的左子节点编号(n/2+1)*2=n+1,而节点总共只有n个。完全二叉树的叶子节点只出现在最下面两层。最下层的叶子集中在左边,倒数二层的叶子集中在右边。

维护最大堆函数MAX_HEAPWEIHU(A,i),假定节点i的左右子树已经是最大堆。那么维护堆时,先比较i节点的值与左右节点值的大小,将三个数中的最大值交换到根节点的位置。假设根节点i与左子节点的值交换了,那么左子树就要再次调用MAX_HEAPWEIHU(A,2*i),判断左子树还是不是最大堆,如果是则结束,否则继续调用进行维护。因此调用MAX_HEAPWEIHU(A,i)的时间复杂度为O(logn)

void heapfy(int a[],int index,int heapsize)
{
	int left = index*2;
	int right = index*2+1;
	int largest = index;
	if(left < heapsize && a[index] < a[left])
	{
		largest = left;
	}
	if(right < heapsize && a[largest] < a[right])
	{
		largest = right;
	}
	if(largest != index)
	{
		swap(a[index],a[largest]);
		heapfy(a,largest,heapsize);
	}
}

void heapSort(int a[],int len)
{
	for(int i = len/2-1; i>=0; i--)
	{
		heapfy(a,i,len);
	}
	for(int i = len-1; i>=0; i--)
	{
		swap(a[i],a[0]);
		heapfy(a,0,i);
	}
}

计数排序

输入数组a[],输出数据存放在b[],临时存储空间c[0...k]

初始化数组c全被设置为0,遍历一遍输入数组a[],如果一个元素的值为i,就将c[i]的值+1;

于是此时的c[i]保存的就是等于i的元素个数,其中i= 0...k

代码中

c[i] = c[i] + c[i-1];
表示多少元素是小于等于i的

void CountingSort(int a[], int *b)
{
	int c[13] = {-1};
	for(int i = 0; i < 10; i++)
		c[a[i]]++;
	for(int i = 1; i < 13; i++)
		c[i] = c[i] + c[i-1];
	printf("hello world\n");
	for(int i = 9; i >=0; i--)
	{
		#printf("a[%d] = %d\t,b[%d] = %d\t,c[%d] = %d\n",i,a[i],i,b[i],i,c[a[i]]);
		b[c[a[i]]] = a[i];
		c[a[i]] = c[a[i]] -1;
	}
} 
时间复杂度是O(k+n),实际应用中档k = O(n)时,一般会采用技术排序,总时间复杂度是O(n)

基数排序

桶排序



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值