【算法笔记】【排序】ICPC训练联盟2021寒假冬令营(4、5)笔记

PPT:第五章 排序

三个基础排序
两数交换
void swap(int* x, int* y) {
	int temp;
	temp = *x;
	*x = *y;
	*y = temp;
}
冒泡排序
//冒泡排序 每次遍历两两比较,将最大元素互换到最后一个位置
void bubble_sort(int arr[], int len) {
	for (int i = 0; i < len-1; i++) {
		for (int j = 0; j < len - 1 - i; j++) {//第一轮换由于是两两比较,一定会吧最大的元素换到最后面,则下一轮可不与最后一个元素比较
			if (arr[j] > arr[j + 1])
				swap(&arr[j], &arr[j + 1]);
		}
	}
}
插入排序
//直接插入排序 每次遍历将未排序数组第一个数插入到已排序数组中
void insertion_sort(int arr[], int len)
{
	for (int i = 1; i < len; i++) {//从下标为1的元素开始选择合适的位置插入
		int key = arr[i];//key为本轮要插入的数据
		int j = i - 1;//比较区间为[0,i-1]的数据,即已排列好的递增序列
		while (j >= 0 && key < arr[j]) {//从已经排序的序列最右边的开始比较,找到比其小的数(key应插入在第一个比key小的数的后面)。
		//当key>=arr[j]或j<0时跳出,因为与key比较的是递增序列,我们只需找到比key小的第一个数
			arr[j + 1] = arr[j];//将比key大的数(arr[j])逐次往后移,给key让空
			j--;
		}
		arr[j + 1] = key;//将key插入到比key小的数的后一个位置。
	}
}
选择排序
//选择排序 每次遍历找到最小元素,把他放到已排序数组的最后一个位置
void selection_sort(int arr[], int len) {
	int i, j;
	for (i = 0; i < len - 1; i++) {
		int min = i;
		for (j = i + 1; j < len; j++) {//找到最小的元素下标
			if (arr[j] < arr[min])
				min = j;
		}
		swap(&arr[min], &arr[i]);//把最小的元素与第i个位置互换
	}
}

总。

#include<algorithm>
#include<iostream>
using namespace std;
void swap(int* x, int* y) {
	int temp;
	temp = *x;
	*x = *y;
	*y = temp;
}
//直接插入排序 每次遍历将未排序数组第一个数插入到已排序数组中
void insertion_sort(int arr[], int len)
{
	for (int i = 1; i < len; i++) {//从下标为1的元素开始选择合适的位置插入
		int key = arr[i];//key为本轮要插入的数据
		int j = i - 1;//比较区间为[0,i-1]的数据,即已排列好的递增序列
		while (j >= 0 && key < arr[j]) {//从已经排序的序列最右边的开始比较,找到比其小的数(key应插入在第一个比key小的数的后面)。
		//当key>=arr[j]或j<0时跳出,因为与key比较的是递增序列,我们只需找到比key小的第一个数
			arr[j + 1] = arr[j];//将比key大的数(arr[j])逐次往后移,给key让空
			j--;
		}
		arr[j + 1] = key;//将key插入到比key小的数的后一个位置。
	}
}
//冒泡排序 每次遍历两两比较,将最大元素互换到最后一个位置
void bubble_sort(int arr[], int len) {
	for (int i = 0; i < len-1; i++) {
		for (int j = 0; j < len - 1 - i; j++) {//第一轮换由于是两两比较,一定会吧最大的元素换到最后面,则下一轮可不与最后一个元素比较
			if (arr[j] > arr[j + 1])
				swap(&arr[j], &arr[j + 1]);
		}
	}
}
//选择排序 每次遍历找到最小元素,把他放到已排序数组的最后一个位置
void selection_sort(int arr[], int len) {
	int i, j;
	for (i = 0; i < len - 1; i++) {
		int min = i;
		for (j = i + 1; j < len; j++) {//找到最小的元素下标
			if (arr[j] < arr[min])
				min = j;
		}
		swap(&arr[min], &arr[i]);//把最小的元素与第i个位置互换
	}
}
int main() {
	int n = 10;
	int arr[10] = { 2,67,34,56,3,45,60,86,32,5 };
	//insertion_sort(arr, n);
	//bubble_sort(arr, n);
	selection_sort(arr, n);
	for (int i = 0; i < n; i++)
		cout << arr[i] << " ";
	cout << endl;
	return 0;
}
例题
4.G POJ 2388 Who’s in the Middle 排个序就行了
4.H UVA 299 Train Swapping 冒泡排序记一下交换次数
#include <iostream>
using namespace std;
#define N  10007 
int sort(int arr[], int n) {
	int num = 0;
	for (int i = 0; i < n - 1; i++) {
		for (int j = 0; j < n - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				num++;
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
	return num;
}
int main() {
	int arr[N];
	int num = 0;
	int n;
	cin >> n;
	while (n--) {
		cin >> num;
		for (int i = 0; i < num; i++)
			cin >> arr[i];
		cout << "Optimal train swapping takes " << sort(arr, num) << " swaps." << endl;
	}
	return 0;
}
4.I POJ 1007 DNA Sorting 冒泡找一下有多少逆序对,再根据逆序对数排一下序即可

##做题一定要细心!!!!!!!!!!m和n又搞混了。。。。。

#include <iostream>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define N  107 


string s[N];
void insertion_sort(int arr[], int len){
	for(int i=1;i<len;i++){
		int key=arr[i];
		string keys;
		keys=s[i];
		int j=i-1;
		while(j>=0&&key<arr[j]){
			arr[j+1]=arr[j];
		    s[j+1]=s[j];
			j--;
		}
		arr[j+1]=key;
		s[j+1]=keys;
	}
}
int main(){
	int n,m;
	int nixu[N];
	cin>>n>>m;
	getchar();
	for(int k=0;k<m;k++){
		cin>>s[k];
		int ans=0;
    	for(int i=0;i<n-1;i++){
			for(int j=i+1;j<n;j++){
				if(s[k][i]>s[k][j])
					ans++;
			}
		}
		nixu[k]=ans;
	}
	insertion_sort(nixu,m);
	for(int i=0;i<m;i++){
		cout<<s[i]<<endl;
	}
	return 0;
}


更新
归并排序

在这里插入图片描述
在这里插入图片描述

参考代码

 void Merge(int r[],int temp[],int s,int m,int t){
//将数组r的两个连续的有序序列:第s到第m个元素,第m+1到第t个元素,合并产生一个有序序列:第s到第t个元素的有序序列	
	int i=s;
	int j=m+1;//i,j是两个连续的有序序列的开始位置 
	int k=i;//k 临时数组temp的下标
	while(i<=m&&j<=t){
		if(r[i]<=r[j])//谁小就先把谁放进去 
			temp[k++]=r[i++];
		else
			temp[k++]=r[j++];
	}	
		while(i<=m)//剩余部分依次放入临时数组(俩while只会执行一个) 
			temp[k++]=r[i++];
		while(j<=t)
			temp[k++]=r[j++];
		for(i=s;i<=t;i++)//将临时数组中的内容拷贝回原数组中 
			r[i]=temp[i];				
}
void MergeSort(int r[],int temp[],int s,int t){//分解:把 n 个元素组成的序列r分解为n 个长度为1的有序子表;
if(s==t)//分解为长度为1的有序子表 
	return;
else
{
	int m=(s+t)/2;
	MergeSort(r,temp,s,m) ;//对左边序列进行递归
	MergeSort(r,temp,m+1,t);//对右边序列进行递归
	Merge(r,temp,s,m,t);//合并  即r[s]~r[m]合并成有序数列 
 } 
	
}
例题例题
5.A POJ1084 Brainman

利用归并排序求出逆序对即可。
最近脑子不太好使,老是看错输入输出,自认为第二行前面的10是数组的第一个元素,还寻思了半天为啥 10 1 2 3 4 5 6 7 8 9逆序对是0。。。。。。
还有Dev函数没有返回值的话不会报错,,提交的时候一直显示语法错误,一直以为是头文件的事情,没看到代码里有个毫无关系的函数还没有返回值。。。。

题解题解

在这里插入图片描述

#include<iostream>
#include<cstdio>
using namespace std;
#define N  1007 
int num = 0;
int arr[N];
int temp[N];
void Merge(int a[], int temp[], int l, int m, int r) {
	int i = l;
	int j = m + 1;
	int k = l;
	while (i <= m && j <= r) {
		if (a[i] <= a[j]) {
			temp[k++] = a[i++];
		}
		else {
			temp[k++] = a[j++];
			num += m - i + 1;
		}
	}
	while (i <= m)
		temp[k++] = a[i++];
	while (j <= r)
		temp[k++] = a[j++];
	for (i = l; i <= r; i++)
		a[i] = temp[i];
}
void MergeSort(int a[], int temp[], int l, int r) {
	if (l == r)
		return;
	else {
		int m = (l + r) / 2;
		MergeSort(a, temp, l, m);
		MergeSort(a, temp, m + 1, r);
		Merge(a, temp, l, m, r);//合并 
	}
}

int main() {
	int T;
	cin >> T;
	for (int t = 1; t <= T; t++) {
	
		num = 0;
		int len = 0;
		cin>>len;
		for(int i=0;i<len;i++)
			cin>>arr[i];
		MergeSort(arr, temp, 0, len - 1);
		cout << "Scenario #" << t << ":" << endl;
		cout << num << endl << endl;

	}

	return 0;
}

5.B POJ 2299,ZOJ 2386,UVA 10810 Ultra-QuickSort

同上题,求出逆序对即可
注意取值范围,结果要设为longlong

#include<iostream>
#include<cstdio>
using namespace std;
#define N  500007 
//int num = 0;
long long num = 0;
int arr[N];
int temp[N];
void Merge(int a[], int temp[], int l, int m, int r) {
	int i = l;
	int j = m + 1;
	int k = l;
	while (i <= m && j <= r) {
		if (a[i] <= a[j]) {
			temp[k++] = a[i++];
		}
		else {
			temp[k++] = a[j++];
			num += m - i + 1;
		}
	}
	while (i <= m)
		temp[k++] = a[i++];
	while (j <= r)
		temp[k++] = a[j++];
	for (i = l; i <= r; i++)
		a[i] = temp[i];
}
void MergeSort(int a[], int temp[], int l, int r) {
	if (l == r)
		return;
	else {
		int m = (l + r) / 2;
		MergeSort(a, temp, l, m);
		MergeSort(a, temp, m + 1, r);
		Merge(a, temp, l, m, r);//合并 
	}
}

int main() {
	int len = 0;
	while(scanf("%d",&len)!=EOF&&len!=0) {
		num = 0;
		for(int i=0;i<len;i++)
			cin>>arr[i];
		MergeSort(arr, temp, 0, len - 1);
		cout << num << endl;
	}
	return 0;
}

快速排序

在这里插入图片描述

又对比初始数组和最终数组,我们可以看出初始将a[0]作为pivot,从右向左找第一个比pivot小的数,a[high],赋值给a[0],再从左向右找第一个比pivot大的数,a[low],赋值到a[high]的位置上,最后再将pivot,既原始a[0]处的值赋值到a[low]的位置上。最后返回新的low的下标作为下一次的基准值。在这里插入图片描述
参考代码

Parition(int a[],int low,int high){
	int pivot=a[low];//设下标low为“基准” 
	while(low<high){
		while(low<high&&a[high]>=pivot){//从右向左找比pivot小的值
			high--;	 
		} 
		a[low]=a[high];
		while(low<high&&a[low]<=pivot){//从左向右找比pivot大的第一个值 
			low++;
		}
		a[high]=a[low]; 
	}
	a[low]=pivot;//
	return low;//返回新 low 的位置,作为分界 
}
void QuickSort(int a[],int low,int high){
	if(low<high){
		int pivot=Parition(a,low,high);
		QuickSort(a,low,pivot-1);
		QuickSort(a,pivot+1,high);
	}
}

俩O(nlog2n)的排序 总。

#include <iostream>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define N  10 
void Merge(int r[],int temp[],int s,int m,int t){
//将数组r的两个连续的有序序列:第s到第m个元素,第m+1到第t个元素,合并产生一个有序序列:第s到第t个元素的有序序列	
	int i=s;
	int j=m+1;//i,j是两个连续的有序序列的开始位置 
	int k=i;//k 临时数组temp的下标
	while(i<=m&&j<=t){
		if(r[i]<=r[j])//谁小就先把谁放进去 
			temp[k++]=r[i++];
		else
			temp[k++]=r[j++];
	}	
		while(i<=m)//剩余部分依次放入临时数组(俩while只会执行一个) 
			temp[k++]=r[i++];
		while(j<=t)
			temp[k++]=r[j++];
		for(i=s;i<=t;i++)//将临时数组中的内容拷贝回原数组中 
			r[i]=temp[i];				
}
void MergeSort(int r[],int temp[],int s,int t){//分解:把 n 个元素组成的序列r分解为n 个长度为1的有序子表;
if(s==t)//分解为长度为1的有序子表 
	return;
else
{
	int m=(s+t)/2;
	MergeSort(r,temp,s,m) ;//对左边序列进行递归
	MergeSort(r,temp,m+1,t);//对右边序列进行递归
	Merge(r,temp,s,m,t);//合并  即r[s]~r[m]合并成有序数列 
 } 	
}
Parition(int a[],int low,int high){
	int pivot=a[low];//设下标low为“基准” 
	while(low<high){
		while(low<high&&a[high]>=pivot){//从右向左找比pivot小的值
			high--;	 
		} 
		a[low]=a[high];
		while(low<high&&a[low]<=pivot){//从左向右找比pivot大的第一个值 
			low++;
		}
		a[high]=a[low]; 
	}
	a[low]=pivot;//
	return low;//返回新 low 的位置,作为分界 
}
void QuickSort(int a[],int low,int high){
	if(low<high){
		int pivot=Parition(a,low,high);
		QuickSort(a,low,pivot-1);
		QuickSort(a,pivot+1,high);
	}
}
int main() {
	int arr[N]={5,64,36,343,6,24,1,4,8,29};
	int temp[N]={0};
	//MergeSort(arr,temp,0,9);
	QuickSort(arr,0,9); 
	for(int i=0;i<10;i++)
		cout<<arr[i]<<" ";
	return 0;
}
例题例题
5.C Who’s in the Middle

//这次要用快速排序做一下,输出中间数即可

#include<iostream>
#include<cstdio>
using namespace std;
#define N  500007 
int a[N];
int partition(int l, int r) {
	int pivot = a[l];
	while (l < r) {
		while (l < r && pivot <= a[r])
			r--;
		a[l] = a[r];
		while (l < r && pivot >= a[l])
			l++;
		a[r] = a[l];
	}
	a[l] = pivot;
	return l;
}
void quick_sort(int l, int r) {
	if (l < r) {
		int m = partition(l, r);
		quick_sort(l, m);
		quick_sort(m + 1, r);
	}
}
int main() {
	int n;
	while (~scanf("%d", &n)) {
		for (int i = 0; i < n; i++)
			scanf("%d", &a[i]);
		quick_sort(0, n - 1);

		cout << a[n / 2] << endl;
	}
	return 0;
}

利用sort函数排序

例题
5.D sort 给你n个整数,请按从大到小的顺序输出其中前m大的数。

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 1000007 
int a[N];
bool cmp(int a,int b){
	return a>b;

}
int main() {
	int n,m;
	while(cin>>n>>m){
		for(int i=0;i<n;i++)
			scanf("%d",&a[i]);
		sort(a,a+n,cmp);
		for(int i=0;i<m-1;i++)
			cout<<a[i]<<" ";
		cout<<a[m-1]<<endl;	
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值