排序算法总结

c++经典排序算法总结

算法分类

一、冒泡排序

步骤
从头开始,每次比较两元素,若大者在前,则交换两元素,直至数组末尾,此时最大元素为数组最后的元素;
重复以上步骤,从头开始至上一轮比较的末尾元素;
性质
稳定算法;
适用场景
数据量量不大,对稳定性有要求,且数据基本有序的情况下

//冒泡排序
void BubbleSort(int* h,size_t len)
{
    if(h==NULL)
    {
       return;
    }
    if(len<=1)
    {
        return;
    }

    for(int i=0;i<len-1;++i)
    {
        for(int j=0;j<len-1-i;++j)
        {
            if(h[j]>h[j+1])
            swap(h[j],h[j+1]);
        }
    }
    return 
}

//时间复杂度:o(n*n)   空间复杂度o(1)

二、选择排序

步骤
搜索整个列表,找出最小项,若此项不为第1项,则与第1项交换位置;
重复上述步骤,每次搜索未被排序的剩余列表,并将最小元素与已排序段的后一位交换,直至列表所有元素均被排序;
性质
不稳定算法;

// 选择排序
void selectSort(vector<int>& array){
    for (size_t i = 0; i < array.size(); i++){
        size_t minIndex = i;
        for (size_t j = i + 1; j < array.size(); j++){
            if (array[minIndex] > array[j]){
                minIndex = j;
            }
        }
        if (minIndex != i){
            swap(array[i], array[minIndex]);
        }
    }
}
时间复杂度O(n*n)  空间复杂度O(1)

插入排序

步骤
将第一个元素看作有序序列,后续元素当作无需序列,依次将无序序列元素插入有序序列当中;
性质
稳定算法;

for (int i = 1; i < nums.size(); ++i)
		{
			int temp = nums[i];
			for (int j = i - 1; j >= 0; --j)
			{
				if (nums[j+1] < nums[j])
				{
					swap(nums[j], nums[j + 1]);
				}
				else
				{
					break;
				}
			}
		}
		return nums;
	}
	时间复杂度O(n*n)  空间复杂度O(1)

希尔排序

步骤
选择一个增量序列,初始增量gap=length/2,后续元素依次为前一元素除2,直至gap=1;
每轮以gap为步长,在列表上进行采样,将列表分为gap个小组,在每个小组内进行选择排序;
重复第二步,直至gap=1;
性质
不稳定算法;

int n = nums.size();
		for (int gap = n / 2; gap >= 1; gap /= 2)
		{
			for (int i = gap; i < nums.size(); i ++)
			{
				//int tempValue = nums[i];

				for (int j = i- gap; j >= 0; j -= gap)
				{
					if (nums[j+gap]<nums[j])
					{
						swap(nums[j + gap], nums[j]);
					}
				}

			}
		}
		时间复杂度 O(nlogn),空间复杂度O(1)

并归排序

步骤
将列表从正中间分为两个子列表;
按照第一步,递归拆分每个子列表,直至子列表最大长度为1;
按照拆分层级,依次按大小合并各子列表,直至全部合并完成。
性质
稳定算法;

void mergeSortHelper(vector<int> &nums, vector<int> &copyNums, int left, int right)
	{
		if (left < right)
		{
			int mid = (left + right) / 2;
			mergeSortHelper(nums, copyNums, left, mid);
			mergeSortHelper(nums, copyNums, mid + 1, right);
			mergeSort(nums, copyNums, left, right);
		}
	}

	void mergeSort(vector<int> &nums, vector<int> &copyNums, int left, int right)
	{ 
		int mid = (left + right) / 2;
		int i = left, j = mid + 1, k = 0;

		while (i <= mid || j <= right)
		{
			if (i > mid)
			{
				copyNums[k] = nums[j];
				j++;
			}
			else if (j > right)
			{
				copyNums[k] = nums[i];
				i++;
			}
			else if(nums[i]>nums[j])
			{
				copyNums[k] = nums[j];
				j++;
			}
			else
			{
				copyNums[k] = nums[i];
				i++;
			}
			k++;
		}

		for (int i = left; i <= right; ++i)
		{
			nums[i] = copyNums[i - left];
		}
	}
	时间复杂度O(nlogn) 空间复杂度O(1)

快速排序

步骤
从列表中选出一个元素,作为“基准”pivot,基准一般随机选择,或采用最左端、最右端和中间位置3元素的中值;
将小于基准的元素排在基准前面,大于基准的元素排在基准后面,此时基准元素所在位置即为其最终排序完成时的位置;
以基准元素为界,将列表分为两个子列表;
递归地对子列表重复上述操作。
性质
不稳定算法;
当数据量很小(N<=20)时,快速排序效果不如插入排序,因为快速排序不稳定且有递归开销;
适用场景
常用于查找一组中前k大的数据

//快递排序
	int medianPovit(vector<int> &nums, int left, int mid, int right)
	{
		if (nums[left] > nums[mid])
		{
			swap(nums[left], nums[mid]);
		}
		if (nums[left] > nums[right]) {
			swap(nums[left], nums[right]);
		}
		if (nums[mid] > nums[right]) {
			swap(nums[mid], nums[right]);
		}
		return nums[mid];
	}

	int partition(vector<int> &nums, int left, int right)
	{
		int meida = (left + right) / 2;
		int pivotkey = medianPovit(nums,left, meida, right);
		swap(nums[meida], nums[right]);

		int j = left-1;
		for (int i = left; i <right; ++i)
		{
			if (nums[i] < pivotkey)
			{
				j++;
				if (j != i)
				{
					swap(nums[i], nums[j]);
				}
			}
		}
		j++;
		swap(nums[j], nums[right]);
		return j;

	}
	void quickSort(vector<int> &nums, int left, int right)
	{
		if (left < right)
		{
			int pivot = partition(nums, left, right);
			quickSort(nums, left, pivot - 1);
			quickSort(nums, pivot + 1, right);
		}
	}
	时间复杂度:最好O(nlogn) 最坏O(n*n) 平均O(nlogn)
	空间复杂度:最好O(logn) 最坏O(n)  平均O(logn)
// 快速排序 非递归(迭代版)
void quickSortIteration(vector<int>& array) {
	stack<vector<int>> boundaries;
	int left = 0, right = array.size() - 1;
	while (left < right || !boundaries.empty()) {
		if (left >= right) {
			vector<int> boundary = boundaries.top();
			boundaries.pop();
			left = boundary[0];
			right = boundary[1];
		}
		int pivotLoction = partition(array, left, right);
		if (pivotLoction + 1 < right) {
			boundaries.push({ pivotLoction + 1, right });
		}
		right = pivotLoction - 1;
	}
}

堆排序

步骤
将数字转化为一个堆;

堆是具有以下两属性的二叉树:
(1)每个节点的值大于等于其子节点的值;
(2)树完全平衡,即最底层叶子节点都位于左侧(完全),且左右子树高度相差不超过1(平衡);
堆也被称为优先队列,具有先进先出的特性,在堆底插入元素,在堆顶取出元素。

取出堆顶元素(最大元素),作为有序数数组末尾元素,并对二叉树进行调整使其满足堆的特性;

重复上一步骤,依次取出堆顶元素,并插入到有序数组中,上一插入元素之前的位置,直到堆空为止;

性质
不稳定算法;
适用范围
不适合待排序序列个数较少的情况

void Heapsort(vector<int> &nums)
{
	for (int i = nums.size() / 2 - 1; i >= 0; --i)
	{
		HeapAdjust(nums, i, nums.size() - 1);
	}
	for (int i = nums.size() - 1; i >= 1; --i)
	{
		swap(nums[0], nums[i]);
		HeapAdjust(nums, 0, i - 1);
	}
}       
       建堆时间为O(n)
	   时间复杂度:最好、最坏、平均均为(nlogn)
	   空间复杂度O(1)
      堆的插入和删除时间复杂度均为lon(n)
//堆排序;
	void HeapAdjust(vector<int> &nums, int i, int length)
{
	int temp = nums[i];

	for (int j = i * 2 + 1; j <= length; j = j * 2 + 1)
	{
		if (j + 1 <= length&&nums[j] < nums[j + 1])
		{
			j++;
		}
		if (nums[j] < temp)
		{
			break;
		}
		nums[i] = nums[j];
		i = j;
	}
	nums[i] = temp;
}

计数排序

步骤
遍历待排序数组A,找出其最小值min和最大值max;
创建一个长度为max-min+1的数组B,其所有元素初始化为0,数组首位对应数组A的min元素,索引为i位置对应A中值为min+i的元素;
遍历数组A,在B中对应位置记录A中各元素出现的次数;
遍历数组B,按照之前记录的出现次数,输出几次对应元素;
性质
稳定排序算法;
外部排序;

 //计数排序
        int min = nums[0], max = nums[0];
		for (int i = 0; i < nums.size(); ++i)
		{
			if (min > nums[i])
			{
				min = nums[i];
			}

			if (max < nums[i])
			{
				max = nums[i];
			}
		}

		vector<int> counts(max - min + 1);
		for (int i = 0; i < nums.size(); ++i)
		{
			counts[nums[i] - min]++;
		}
		int index = 0;
		for (int i = 0; i < counts.size(); ++i)
		{
			int n = counts[i];
			while (n>0)
			{
				nums[index] = i + min;
				index++;
				n--;
			}
		}
		return nums;
		时间复杂度O(n+k) 空间复杂度O(k)

桶排序

步骤
设置固定数量的空桶;
找出待排序数组的最大值和最小值;
根据最大最小值平均划分各桶对应的范围,并将待排序数组放入对应桶中;
为每个不为空的桶中数据进行排序(例如,插入排序);
拼接不为空的桶中数据,得到排序后的结果。
特性
稳定算法;
常见排序算法中最快的一种;
适用于小范围(最大值和最小值差值较小),独立均匀分布的数据;
可以计算大批量数据,符合线性期望时间;
外部排序方式,需额外耗费n个空间;

// 桶排序
void bucketSort (vector<int>& array, int bucketCount){
    if (array.empty()){
        return;
    }
    // 找出最大最小值
    int max = array.front(), min = array.front();
    for (int i = 1; i < array.size(); i++){
        if (min > array[i]){
            min = array[i];
        }
        else if (max < array[i]){
            max = array[i];
        }
    }

    // 将待排序的各元素分入对应桶中
    vector<vector<int>> buckets(bucketCount);
    int bucketSize = ceil((double)(max - min + 1) / bucketCount);
    for (int i = 0; i < array.size(); i++){
        int bucketIndex = (array[i] - min) / bucketSize;
        buckets[bucketIndex].push_back(array[i]);
    }

    // 对各桶中元素进行选择排序
    int index = 0;
    for (vector<int> bucket : buckets){
        if (!bucket.empty()){
            // 使用选择排序算法对桶内元素进行排序
            selectSort(bucket);
            for (int value : bucket){
                array[index] = value;
                index++;
            }
        }
    }

}
// 桶排序
void bucketSort (vector<int>& array){
    bucketSort (array, array.size() / 2);
}
时间复杂度O(n+k) 空间复杂度O(n+k)

基数排序

步骤
将各待比较元素数值统一数位长度,即对数位短者在前补零;

根据个位数值大小,对数组进行排序;

重复上一步骤,依次根据更高位数值进行排序,直至到达最高位;

在这里插入图片描述
特性
稳定算法;
适用于正整数数据(若包含负数,那么需要额外分开处理);
对于实数,需指定精度,才可使用此算法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值