排序算法总结(C++)

img

1. 排序算法

将每一个数依次与其后面的数做对比,如果前后两个数逆序,则交换,然后进行下一步对比,经过每一个交换,最大的数都会被换到数组尾部,然后使数组总长度减一,这里为了节省时间,可以设置一个标志位,在每一次循环的开始都设为 true,如果在遍历过程中发生交换,则同时置为 false,经过一次完整循环以后没有发生过一次交换,则标志位一直为 true 视为数组已经有序,直接结束。

void bubbleSort(vector<int> &A)
{
	int n = A.size();
	bool sorted = false;  // flag to save time
	while (!sorted)
	{
		sorted = true;
		for (int i = 0; i < n-1; i++)
		{
			if (A[i]>A[i+1])
			{
				swap(A[i], A[i + 1]);
				sorted=false;
			}
		}
		n--;
	}
}

2. 选择排序

2021.1.3 新增部分内容

时间复杂度最好,最坏,平均都是 n^2,不稳定,类似于冒泡。但是冒泡是稳定的。

每一次迭代都找到从出发位置开始后面数组中的最小数字,然后让其与当前位置的值互换,需要借助两个中间遍历来记录最小值的位置与数值。

void selectionSort(vector<int> &A)
{
	int k=0; int temp=0;
	for (int i = 0; i < A.size(); i++)
	{
		k = i;
		temp = A[i];
		for (int j = i; j < A.size(); j++)
		{
			if (temp > A[j])
			{
				k = j;
				temp = A[j];
			}
		}
		A[k] = A[i];
		A[i] = temp;
	}
}

3. 插入排序

2021.1.3 新增部分内容

稳定排序,时间复杂度最坏和平均都是 n^2, 最好是 n(数组有序时),原地变换,时间复杂度是1。

当数组中只有一个数时数组肯定有序,这里假设数组前半部分是有序的,则只要从数组的后半部分每次取一个数,查找到这个待插入的数在前半部分的位置,让后将其加入,直到后半部分数组长度为0,整个数组即有序。

void insertSort(vector<int>& A) {

	for (int i = 1; i < A.size(); i++)
	{
		for (int j = 0; j < i; j++) {
			if (A[j] > A[i])
			{
				swap(A[j],A[i])
			}
		}
	}
}

4. 快速排序

这里首先找到一个中心旋转点,一般是数组 A 的第一个A[0]/最后一个元素A[A.size()-1],这里以 A[0] 为旋转点为例,设置两个哨兵i,j 分辨去找大于 A[0] 和 大于 A[0] 的点,哨兵 i 从左向右遍历,当满足 i <= j,并且A[i] > A[0]时,i停下,同理 j 从右向左找小于A[0] 的值,当满足 i<=j,且 A[j] < A[0]时,j 停下,交换 A[i] 和 A[j],然后继续向前寻找,直到 i> j 时,以 A[0] 为中间点的,然后对 A[0] 的左、右分辨重复上面的过程。

int partition(vector<int>& a, int left, int right) {
	int i = left;
	int j = right;
	int temp = a[right];
	while (i <j) {
		while (i<j && a[i] <= temp)
		{
			i++;
		}
		while (i<j && a[j]>=temp)
		{
			j--;
		}
		if (i < j)
			swap(a[i], a[j]);
	}
	swap(a[j], a[right]);
	return j;
}

void quickSort(vector<int> a, int left, int right) {
	if (left >= right)
	{
		return;
	}
	int j = partition(a, left, right);
	quickSort(a, left, j - 1);
	quickSort(a, j + 1, right);
}
2021.1.3 新增部分内容

不稳定排序:最好和平均都是 nlogn,最坏 n^2(逆序或有序),空间复杂度是 nlogn。快速的主要原因是内循环用一个递增的索引与定值做比较,干的事情相对较少。相较于归并和希尔排序,它们在内循环中还需要移动数据。
对于快排的进一步优化
1.更好地处理小数组带来的影响:将插入排序合并进快排之中,对于小数组(长度 5 ~ 15),插入排序比较快,将 if (hi <= lo) return; 改为 if(hi <= lo + M) return insertSort(xxxx);
2. 更好地解决重复元素:对于重复元素比较多的数组,使用三向切分,多增加一个等于的指针,荷兰国旗问题。
3. 更好地切分三取样切分,主要是选取更合适的 partition 点,使用子数组的一小部分元素的中位数来切分数组。

5. 合并排序

void merge(vector<int>&s1, vector<int>& s2, vector<int> & result)
{
	int i = 0, j = 0;
	for (; i < s1.size()&&j<s2.size();)
	{
		if (s1[i] >= s2[j])
		{
			result.push_back(s2[j]);
			j++;
		}
		else {
			result.push_back(s1[i]);
			i++;
		}
	}
	if (i < s1.size())
	{
		for(int k=i;k<s1.size();k++)
			result.push_back(s1[k]);
	}
	if (j < s2.size())
	{
		for (int k = j; k<s2.size(); k++)
			result.push_back(s2[k]);
	}
}

void mergeSort(vector<int>&nums)
{
	if (nums.size() <= 1)
		return;
	int i = 0, j = nums.size();
	int mid = (i + j) / 2;
	vector<int> s1;
	vector<int> s2;
	for (int k = 0; k < mid; k++)
		s1.push_back(nums[k]);
	for (int k = mid; k < j; k++)
		s2.push_back(nums[k]);
	mergeSort(s1);
	mergeSort(s2);
	nums.clear();
	merge(s1, s2, nums);
}

数组实现

void merge(vector<int>& A, int lo, int mi, int hi) {
	int ll = mi - lo + 1; int lr = hi - mi;
	vector<int> L(ll+1, 0);
	vector<int> R(lr+1, 0);

	for (int i = 0; i < ll; i++) {
		L[i] = A[lo + i];
	}
	for (int j = 0; j < lr; j++) {
		R[j] = A[mi + j+ 1];
	}
	L[ll] = INT_MAX;
	R[lr] = INT_MAX;
	for (int i = 0, j = 0, k = lo; k <= hi; k++)
	{
		if (L[i] <= R[j])
			A[k] = L[i++];
		else
			A[k] = R[j++];
	}
}

void mergeSort(vector<int>& A, int lo, int hi)
{
	// 若两数组默认有序,则直接 hi-lo<2 ,就可以 return,这里还需要换一下
	if (hi - lo == 1)
	{
		if (A[lo] > A[hi]) {
			swap(A[lo], A[hi]);
			return;
		}
	}
	if (hi - lo<1)
	{
		return;
	}
	int mi = (hi + lo) >> 1;
	mergeSort(A, lo, mi);
	mergeSort(A, mi + 1, hi);
	merge(A, lo, mi, hi);
}
2021.1.3 新增部分内容

归并排序是稳定的排序算法,采用了分支的思想是,所以时间复杂度最好最坏都是 nlogn,空间复杂度是 n。归并排序有两种,自顶向下的,还有自底向上的。

  • 自顶向下:
    数组经过分值不断切分,然后切到子单位再合起来。
    —————————
    ———— ————
    —— —— —— ——
    ———— ————
    —————————

  • 自底向上:
    先两两合并长度为 1 的子数组,然后两两合并长度为 2 的子数组,以此类推。更适合链表。
    —— —— —— ——
    ———— ————
    —————————

6 堆排序

void adjust(vector<int> &arr, int len, int index)
{
	int left = 2 * index + 1; // index的左子节点
	int right = 2 * index + 2;// index的右子节点

	int maxIdx = index;
	if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
	if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;

	if (maxIdx != index)
	{
		swap(arr[maxIdx], arr[index]);
		adjust(arr, len, maxIdx);
	}

}

// 堆排序
void heapSort(vector<int> &arr, int size)
{
	// 构建大根堆(从最后一个非叶子节点向上)
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		adjust(arr, size, i);
	}

	// 调整大根堆
	for (int i = size - 1; i >= 1; i--)
	{
		swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
		adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
	}
}

7. 希尔排序

2021.1.3 新增部分内容

插入排序的变种,生成一个希尔序列,通过交换不相邻的元素来达到局部有序,最后通过局部有序的数据来使用插入排序达到加速的目的。

vector<int> shellSort(vector<int> &v)
{
    int n = v.size();
    // 产生希尔序列
    while (h < n / 3)
         h = 3 * h + 1;
    while (h >= 1)
    {
        for (size_t i = h; i < n; i++)
        {
            for (size_t j = i; j >= h; j -= h)
            {
                if (v[j] < v[j - h])
                {
                    swap(v[j - h], v[j]);
                }
            }
        }
        h = h / 2;
    }
    return v;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值