排序 算法

本文详细介绍了排序算法中的稳定性和不稳定性的概念,并列举了冒泡排序、选择排序、插入排序、快速排序等常见排序算法的工作原理和时间复杂度。稳定排序包括冒泡排序、插入排序和归并排序,而快速排序属于不稳定排序。对于不同的应用场景,选择合适的排序算法至关重要。

稳定排序与不稳定排序
如果我们对一串数字排序,那么稳定与否并不是很重要,因为一串数字的属性是单一的,就是数值的大小。但是排序的元素的属性往往不是只有一个属性,例如我们对一群人进行年龄排序,但是人除了年龄属性还有身高体重属性,在年龄相同时如果不想破坏原先身高体重的次序,就必须用稳定排序算法。

判断某个排序算法是否稳定,可以简单的理解为:排序前后两个相等的数在其在序列的前后位置和排序后它们两个的前后位置顺序是否相同。
如果相同,则是稳定排序算法。
如果不同,则是不稳定排序算法。

稳定排序:冒泡排序,插入排序,归并排序
不稳定排序:选择排序,希尔排序,快速排序

1.冒泡排序算法实现

原理: 排序的次数是排序数组长度减一,排序依次将待排序数组的最大值排到数组的末尾。每一次的排序,需要比较的次数也会递减。时间复杂度O(N^2)

比如

int array [] =[2,5,1,3,4];

第一次排序结果为:

int array [] =[2,1,3,4,5];

算法实现如下:

void maopaosort(vector<int> arraynum)///冒泡排序
{
	for (int i=0;i<arraynum.size()-1;i++)///排序的次数 
	{
		int isChange=0;
		for (int j=0;j<arraynum.size()-i-1;j++)////每一次排序要比较的次数
		{
			if (arraynum[j]>arraynum[j+1])
			{
				int tem = arraynum[j] ;
				arraynum[j]=arraynum[j+1] ;
				arraynum[j+1]=tem;	
				isChange=1;
			}
		}
		if (isChange == 0)
		{
           break;
		}
	}
}

代码中的 isChange 是对冒泡排序的一点小优化 。

2.选择排序

工作原理:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在数组的起始(末尾)位置,直到待排序的数组全部排序完。时间复杂度O(N^2)

比如:

int[] arrays = {2, 3, 1, 4, 3, 5, 1, 6, 1, 2, 3, 7, 2, 3};

第一次排序后:(将最大值的位置 “7”与数组的最后一个值的位置“3”对换)

int[] arrays = {2, 3, 1, 4, 3, 5, 1, 6, 1, 2, 3, 3, 2, 7};

算法实现如下:

void Select_Sort(vector<int> arraynum)///
{
	int pos; //记录当前趟数的最大值角标
	int tem; // 交换的变量
	for (int i=0;i<arraynum.size()-1;i++) 
	{
		pos=0;
		for (int j=0;j<arraynum.size()-i;j++) // 寻找最大值
		{
			if (arraynum[j] > arraynum[pos])
			{
				pos=j;
			}
		}
		//交换
		tem=arraynum[pos];
		arraynum[pos]=arraynum[arraynum.size()-i-1];
		arraynum[arraynum.size()-i-1]=tem;
	}
}

3.插入排序

1.首先将已排序的数据看成一个整体。
2.一个数组需要n-1趟排序,总是后一位和已排序的数据进行比较(第一趟:第二 位和已排序的数据比,第二趟:第三位和已排序的数据比)
3.用第三位和已排序的数据比,实际上就是让第三位更两个数比较,只不过这里按个数已经是排好序的。正是因为已经排好序,我们可以使用一个循环将我们比较的数据插入进去。
时间复杂度O(N^2)

void charu_sort(vector <int> nums)
{
	for (int i=1;i<nums.size();i++)
	{
		int tem=nums[i] ;
		int j=i-1;
		while(j>=0 && nums[j]>tem)
		{
			nums[j+1]=nums[j];
			j--;
		}
		nums[j+1] = tem ;
	}
}

4.快速排序
通过一趟排序将要排序的数据分割成 独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这 两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
时间复杂度:O(nlogn)

void fast_sort(vector <int> nums)
{
	int n=nums.size();
	quicksort(nums,0,n-1) ;
}

void quicksort(vector <int> nums,int le,int rt)
{
	int i=le,j=rt;
	int midval = nums[(i+j)/2];
	while(i<=j)
	{
		while(midval > nums[i])
		{
			i++;
		}
		while(midval < nums[j])
		{
			j--;
		}
		if (i<=j)
		{
			int tem=nums[i];
			nums[i] = nums[j];
			nums[j] = tem ;
			i++;j++;
		}
	}
	if (le<j)
	{
		quicksort(nums,le,j);
	}
	if (i<rt)
	{
		quicksort(nums,i,rt);
	}

}

5.桶排序

对有负数和有0的数列难以进行排列
桶排序步骤:
分配:按元素的大小放入不同的筒子(有二维数组表示)
回收:按照筒子序号的大小回收;
重复

void radixsort(vector<int > nums)  
{
	int max=findmax(nums,0,nums.size()-1);
	for (int i=1;max/i>0;i=i*10)
	{
		vector<vector<int>> buckets(nums.size(),vector<int>(10));
		for (int j=0;j<nums.size();j++)
		{
			int num= (nums[j]/i)%10;
			buckets[j][num] = nums[j] ; 
		}
		for (int x=0;x<10;x++)
		{
			for (int y=0;y<nums.size();y++)
			{
				int k=0;
				if (buckets[y][x] != 0)
				{
					nums[k]=buckets[y][x];
					k++;
				}
			}
		}
		vector<vector<int>>().swap(buckets);
	}
}

int findmax(vector<int > nums,int l,int r)
{
	if (l == r)
	{
		return nums[l];
	}
	int a = nums[l];
	int b = findmax(nums,l+1,r);
	return a>b?a:b;
}

6.希尔排序

插入排序的高级版

希尔排序在带批量数据时和归并排序差不多

void shellsort(vector<int> nums)
{
	for (int step = nums.size()/2;step>0;step+=2)
	{
		for (int i=step;i<nums.size();i++)
		{
			int j=i;
			int tem=nums[j];
			while(j-step>=0 && nums[j-step] > tem)
			{
				nums[j] = nums[j-step];
				j=j-step;
			}
			nums[j] = tem ;
		}
	}
}

7.堆排序

int main()
{
	vector<int > nums;
	maxHeapify(nums,nums.size()); //线进行一次堆排序 使得数组的树满足 父>子
	int size = nums.size()-1;

	for (int i=0;i<nums.size();i++) 
	{
		///数组最后一个是最大值 建一次堆下次可少一个 且第一个数是最大值 在交换到最后一位
		int tem = nums[0];
		nums[0] = nums[nums.size()-1-i];
		nums[nums.size()-1-i] = tem;

		heapif(nums,0,size);
		size--;
	}
}

void maxHeapify(vector<int> nums,int size)
{
	////第一次建堆要从数组的最后一个值开始 保证第一次建堆后树满足 父>子
	for (int i= size-1 ; i>=0;i--)
	{
		heapif(nums,i,size);
	}
}

void heapif(vector<int> nums,int curtootnode,int size)
{
	int l = 2*curtootnode+1;
	int r = 2*curtootnode+2;
	int max = curtootnode;
	if (l<size)
	{
		if (nums[l] > nums[max] )
		{
			max=l;
		}
	}
	if (r<size)
	{
		if (nums[r] > nums[max])
		{
			max=r;
		}
	}
	if (max != curtootnode)
	{
		int tem = nums[max] ; 
		nums[max] = nums[curtootnode] ;
		nums[curtootnode]  = tem;
		//// 如果 左右节点和根节点换了值 那么换了值后的子树 也要重新建堆排序 使得 父>子 成立
		heapif(nums,max,size); 
	}
}

8.归并排序

缺点 :需要额外的空间 典型的空间换时间操作
时间复杂度:O(nlogn)

int main()
{
	vector<int> nums;
	mergeSort(nums,1,nums.size()-1);
}

void mergeSort(vector<int> nums,int l,int r)
{
	if (l == r)//只有一个元素 不用进行拆分
	{
		return;
	}
	else
	{
		int mid = (l+r)/2;
		//z左边进行拆分
		mergeSort(nums,l,mid);
		//右边进行拆分
		mergeSort(nums,mid+1,r);
		//合并
		merge(nums,l,mid+1,r);
	}
}

void merge(vector<int> nums,int l,int m,int r)
{
	vector<int> left_array(m-l);
	vector<int> right_array(r-m+1);
	for (int i=l;i<m;i++)
	{
		left_array[i] = nums[i];
	}
	for (int i=m;i<=r;i++)
	{
		right_array[i-m] = nums[i];
	}

	int i=0,j=0;
	int k=l;

	while(i<left_array.size() && j<right_array.size())
	{
		if (left_array[i]>right_array[j])
		{
			nums[k] = left_array[i];
			i++;k++;
		}
		else
		{
			nums[k] = right_array[j];
			j++;k++;
		}
	}
	//左右数组谁没有完 吧剩下的放进去
	while(i<left_array.size())
	{
		nums[k] = left_array[i];
		i++;k++;
	}
	while(j<right_array.size())
	{
		nums[k] = right_array[j];
		j++;k++;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值