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

Posted 逗逗飞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试系列之排序算法总结(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)

基数排序

桶排序



以上是关于面试系列之排序算法总结(C/C++版本)的主要内容,如果未能解决你的问题,请参考以下文章

面试高频算法精简总结

转白话经典算法系列之七 堆与堆排序

整理常见排序算法及其时间复杂度总结

图解算法系列之冒泡排序(优化版)

白话经典算法系列之七 堆与堆排序

图解算法系列之归并排序