左程云算法基础班整理(三)(c++版)

左程云算法基础班整理(三)

1.归并排序

(1)整体是递归,左边排好序加右边排好序+merge让整体有序
(2)让其整体有序的过程用了排外序方法
(3)利用master公式来求解时间复杂度
(4)当然可以用非递归实现,一切递归都能用非递归实现,其实就是把系统压栈改成手动压栈。

归并排序的实质是把比较行为变成了有序信息并传递,比n²要快。
为什么选择排序和插入排序是n²呢?因为浪费了大量的比较行为。
归并的时间复杂度使用master公式得来的
归并排序时间复杂度O(nlogn),空间上因为用了辅助数组,所以说是O(N)

1.1递归方法实现归并排序
#include<iostream>
#include<vector>
using namespace std;

void process(vector<int>& a, int L, int R);
void merge(vector<int>& a, int L, int M, int R);

void mergeSort(vector<int>& a)
{
	int n = a.size();
	if (n < 2)return;
	process(a, 0, n - 1);
}
//递归的思想是分而治之。
void process(vector<int>& a, int L, int R)
{
	//写终止条件,终止条件一般是到了最小子问题
	if (L == R)
	{
		return;
	}

	int mid = L + ((R - L) >> 1);
	//分而治之,处理子过程
	process(a, L, mid);//左有序
	process(a, mid + 1, R);//右有序
	merge(a, L, mid, R);//左右两个有序数组如何凑成一个新的有序数组
}
void merge(vector<int>& a, int L, int M, int R)
{

	//申请个辅助数组
	int* help = new int[R - L + 1];
	
	int i = 0;
	int p1 = L;
	int p2 = M + 1;
	while (p1 <= M && p2 <= R)
	{
		help[i++] = a[p1] <= a[p2] ? a[p1++] : a[p2++];
	}
	while (p1 <= M)//p2完了。则p1直接拼上即可
	{
		help[i++] = a[p1++];
	}
	while (p2 <= R)//p1完了。则p2直接拼上即可
	{
		help[i++] = a[p2++];
	}
	for (i = 0; i < R - L + 1; i++)//把辅助数组内的元素压回到原数组中。注意是L到R范围上变得有序,因此要L+i;
	{
		a[L+i] = help[i];
	}
}
int main()
{
	vector<int> a = { 1,343,5453,3213,5242,21,43,6576,4324,34,36547,544,24,214,3534543 };
	mergeSort(a);
	for (auto num : a)
	{
		cout << num << endl;
	}
	return 0;
}
1.2非递归方法实现归并排序

把系统压栈转化成手动压栈
非递归方法实现,相邻元素之间进行merge排序,2,4,8这样不断地进行merge,直到最后>n

#include<iostream>
#include<vector>
using namespace std;

void merge(vector<int>& a,int L,int M,int R)
{

	int* help = new int[R - L + 1];
	int i = 0;
	int p1 = L;
	int p2 = M + 1;
	while (p1 <= M && p2 <= R)
	{
		help[i++] = a[p1] <= a[p2] ? a[p1++] : a[p2++];
	}
	while (p1 <= M)
	{
		help[i++] = a[p1++];
	}
	while (p2 <= R)
	{
		help[i++] = a[p2++];
	}
	for (i = 0; i < R-L+1; i++)
	{
		a[L + i] = help[i];
	}
	delete[] help;
}

//非递归方法实现,相邻元素之间进行merge排序,2,4,8这样不断地进行merge,直到最后>n
void mergeSort(vector<int>& a)
{
	int n = a.size();
	if (n < 2)return;
	int mergeSize = 1;//当前有序的左组长度
	while (mergeSize < n)//logN
	{
		int L = 0;
		//0...
		while (L < n)
		{
			//L..M,左组(mergeSize)
			int m = L + mergeSize - 1;
			if (m >= n)
			{
				break;
			}
			//L..M  M+1...R(mergeSize)
			int r = min(m + mergeSize, n - 1);//小心越界的情况
			merge(a, L, m, r);
			L = r + 1;
		}
		//防止溢出
		if (mergeSize > n / 2)
		{
			break;
		}
		//左移一位,相当于乘2
		mergeSize <<= 1;
	}
}


int main()
{
	vector<int> a = { 121,32433,43,454,5,46,56,5,7,5,7,55342,32,3,2,3,242,4,3,5,32,543643,2 };
	mergeSort(a);
	for (auto num : a)
	{
		cout << num << endl;
	}
	
	return 0;
}

2.小和问题

2.1小和问题

在一个数组中,一个数左边比它小的数的总和,叫做数的小和,所有数的小和累加起来,叫做数组小和。求数组小和。 例子:[1,3,4,2,5]
1左边比1小的数,没有 3左边比3小的数,1 4左边比4小的数,1、3 2左边比2小的数,1 5左边比5小的数,1、3、4、2
所以数组的小和为1+1+3+1+1+3+4+2=16

小和问题,找左边有几个比它小的,等价是找右侧有几个比他大的。比如1右侧4个都比1大,所以1要记4次,3右侧两个比他大的,所以3记2次。4右侧一个大的,所以4记一次,2右侧一个大的,所以2记一次,5右侧没有比5大的,所以5不记。
总体下来,结果为:1×4+3×2+4 ×1+2×1+5×0 = 16

#include<iostream>
#include<vector>
using namespace std;

//提前声明
int merge(vector<int>& a, int L, int M, int R);
int process(vector<int>& a, int L, int R);

int SmallSum(vector<int>& a)
{
	int n = a.size();
	if (n < 2) return 0;
	return process(a, 0, n-1);
}

int process(vector<int>& a, int L, int R)
{
	if (L == R)return 0;
	int mid = L + ((R - L) >> 1);

	return process(a, L, mid)//左边的有序组里的小和
		+ process(a, mid + 1, R)//右边有序组里的小和
		+ merge(a, L, mid, R);//左右都有序,但是左右合起来不有序,所以还会产生一个小和。
}

int merge(vector<int>& a, int L,int M,int R)
{
	int* help = new int[R - L + 1];//辅助数组
	int i = 0;//辅助数组写入坐标
	int p1 = L;//左数组的起始位置
	int p2 = M + 1;//右数组的其实位置
	int res = 0;//多加了一个res返回值,因为要在归并过程中返回小和信息
	while (p1 <= M && p2 <= R)
	{
		res += a[p1] < a[p2] ? (R - p2 + 1) * a[p1] : 0;//找右边有多少个比他大的
		help[i++] = a[p1] < a[p2] ? a[p1++] : a[p2++];//只有左组严格小于右组时,才将左组写进去,否则都将右组写进help数组里面。
	}
	while (p1 <= M)
	{
		help[i++] = a[p1++];
	}
	while (p2 <= R)
	{
		help[i++] = a[p2++];
	}
	for (int i = 0; i < R - L + 1; i++)
	{
		a[L + i] = help[i];
	}
	delete[] help;
	return res;
}
int main()
{
	vector<int> a = {1,3,4,2,5};
	cout << SmallSum(a) << endl;
	system("pause");
	return 0;
}
2.2求逆序对

在一个数组中,求所有的逆序对,概念如下图所示
在这里插入图片描述这道题其实在求每一个数右边有多少个数比它小。所以和小和问题的区别就是大于小于号,和输出格式的问题。。。
当右边的数严格比左边小的时候,才产生个数。在相等的时候先拷贝左边的,保证右组时严格小于左组。

什么时候用mergeSort?当纠结整体数,右边几个数大,几个数小,左边几个数大,几个数小。都可以用mergesort,直接要总数。

3.快速排序

3.1经典问题:划分

给定一个数组arr,和一个整数num。请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
要求时间复杂度O(n),额外空间复杂度O(1);

分两个区,小于等于区和大于区。
其实就是记录小于等于区的坐标,然后不断地扩容。
在这里插入图片描述

#include<iostream>
#include<vector>
using namespace std;


void swap(vector<int>& a,int i,int j)
{
	a[i] = a[i] ^ a[j];
	a[j] = a[i] ^ a[j];
	a[i] = a[i] ^ a[j];
}

int partition(vector<int>& a, int L, int R)
{
	if (L > R)return -1;
	if (L == R)return L;
	int lessEqual = L - 1;//小于等于区的坐标
	int index = L;//遍历的坐标

	for(int index = L; index < R; index++)
	{
		if (a[index] <= a[R])
		{
			swap(a, index, ++lessEqual);
		}
		index++;
	}
	swap(a, ++lessEqual, R);
	return lessEqual;
}

int main()
{
	vector<int> a = { 1,333,12,545,121, 32};
	cout << partition(a, 0, 5) << endl;
	system("pause");
	return 0;
}

3.2荷兰国旗问题

这道问题的引申是荷兰国旗问题,何为荷兰国旗?三色!!!即分为小于区,等于区,大于区。
荷兰国旗问题:
给定一个数组arr,和一个整数num.请把小于num的数放在数组的左边,等于num的放在中间,大于num的数放在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<vector>
using namespace std;

void swap(vector<int>& a, int i, int j)
{
	a[i] = a[i] ^ a[j];
	a[j] = a[i] ^ a[j];
	a[i] = a[i] ^ a[j];
}

vector<int> netherlandsFlag(vector<int>& a, int L, int R)
{
	if(L > R)return { -1,-1 };
	if (L == R)return{ L,R };
	int less = L - 1;//<区,右边界
	int more = R;//>区,左边界
	int index = L;//遍历的指针,要保证遍历的指针从小于边界开始,到大于区边界结束
	while(index<more)
	{
		if (a[index] < a[R])
		{
			swap(a, ++less, index++);
		}
		else if(a[index]=a[R])
		{
			index++;
		}
		else
		{
			swap(a, index, --more);
		}
	}
	swap(a, more, R);
	return {less+1,more};
}



int main()
{
	vector<int> a = { 121,343,546,2,446,67,67 };
	vector<int> b = netherlandsFlag(a, 0, 6);

	cout << b[0] << b[1] << endl;
	system("pause");
	return 0;
}
3.3快速排序1.0

1.0基于划分,partition

#include<iostream>
#include<vector>
using namespace std;

void swap(vector<int>& a, int i, int j)
{
	a[i] = a[i] ^ a[j];
	a[j] = a[i] ^ a[j];
	a[i] = a[i] ^ a[j];
}
int partition(vector<int>& a, int L, int R)
{
	if (L > R)return -1;
	if (L == R) return L;
	int less = L - 1;
	int index = L;
	
	for (int index = L; index < R; index++)
	{
		if (a[index] < a[R])
		{
			swap(a, index, ++less);
		}
		index++;
	}
	swap(a, R, ++less);
	return less;
}
void process1(vector<int>& a, int L, int R)
{
	if (L >= R) return;

	// L..R partition a[R]  [   <=a[R]   a[R]    >a[R]  ]

	int M = partition(a, L, R);
	process1(a, L, M - 1);
	process1(a, M, R);
}
void quickSort(vector<int>& a)
{
	if (a.size() < 2)return;
	process1(a, 0, a.size() - 1);
}
3.4快速排序2.0

基于荷兰国旗问题

#include<iostream>
#include<vector>
using namespace std;

void swap(vector<int>& a, int i, int j)
{
	a[i] = a[i] ^ a[j];
	a[j] = a[i] ^ a[j];
	a[i] = a[i] ^ a[j];
}
vector<int> netherlandsFlag(vector<int>& a, int L, int R)
{
	if (L > R)return { -1,-1 };
	if (L == R)return{ L,R };
	int less = L - 1;//<区,右边界
	int more = R;//>区,左边界
	int index = L;//遍历的指针,要保证遍历的指针从小于边界开始,到大于区边界结束
	while (index < more)
	{
		if (a[index] < a[R])
		{
			swap(a, ++less, index++);
		}
		else if (a[index] = a[R])
		{
			index++;
		}
		else
		{
			swap(a, index, --more);
		}
	}
	swap(a, more, R);
	return { less + 1,more };
}
void process(vector<int>& a, int L, int R)
{
	if (L >= R) return;
	int mid = L + ((R - L) >> 1);
	vector<int> b = netherlandsFlag(a, L, R);
	process(a, 0, b[0]-1);
	process(a, b[1]+1, R);
}
void quickSort2(vector<int>& a)
{
	if (a.size() < 2)
	{
		return;
	}
	process(a, 0, a.size() - 1);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值