首页 > 编程语言 >排序算法

排序算法

时间:2023-07-31 21:11:39浏览次数:34  
标签:arr int max len 算法 排序 TYPE

时间复杂度:

由于计算机的性能不同,无法准确地确定一个算法的执行时间

因此使用执行算法的次数来代表算法的时间复杂度

一般用O(公式)来表示

空间复杂度:

执行一个程序(算法)所需要的内存空间的大小,是对一个算法在运行过程中临时占用存储空间大小的衡量

通常来说,只要这个算法不涉及动态分配内存以及递归,通常空间复杂度为O(1)

注意:一个算法的时间复杂度、空间复杂度往往是相互影响的,如何选择根据实际情况

排序算法的稳定性:

在待排序的数据中,对于数值相同的数据,在整个排序过程中如果不会改变他们原来的先后顺序,则认为该排序算法是稳定的

冒泡排序:

数据左右比较,把较大的数据交换到右边,往后重复以上操作,直到把最大的数据交换到最后,特点是该算法对数据的有序性敏感,如果在一次的排序过程中没有发生一次交换,那么就意味着数据已经有序,可以立即停止排序

特点:适合待排序的数据基本有序时,则冒泡的效率非常高    时间复杂度:平均O(N^2) 最优O(N)    稳定的

void bubble_sort(TYPE* arr,size_t len)
{
	bool flag = true;
	for(int i=len-1; i>0 && flag; i--)
	{
		flag = false;
		for(int j=0; j<i; j++)
		{
			if(arr[j] > arr[j+1])
			{
				SWAP(arr[j],arr[j+1]);	
				flag = true;
			}
		}
	}
}

选择排序:
假定最开始的位置是最小值并记录下标min,然后与后面所有的数据比较,如果有比min位置的值更小的数,那么更新min,结束后min的下标与开始时发生过改变,才进行交换到开始位置
特点:虽然时间复杂度较高,但是数据交换次数比较少,因此实际的运行速度并不慢(数据交换比数据比较耗时)    时间复杂度:O(N^2)    不稳定的

void selection_sort(TYPE* arr,size_t len)
{
	for(int i=0; i<len-1; i++)
	{
		int min = i;
		for(int j=i+1; j<len; j++)
		{
			if(arr[j] < arr[min]) min = j;	
		}
		if(i != min) SWAP(arr[i],arr[min]);
	}
}

插入排序:

把数据看成两个部分,前部分是有序的,把剩余部分的数据逐个往前比较,如果比前面的数小,前面的数往后移动一位,继续往前比较,直到遇到更小的数,那么该数据的后一个位置即为可以插入的位置

特点:适合对已经排序后的数据,新增数据后再排序    时间复杂度:O(N^2)    稳定的

void insertion_sort(TYPE* arr,size_t len)
{
	for(int i=1,j=0; i<len; i++)
	{
		int val = arr[i];	//	要插入的数据
		for(j=i; j>0 && arr[j-1] > val; j--)//j表示要插入的位置
		{
			arr[j] = arr[j-1];
		}
		if(j!=i) arr[j] = val;
	}
}

希尔排序:

是插入排序的增强版,由于插入排序时数据移动的步长较短,都是1,所以增加了增量的概念,通过不停地缩减增量,最终步长还是变回1,可以提高排序效率

特点:当待排序数据远离最终位置时,希尔排序的效率高于插入排序    时间复杂度:O(NlogN)~O(N^2)    不稳定

void shell_sort(TYPE* arr,size_t len)
{
	for(int k=len/2; k>0; k/=2)	//k增量
	{
		for(int i=k,j=0; i<len; i++)
		{
			int val = arr[i];
			for(j=i; j-k>=0 && arr[j-k] > val; j-=k)
			{
				arr[j] = arr[j-k];	
			}
			if(j != i) arr[j] = val;
		}
	}
}

快速排序:
在待排序数据中先找一个标杆位置p,备份p位置的值val,记录左标杆l、右标杆r,l从p的左边找比val大的数,找到后赋值给p位置,更新p到l,然后r从p的右边找比val小的数,找到后赋值给p位置,更新p到r,循环往复,直到l和r重合,把val还原回p位置完成一次快排,然后用同样的方式对p左右两边的数据进行快排
特点:它的综合性能最优,因此得名快速排序,笔试时考的最多    时间复杂度:O(NlogN)    不稳定

void _quick(TYPE* arr,int left,int right)
{
	if(left >= right) return;
	int l = left, r = right, p = left;
	//	备份标杆的值
	int val = arr[p];
	while(l < r)
	{
		//	在p右边找比val小的数
		while(r > p && arr[r] >= val) r--;
		//	r在p的右边找到比val小的数
		if(r > p)
		{
			arr[p] = arr[r];
			p = r;
		}

		while(l < p && arr[l] <= val) l++;
		if(l < p)
		{
			arr[p] = arr[l];
			p = l;
		}
	}
	//	还原标杆的值
	arr[p] = val;
	_quick(arr,left,p-1);
	_quick(arr,p+1,right);
}

//	快速排序
void quick_sort(TYPE* arr,size_t len)
{
	printf("%s:\n",__func__);
	show(arr,LEN);
	_quick(arr,0,len-1);
}

堆排序:
把待排序数据当做完全二叉树看待,先把二叉树调整成大顶堆结构,把根节点的值与末尾节点的值交换,然后数量范围-1,重新从上到下调整回大顶堆,继续以上操作,直到数量为1结束,此时全部数据就从小到大排序了
特点:既可以顺序实现,也可以递归实现    时间复杂度:O(N*logN)    不稳定

void heap_sort(int* arr,int len)
{
    if(arr == NULL)return;
    // 把数组调整成堆结构
    for(int i = 1;i < len;i++)
    {
        int number = i+1;
        while(number/2 >= 1)
        {
            if(arr[number-1] > arr[number/2-1])
            {
                SWAP(arr[number-1],arr[number/2-1]);
                number /= 2;
                continue;
            }
            break;
        }
    }

    // 堆顶交换到末尾 从堆顶到末尾-1 调整回堆 直到完成
    int cnt = len;
    for(int i = 0;i < len-1;i++)
    {
        SWAP(arr[0],arr[len-i-1]);
        cnt--;
        int n = 1;
        while(n-1 < cnt)
        {
            // 有左右子树
            if(n*2 < cnt)
            {
                // 左比右大 且比根大
                if(arr[n*2-1] >= arr[n*2] && arr[n*2-1] > arr[n-1])
                {
                    SWAP(arr[n-1],arr[n*2-1]);
                    n *= 2;
                }
                else if(arr[n*2] > arr[n*2-1] && arr[n*2] > arr[n-1])
                {
                    SWAP(arr[n-1],arr[n*2]);
                    n = n*2+1;
                }
                else break;
            }
            else if(n*2 - 1 < cnt)
            {
                if(arr[n*2-1] > arr[n-1])
                {
                    SWAP(arr[n-1],arr[n*2-1]);
                    n = n*2;
                }
                else 
                    break;
            }
            else
                break;
        }
    }
}
// 对从top下标到end下标进行调整成堆
void _heap_sort(int* arr,int top,int end)
{
    if(top >= end)return;

    int max = top+1; // 根左右中的最大值编号
    int l = max * 2; // 左子树编号
    int r = max * 2 + 1;// 右子树编号
    if(l - 1 <= end && arr[l-1] > arr[max-1])
        max = l;
    if(r - 1 <= end && arr[r-1] > arr[max-1])
        max = r;
    if(max != top + 1)
    {
        // 最大值不是根
        SWAP(arr[top],arr[max-1]);
        _heap_sort(arr,max-1,end);
    }    
}

// 递归实现堆排序
void heap_sort_recursion(int* arr,int len)
{
    for(int i = 1;i < len;i++)
    {
        int number = i+1;
        while(number/2 >= 1)
        {
            if(arr[number-1] > arr[number/2-1])
            {
                SWAP(arr[number-1],arr[number/2-1]);
                number /= 2;
                continue;
            }
            break;
        }
    }
    for(int i = len-1;i > 0;i--)
    {
        SWAP(arr[0],arr[i]);
        _heap_sort(arr,0,i-1);
    }
}

归并排序:
首先需要把数据拆分成单独的个体,然后按照从小到大的顺序比较后排序到一段临时空间中,把排序后的临时空间中的数据拷贝回原内存,然后依次把有序的个体继续以上操作合并成更大的有序部分
特点:归并排序不需要交换数据,减少了交换的耗时,但是需要额外的存储空间,是一种典型的以空间换时间的算法
时间复杂度:O(N*logN)     稳定的

//	合并 l左部分最左 p左部分最右 p+1右部分最左 r右部分最右 
void __merge(TYPE* arr,TYPE* temp,int l,int p,int r)
{
	//	合并前 左右部分各自有序
	if(arr[p] <= arr[p+1]) return;

	int s = l;
	int i = l, j = p+1;
	while(i<=p && j<=r)
	{
		//	左右部分从开头进行比较
		if(arr[i] <= arr[j])	// <=  确保是稳定的
			temp[s++] = arr[i++];
		else
			temp[s++] = arr[j++];
	}
	//	比完后,把多的部分剩余的数据依次存入temp
	while(i<=p) temp[s++] = arr[i++];
	while(j<=r) temp[s++] = arr[j++];
	//	把temp还原回对应位置的arr
	memcpy(arr+l,temp+l,sizeof(TYPE)*(r-l+1));
}

//	拆分
void _merge(TYPE* arr,TYPE* temp,int l,int r)
{
	if(l >= r) return;
	int p = (l+r)/2;
	_merge(arr,temp,l,p);
	_merge(arr,temp,p+1,r);
	//	合并
	__merge(arr,temp,l,p,r);
}

//	归并排序
void merge_sort(TYPE* arr,size_t len)
{
	printf("%s:\n",__func__);
	show(arr,LEN);
	
	//	申请临时存储空间
	TYPE* temp = malloc(sizeof(TYPE)*len);
	_merge(arr,temp,0,len-1);
	free(temp);
}

计数排序:
找出待排序数据中的最大、最小值,创建长度为
最大值-最小值+1 的哈希表,根据哈希函数 数据-最小值 当做哈希表的下标并对所有数据做标记
然后从头遍历哈希表,当某个位置的值大于0,把该位置的下标+最小值的到结果依次放回原数据内存中
是一种典型的以空间换时间的算法
理论上该算法的速度是非常快的,它不是基于比较的排序算法,在一定范围内的整数排序中快于任意一种基于比较的排序算法,但是有很大的局限性,只适合整型数据排序,数据的差值不宜太大,否则会非常浪费内存
特点:反之数据差值不大、重复数比较多的情况下性价比很高    时间复杂度:Ο(n+k)(其中k是整数的范围)    稳定的

//	计数排序
void count_sort(TYPE* arr,size_t len)
{
	printf("%s:\n",__func__);
	show(arr,LEN);

	TYPE max = arr[0],min = arr[0];
	for(int i=1; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];
		if(arr[i] < min) min = arr[i];
	}

	TYPE* temp = calloc(sizeof(TYPE),max-min+1);
	for(int i=0; i<len; i++)
	{
		temp[arr[i]-min]++;	
	}
	for(int i=0,j=0; i<max-min+1; i++)
	{
		while(temp[i]--) arr[j++] = i+min; 	
	}
	free(temp);
}

桶排序:
一般是把数据根据数值分到不同的"桶",通过不同的、合适的其他排序算法对"桶"中的数据进行排序,然后再把各个"桶"的数据依次拷贝回原内存中
桶排序是一种降低排序规模的排序思想,也是以空间换时间的算法
缺点:如何分桶、桶如何定义,都需要针对具体待排序的数据进行分析后才能确定
桶排序的稳定性取决于桶内排序使用的算法
有些资料上认为桶排序可以做到稳定,所以认为桶排序稳定

// cnt桶数 range桶中数据的差值	
void _bucket(TYPE* arr,size_t len,int cnt,TYPE range)
{
	//	申请桶内存
	//	bucket_s指向桶的开头位置
	//	bucket_e指向桶的末尾 接下去要入桶的位置
	TYPE* bucket_s[cnt],*bucket_e[cnt];
	for(int i=0; i<cnt; i++)
	{
		//	数据有可能全在一个桶内
		bucket_s[i] = malloc(sizeof(TYPE)*len);
		bucket_e[i] = bucket_s[i];
	}

	//	把数据按照对应的范围放入对应的桶中
	for(int i=0; i<len; i++)
	{
		for(int j=0; j<cnt; j++)
		{
			if(j*range <= arr[i] && arr[i] < (j+1)*range)
			{
				*(bucket_e[j]) = arr[i];
				bucket_e[j] += 1;
				break;
			}
		}
	}

	//	通过其他排序对各个桶中的数据排序
	for(int i=0; i<cnt; i++)
	{
		//	计算桶中元素的数量
		int size = bucket_e[i] - bucket_s[i];
		//	其他排序
		if(1 < size)
			bubble_sort(bucket_s[i],size);
		//	按照先后顺序 放入原内存中
		memcpy(arr,bucket_s[i],size*sizeof(TYPE));
		arr += size;
		free(bucket_s[i]);
	}
}

//	桶排序
void bucket_sort(TYPE* arr,size_t len)
{
	printf("%s:\n",__func__);
	show(arr,LEN);
	_bucket(arr,len,4,25);
}

基数排序:

void radix_sort(TYPE* arr,size_t len)
{
	printf("%s:\n",__func__);
	show(arr,LEN);

	ListQueue* queue[10] = {};
	for(int i=0; i<10; i++)
	{
		queue[i] = create_list_queue();	
	}

	//	循环次数由最大值的位数决定
	TYPE max = arr[0];
	for(int i=1; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];
	}

	//	i是1表示处理个位,2表示处理十位...
	for(int i=1,k=1; max/k>0; k*=10,i++)
	{
		int mod = pow(10,i);
		int div = mod/10;
		for(int j=0; j<len; j++)
		{
			//	获取arr[j] 某位的数
			int index = arr[j]%mod/div;
			push_list_queue(queue[index],arr[j]);
		}
		//	对所有队列依次出队回原内存中
		for(int j=0,i=0; j<10; j++)
		{
			while(!empty_list_queue(queue[j]))
			{
				arr[i++] = head_list_queue(queue[j]);
				pop_list_queue(queue[j]);
			}
		}
	}
	for(int i=0; i<10; i++)
	{
		destroy_list_queue(queue[i]);	
	}
}

标签:arr,int,max,len,算法,排序,TYPE
From: https://www.cnblogs.com/wangqiuji/p/17594501.html

相关文章

  • 快速排序
    主要思想:分治关键步骤:确定分界点:创建一个数组q,在数组中选一个基准数(通常为数组第一个),x=q[left],q[(left+right)/2],q[right].2.调整区间:把比基数(x)小的数放在左边,比基数大的数放在右边。3.递归处理左右两段,不断递归直至排序完成。例如:1.6为基准数,设i,j为两哨兵,目前指向......
  • NET/C#中SM2/SM3国密加密算法
    usingOrg.BouncyCastle.Asn1;usingOrg.BouncyCastle.Asn1.GM;usingOrg.BouncyCastle.Asn1.X9;usingOrg.BouncyCastle.Crypto;usingOrg.BouncyCastle.Crypto.Parameters;usingOrg.BouncyCastle.Math;usingOrg.BouncyCastle.Security;usingOrg.BouncyCastle.Util......
  • java常见的排序算法(冒泡排序、选择排序、插入排序、shell排序、归并排序、堆排序、快
    文章目录一、冒泡排序1、效率表现和适用范围2、算法实现二、选择排序1、效率表现和适用范围2、算法实现三、插入排序1、效率表现和适用范围2、算法实现四、shell排序1、效率表现和适用范围2、算法实现五、归并排序1、效率表现和适用范围2、算法实现六、快速排序1、效率表现和适用......
  • 【九】DRF之过滤排序异常
    【一】过滤(Filtering)对于列表数据可能需要根据字段进行过滤我们可以通过添加django-fitlter扩展来增强支持。pipinstalldjango-filter在配置文件中增加过滤后端的设置:INSTALLED_APPS=[...'django_filters',#需要注册应用,]REST_FRAMEWORK={......
  • 算法训练 与1连通的点的个数
    主要思想是并查集,不懂的可以先了解下这个算法再来做题就明白了。c++实现:#include<iostream>#include<vector>usingnamespacestd;intf[10000];//找根节点intfind(intx){if(f[x]!=x)f[x]=find(f[x]);returnf[x];//不要加els......
  • 强化学习——DQN算法
    1、DQN算法介绍DQN算与sarsa算法和Q-learning算法类似,对于sarsa和Q-learning,我们使用一个Q矩阵,记录所有的state(状态)和action(动作)的价值,不断学习更新,最后使得机器选择在某种状态下,价值最高的action进行行动。但是当state和action的数量特别大的时候,甚至有限情况下不可数时,这时候再......
  • 归并排序
    求逆序对我用的是归并排序直接上我在洛谷里做的那道逆序对的题目的归并排序主要代码吧1voidmsort(intl,intr){2if(l>=r)return;3intmid=(l+r)>>1;4msort(l,mid);5msort(mid+1,r);6inti=l,j=mid+1,k=l;7......
  • 基于内容的个性化推荐算法-电影推荐系统
    之前在博客中介绍了协同过滤算法在电影推荐系统中的应用。今天我将向大家分享另一种常见的推荐算法——基于内容的推荐算法,并使用它来实现一个个性化的电影推荐系统。基于内容的推荐算法原理:基于内容的推荐算法是一种常用的推荐方法,它通过分析电影本身的特征来进行推荐。在电影推荐......
  • 基于标签的个性化推荐算法-电影推荐系统
    之前在博客中介绍了协同过滤算法和基于内容的推荐算法在电影推荐系统中的应用。今天我将向大家介绍另一种常见的推荐算法——基于标签的推荐算法,并使用它来实现一个更加个性化的电影推荐系统。基于标签的推荐算法原理:基于标签的推荐算法是一种利用用户标记信息进行推荐的算法。在电......
  • [算法学习笔记] 强连通分量
    DFS生成树在介绍强连通分量前,我们先来了解一下DFS生成树。一棵DFS生成树分为树边,前向边,返祖边(一说反向边),横叉边。我们来画图解释一下:在这棵DFS生成树中,黑色为树边,它是在DFS遍历时获得的,红色为返祖边,顾名思义,从儿子指向父亲或祖先。蓝色为横叉边,它是在搜索的时候遇到子树中的节......