常用的排序算法

常用的排序算法总结

冒泡排序

冒泡排序,每次遍历都把最大元素(也可以是最小元素)放到无序数组的最后面。
冒泡排序是一个稳定的排序算法。
时间复杂度计算:
最优情况下,已经排好序了,只需双层循环,n*(n-1)/2 。是O( n^2 );
最坏情况,元素是逆序,每次遍历都要3次交换, 3n*(n-1)/2。是O( n^2 );

void bubbleSort(std::vector<int>& arr)
{
	int len = arr.size();
	for (int i = 0; i < len-1; i++)
	{
		int tmp = 0;
		for (int j = i; j < len-1; ++j)
		{
			if (arr[j] > arr[j+1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j+1] = tmp;
			}
		}
	}
}

选择排序

选择排序是一个稳定的排序。
步骤:
依次在无序的序列中找到最大或最小的元素,与无序序列的最后元素交换。直到所有元素有序。
时间复杂度:从代码中可以看出一共遍历了n + n-1 + n-2 + … + 2 + 1 = n * (n+1) / 2 = 0.5 * n ^ 2 + 0.5 * n,那么时间复杂度是O(N^2)。

*/
void selectionSort(std::vector<int>& arr)
{
	int len = arr.size();

	for (int i = 0; i < len - 1; ++i)
	{
		int minindex = i;
		for (int j = i + 1; j < len - 1; ++j)
		{
			if (arr[minindex] > arr[j])
			{
				minindex = j;
			}
		}

		//交换
		int tmp = arr[minindex];
		arr[minindex] = arr[i];
		arr[i] = tmp;
	}
}

插入排序

插入排序是一个也是一个稳定的排序算法。
算法分析:插入排序在有序序列基础上,一次插入一个元素。有序序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是插入元素要和已经有序依次比较,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
时间复杂度依然是O(N^2)。

void insertionSort(std::vector<int>&arr)
{
	int len = arr.size();

	for (int i = 1; i < len; ++i)               //可以认为第一个排好序了
	{
		int j = i - 1;    //指向有序的序列尾
		int val = arr[i];     //将i取出,因为序列要后移,会覆盖i
		while (j >=0 && arr[j] > val)
		{
			arr[j + 1] = arr[j];
			j--;
		}
		arr[j + 1] = val;                          //j + 1
	}
}

希尔排序

希尔排序是第一个时间复杂度突破O(n^2)的排序算法。
是插入排序的改进版,优先比较距离远的元素,是不稳定的排序算法。
思想:希尔排序是把序列按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
希尔排序时间复杂度的下界是O(n*log2n)。
希尔算法在最坏的情况下和平均情况下执行效率相差不是很多。

void shellSort(std::vector<int>&arr)
{
	//分成多个间隔,一般/2
	int len = arr.size();
	for (int i = len / 2; i > 0; i = i / 2)
	{
		//当i = 1时,就是整个数组的排序
		for (int j = i; j < len; ++j)
		{
			//这里多个组同时插入排序
			int cur = arr[j];
			int k = j;           
			while (k - i >= 0 && arr[k - i] > cur)
			{
				arr[k] = arr[k - i];
				k -= i;
			}

			arr[k] = cur;
		}
	}
}

归并排序

归并排序,是稳定排序
采用了分治法的思想:将大的序列划分小的序列,排序然后合并。
时间复杂度O(nlogn)

void merge(std::vector<int>&arr, int l, int m, int r)
{
	int len = r - l + 1;
	std::vector<int> tmp(r - l+1, 0);

	int left = l;
	int right = m + 1;
	int i = 0;
	while (left <= m && right <= r)
	{
		if (arr[left] > arr[right])
		{
			tmp[i] = arr[right];
			right++;
		}
		else
		{
			tmp[i] = arr[left];
			left++;
		}
		i++;

	}
	//判断是否完全合并完
	if (left > m)
	{
		while (right <= r)
		{
			tmp[i] = arr[right];
			i++;
			right++;
		}
	}
	else if (right >= m)
	{
		while (left <= m)
		{
			tmp[i] = arr[left];
			i++;
			left++;
		}
	}

	//将数据返回
	for (int i = 0; i < len; ++i)
	{
		arr[i+l] = tmp[i];
	}

}

void mergeSort(std::vector<int>&arr, int l, int r)
{
	int len = r - l + 1;
	if (len < 2)
	{
		return;
	}

	int mid = (l + r) / 2;
	mergeSort(arr, l, mid);
	mergeSort(arr, mid+1, r);
	merge(arr, l, mid, r);
}

void mergeSort(std::vector<int>&arr)
{
	int len = arr.size();
	mergeSort(arr, 0, len - 1);
}

快速排序

是不稳定算法。
快速排序的基本思想:通过一趟排序将待排序列分隔成独立的两部分,其中一部分序列的元素均比另一部分的元素小,则可分别对这两部分序列继续进行排序,以达到整个序列有序。
时间复杂度:O(nlogn)
最坏能达到O(n^2)

int partition(std::vector<int>&arr, int l, int r)
{
	int left = l;
	int right = r;
	int val = arr[l];
	while (left < right)
	{
		while (left<right&& arr[right]>=val)
		{
			right--;
		}
		if (left < right)
		{
			arr[left++] = arr[right];
		}
		while (left < right&&arr[left] <val)
		{
			left++;
		}
		if (left < right)
		{
			arr[right--] = arr[left];
		}
		
	}

	arr[left] = val;         //基准值放在left上,这样,左边小于,右边大于等于

	return left;
}
void quickSort(std::vector<int>&arr, int l, int r)
{
	if (l < r)
	{
		int p = partition(arr, l, r);

		if (p != l)
		{
			quickSort(arr, l, p - 1);
		}
		if (p != r)
			quickSort(arr, p + 1, r);
	}
}

void quickSort(std::vector<int>&arr)
{
	int len = arr.size();
	quickSort(arr, 0, len - 1);
}

堆排序

参考博客

/*
堆排序
时间复杂度nlogn
不稳定
*/

void adjustHeap(std::vector<int>&arr, int i, int length)
{
	//取出当前节点的值
	int tmp = arr[i];
	for (int k = 2 * i + 1; k < length; k = 2 * k + 1)
	{
		//从左子节点开始
		if (k + 1 < length && arr[k] < arr[k + 1])
		{
			k++;            //移到右节点,主要是找到大的子节点,来判断
		}
		if (arr[k] > tmp)
		{
			//子节点大于父节点, 就将值赋给父节点
			arr[i] = arr[k];
			i = k;             //i 指向新的位置
		}
		else
		{
			//父节点大于子节点
			break;
		}
		arr[i] = tmp;      //将tmp 放入正确的位置
	}
}
void buildHeap(std::vector<int>&arr)
{
	int len = arr.size();
	for (int i = len/2-1; i >= 0; i--)
	{
		//从第一个非叶子结点开始
		adjustHeap(arr, i, arr.size());
	}
}
//堆排序
void heapSort(std::vector<int>&arr)
{
	//建立堆
	buildHeap(arr);
	//
	for (int i = arr.size() - 1; i > 0; i--)
	{
		//交换
		int tmp = arr[0];
		arr[0] = arr[i];
		arr[i] = tmp;
		adjustHeap(arr, 0, i);        //从上向下调整, 这里每次长度减一
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值