算法笔记第四章

4.1排序算法

4.1.1交换排序

4.1.1.1冒泡排序

#include<stdio.h>
#include<stdbool.h>
//用引用的方式交换a,b
void swap(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

void BubbleSort(int A[], int len) {
	for (int i = 0; i < len - 1; i++) {
		//每次新的循环flag置true
		bool flag = true;
		for (int j = 0; j < len - i - 1; j++) {
			if (A[j] > A[j + 1]) {
				swap(A[j], A[j + 1]);
				//本次发生交换,变换flag
				flag = false;
			}
			
		}
		//本次没有交换,说明可以退出
		if (flag) {
			return;
		}
	}
}

int main() {
	int arr[10] = { 3,5,1,-7,4,9,-6,8,10,4 };
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	printf("\n");
	BubbleSort(arr, 10);
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	return 0;
}
性能分析
稳定性

稳定的

空间复杂度

O(1)

时间复杂度

O( n 2 n^2 n2)

4.1.1.2快速排序

#include<stdio.h>
int Partation(int A[], int low, int high) {
	int pivot = A[low];
	while (low < high) {
		while (low<high && A[high]>=pivot)high--;
		A[low] = A[high];
		while (low < high && A[low] <= pivot)low++;
		A[high] = A[low];
	}
	A[low] = pivot;
	return low;//此时high==low
}

void QuickSort(int A[], int low, int high) {
	if (low < high) {
		int pivotpos = Partation(A, low, high);
		QuickSort(A, low, pivotpos - 1);
		QuickSort(A, pivotpos + 1, high);
	}
}

int main() {
	int arr[10] = { 3,5,1,-7,4,9,-6,8,10,4 };
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	printf("\n");
	QuickSort(arr, 0, 9);
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	return 0;
}
性能分析

快速排序是所有内部排序算法中平均性能最优的排序算法

若每次选中的基准将待排序序列划分为很不平均的两部分,则会导数递归深度增加,算法效率降低.

若初始序列有序或者逆序,则快速排序的性能最差(因为每次选择的基准都靠边)

稳定性

不稳定

空间复杂度

最好O( l o g 2 n log_2n log2n)

最坏O(n)

平均O( l o g 2 n log_2n log2n)

时间复杂度

最好O( n l o g 2 n nlog_2n nlog2n)

最坏O( n 2 n^2 n2)

4.1.2插入排序

4.1.2.1直接插入排序

#include<stdio.h>
void InsertionSort(int arr[], int len) {
	int i, j, temp;
	for (i = 1; i < len; i++) {
		if (arr[i] < arr[i - 1]) {
			temp = arr[i];
			for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
				arr[j + 1] = arr[j];
			}
			arr[j + 1] = temp;
		}
	}
}


int main() {
	int arr[10] = { 3,5,1,-7,4,9,-6,8,10,4 };
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	printf("\n");
	InsertionSort(arr, 10);
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	return 0;
}
性能分析
稳定性

空间复杂度

O(1)

时间复杂度

O( n 2 n^2 n2)

4.1.2.2希尔排序


性能分析
稳定性

希尔排序是不稳定的

空间复杂度

O(1)

时间复杂度

d=1时,时间复杂度为O( n 2 n^2 n2),就是直接插入排序

当n为某个特定范围时,可以达到O( n 1.3 n^{1.3} n1.3)

4.1.3选择排序

4.1.3.1简单选择排序

#include<stdio.h>
void swap(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

void SelectionSort(int arr[], int n) {
	for (int i = 0; i < n; i++) {
		int min = i;
		for (int j = i + 1; j < n; j++) {
			if (arr[j] < arr[min]) {
				min = j;
			}
		}
		if (min != i) {
			swap(arr[min], arr[i]);
		}
	}
}


int main() {
	int arr[10] = { 3,5,1,-7,4,9,-6,8,10,4 };
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	printf("\n");
	SelectionSort(arr, 10);
	for (int i = 0; i < 10; i++)
		printf("%d\t", arr[i]);
	return 0;
}
性能分析
稳定性

即可用于顺序表,也可用于链表

不稳定

空间复杂度

o(1)

时间复杂度

O( n 2 n^2 n2)

4.1.3.2堆排序


性能分析
稳定性

不稳定

空间复杂度

O(1)

时间复杂度

建堆时间O(n)

设每次调整时间时间复杂度为O(h)

堆排序的时间复杂度O( n l o g 2 n nlog_2n nlog2n)

4.1.4排序题和sort函数

第六章

? 4.1.5 PAT A1025

Programming Ability Test (PAT) is organized by the College of Computer Science and Technology of Zhejiang University. Each test is supposed to run simultaneously in several places, and the ranklists will be merged immediately after the test. Now it is your job to write a program to correctly merge all the ranklists and generate the final rank.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive number N (≤100), the number of test locations. Then N ranklists follow, each starts with a line containing a positive integer K (≤300), the number of testees, and then K lines containing the registration number (a 13-digit number) and the total score of each testee. All the numbers in a line are separated by a space.

Output Specification:

For each test case, first print in one line the total number of testees. Then print the final ranklist in the following format:

registration_number final_rank location_number local_rank

The locations are numbered from 1 to N. The output must be sorted in nondecreasing order of the final ranks. The testees with the same score must have the same rank, and the output must be sorted in nondecreasing order of their registration numbers.

Sample Input:

2
5
1234567890001 95
1234567890005 100
1234567890003 95
1234567890002 77
1234567890004 85
4
1234567890013 65
1234567890011 25
1234567890014 100
1234567890012 85

Sample Output:

9
1234567890005 1 1 1
1234567890014 1 2 1
1234567890001 3 1 2
1234567890003 3 1 2
1234567890004 5 1 4
1234567890012 5 2 2
1234567890002 7 1 5
1234567890013 8 2 3
1234567890011 9 2 4
#include<cstdio>
#include<cstdbool>
#include<string.h>
#include<algorithm>
using namespace std;

struct student {
	char id[15];
	int score;
	int final_rank;
	int location_number;
	int local_rank;
}stu[30010];

bool cmp(student a,student b) {
	if (a.score != b.score)
		return a.score > b.score;
	else
		return strcmp(a.id, b.id) < 0;
}
//算法笔记
int main() {
	int n, k, num = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &k);
		for (int j = 0; j < k; j++) {
			scanf("%s %d", stu[num].id, &stu[num].score);
			stu[num].location_number = i;
			num++;
		}
		sort(stu + num - k, stu + num, cmp);
		stu[num - k].local_rank = 1;
		for (int j = num - k + 1; j < num; j++) {
			if (stu[j].score == stu[j - 1].score) {
				stu[j].local_rank = stu[j - 1].local_rank;
			}
			else {
				//stu[j].local_rank = stu[j - 1].local_rank + 1;
				stu[j].local_rank = j + 1 - (num - k);
			}
		}
	}
	printf("%d\n", num);
	sort(stu, stu + num, cmp);
	int r = 1;//当前考生排名
	for (int i = 0; i < num; i++) {
		if (i > 0 && stu[i].score != stu[i - 1].score) {
			r = i + 1;
		}
		printf("%s ", stu[i].id);
		printf("%d %d %d\n", r, stu[i].location_number, stu[i].local_rank);
	}
	return 0;
}

4.2散列

4.2.1哈希和整数哈希

4.2.2字符串哈希

int hashFunc(char s[], int len) {
	int id = 0;
	for (int i = 0; i < len; i++) {
		if (s[i] >= 'A' && s[i] <= 'Z') {
			id = id * 52 + s[i] - 'A';
		}
		else if (s[i] >= 'a' && s[i] <= 'z') {
			id = id * 52 + s[i] - 'a' + 26;
		}
	}
	return id;
}

4.3递归

4.3.1分治

4.3.2递归

4.3.2.1 n!和fib

#include<stdio.h>
//阶乘
int F(int n) {
	if (n == 0) return 1;
	else return F(n - 1) * n;
}

//fib第n项
int fib(int n) {
	if (n == 0 || n == 1) return 1;
	else return fib(n - 1) + fib(n - 2);
}

int main() {
	int n;
	scanf("%d", &n);
	printf("%d", F(n));

	int m;
	scanf("%d", &m);
	printf("%d", fib(m));

	return 0;
}

P114的图4-3突然对fib有了新的理解,之前的理解是我以为我会了,今天才又进一步的理解了–2022/1/17

? 4.3.2.2 全排列

#include<cstdio>
const int maxn = 11;
int  n, P[maxn], hashTable[maxn] = { false };

void generateP(int index) {
	if (index == n + 1) {
		for (int i = 1; i <= n; i++) {
			printf("%d", P[i]);
		}
		printf("\n");
		return;
	}
	for (int x = 1; x <= n; x++) {
		if (hashTable[x] == false) {
			P[index] = x;
			hashTable[x] = true;
			generateP(index + 1);
			hashTable[x] = false;
		}
	}
}

int main() {
	n = 3;
	generateP(1);
	return 0;
}

? 4.3.2.3 n皇后

//暴力法
int count = 0;
void generateP(int index) {
	if (index == n + 1) {
		bool flag = true;
		for (int i = 1; i <= n; i++) {
			for (int j = i + 1; j <= n; j++) {
				if (abs(i - j) == abs(P[i] - P[j])) {
					flag = false;
				}
			}
		}
		if (flag) count++;
		return;
	}
	for (int x = 1; x <= n; x++) {
		if (hashTable[x] == false) {
			P[index] = x;
			hashTable[x] = true;
			generateP(index + 1);
			hashTable[x] = false;
		}
	}
}
//回溯法
void generateP(int index) {
	if (index == n + 1) {
		count++;
		return;
	}
	for (int x = 1; x <= n; x++) {
		if (hashTable[x] == false) {
			bool flag = true;
			for (int pre = 1; pre < index; pre++) {
				if (abs(index - pre) == abs(x - P[pre])) {
					flag = false;
					break;
				}
			}
			if (flag) {
				P[index] = x;
				hashTable[x] = true;
				generateP(index + 1);
				hashTable[x] = false;
			}
		}
	}
}

4.4贪心

4.4.1简单贪心

4.4.1 PAT B1020

月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。

注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。

输入格式:

每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。

输出格式:

对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。

输入样例:

3 20
18 15 10
75 72 45

输出样例:

94.50
#include<cstdio>
#include<algorithm>
using namespace std;
struct mooncake {
	double store;
	double sell;
	double price;
}cake[1010];

bool cmp(mooncake m1, mooncake m2) {
	return m1.price > m2.price;
}

int main() {
	int n;
	double D;
	scanf("%d %lf", &n, &D);
	for (int i = 0; i < n; i++) {
		scanf("%lf", &cake[i].store);
	}
	for (int i = 0; i < n; i++) {
		scanf("%lf", &cake[i].sell);
		cake[i].price = cake[i].sell / cake[i].store;
	}
	sort(cake, cake + n, cmp);
	double earning = 0;
	for (int i = 0; i < n; i++) {
		if (cake[i].store <= D) {
			D -= cake[i].store;
			earning += cake[i].sell;
		}
		else {
			earning += cake[i].price * D;
			break;
		}
	}
	printf("%.2f", earning);
	return 0;
}

4.4.1 PAT B1023

给定数字 0-9 各若干个。你可以以任意顺序排列这些数字,但必须全部使用。目标是使得最后得到的数尽可能小(注意 0 不能做首位)。例如:给定两个 0,两个 1,三个 5,一个 8,我们得到的最小的数就是 10015558。

现给定数字,请编写程序输出能够组成的最小的数。

输入格式:

输入在一行中给出 10 个非负整数,顺序表示我们拥有数字 0、数字 1、……数字 9 的个数。整数间用一个空格分隔。10 个数字的总个数不超过 50,且至少拥有 1 个非 0 的数字。

输出格式:

在一行中输出能够组成的最小的数。

输入样例:

2 2 0 0 0 3 0 0 1 0

输出样例:

10015558
#include<stdio.h>
int main() {
	int count[10];
	for (int i = 0; i < 10; i++) {
		scanf("%d", &count[i]);
	}
	for (int i = 1; i < 10; i++) {
		if (count[i] > 0) {
			printf("%d", i);
			count[i]--;
			break;
		}
	}
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < count[i]; j++) {
			printf("%d", i);
		}
	}
	return 0;
}

? 4.4.2 区间贪心

#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 110;
struct Interval {
	int x, y;
}I[maxn];

bool cmp(Interval a, Interval b) {
	if (a.x != b.x) {
		return a.x > b.x;
	}
	else {
		return a.y < b.y;
	}
}
int main() {
	int n;
	while (scanf("%d", &n), n != 0) {
		for (int i = 0; i < n; i++) {
			scanf("%d%d", &I->x, &I->y);
		}
		sort(I, I + n, cmp);
		//ans记录不相交的区间个数,lastX记录释放一个被选中区间的左端点
		int ans = 1, lastX = I[0].x;
		for (int i = 1; i < n; i++) {
			if (I[i].y <= lastX) {
				lastX = I[i].x;
				ans++;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

4.5二分

4.5.1二分查找

//找等于x的元素位置
#include<stdio.h>
int binarySearch(int A[], int left, int right, int x) {
	int mid;
	while (left <= right) {
		mid = (left + right) / 2;
		if (A[mid] == x) {
			return mid;
		}
		else if (A[mid] > x) {
			right = mid - 1;
		}
		else {
			left = mid + 1;
		}
	}
	return -1;
}

int main() {
	int A[10] = { 1,3,4,6,7,8,10,11,12,15 };
	printf("%d %d\n", binarySearch(A, 0, 9, 6), binarySearch(A, 0, 9, 9));
	return 0;
}
-----------------------------------------;
3 -1;
============================================;
//找第一个大于等于x的元素的位置
int lower_bound(int A[], int left, int right, int x) {
	int mid;
	while (left < right) {
		mid = (left + right) / 2;
		if (A[mid] >= x) {
			right = mid;
		}
		else {
			left = mid + 1;
		}
	}
	return left;//退出条件left==right
}
=============================================;
//找第一个大于x的元素的位置
int upper_bound(int A[], int left, int right, int x) {
	int mid;
	while (left < right) {
		mid = (left + right) / 2;
		if (A[mid] > x) {
			right = mid;
		}
		else {
			left = mid + 1;
		}
	}
	return left;
}
==============================================;
//模板

int solve(int left, int right) {
	int mid;
	while (left < right) {//[]区间使用
		mid = (left + right) / 2;
		if (条件成立) {
			right = mid;
		}
		else {
			left = mid + 1;
		}
	}
	return left;
}

4.5.2 二分拓展

//求根号2
const double eps = 1e-5;
double f(double x) {
	return x * x;
}
double calSqrt() {
	double left = 1, right = 2, mid;
	while (right - left > eps) {
		mid = (left + right) / 2;
		if (f(mid) > 2) {
			right = mid;
		}
		else {
			left = mid;
		}
	}
	return mid;
}

? 4.5.3快速幂(也叫二分幂)

4.6.3快速排序(随机版)

C语言中产生随机数

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main() {
	srand((unsigned)time(NULL));
	for (int i = 0; i < 10; i++) {
		printf("%d ", rand());
	}
	return 0;
}

给定范围[a.b]之间的随机数 rand()%(b-a+1)+a

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main() {
	srand((unsigned)time(NULL));
	for (int i = 0; i < 10; i++) {
		printf("%d ", rand() % 2);//[0,1]
	}
	printf("\n");
	for (int i = 0; i < 10; i++) {
		printf("%d ", rand() % 5 + 3);//[3,7]
	}
	return 0;
}
------------------------------------;
1 1 1 1 0 0 1 1 1 1
7 5 3 4 6 7 6 7 3 5

随机快排

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int randPartation(int arr[], int left, int right) {
	//生成[left,right]的随机数
	int p = round(1.0 * rand() / RAND_MAX * (right - left) + left);
	swap(arr[p], a[left]);
	//原partation
	int temp = arr[left];
	while (left < right) {
		while (left<right && arr[right]>temp) right--;
		arr[left] = arr[right];
		while (left < right && arr[left] < temp) left++;
		arr[right] = arr[left];
	}
	arr[left] = temp;
	return left;
}

4.6 two pointers双指针

4.6.1 双指针

给定递增有序序列arr和正整数M,找到不同位置的两个数a,b,使得a+b==M

两层循环
//时间复杂度O(n^2),比较大
for (int i = 0; i < len; i++) {
	for (int j = i + 1; j < len; j++) {
		if (a[i] + a[j] == M) {
			printf("%d %d\n", a[i], a[j]);
		}
	}
}
双指针
int i = 0, j = len - 1;
while (i < j) {
	if (a[i] + a[j] == M) {
		printf("%d %d", a[i], a[j]);
		i++;
		j--;
	}
	//a[i] + a[j] < M,接下来向大的方向移动
	else if (a[i] + a[j] < M) {
		i++;
	}
	//a[i] + a[j] > M,接下来向小的方向移动
	else {
		j--;
	}
}

4.6.2归并排序

递归实现
const int maxn = 100;
void merge(int arr[], int l1, int r1, int l2, int r2) {
	int i = l1, j = l2;
	int temp[maxn], index = 0;
	while (i < r1 && j < r2) {
		if (arr[i] <= arr[j]) {
			temp[index++] = arr[i++];
		}
		else {
			temp[index++] = arr[j++];
		}
	}
	while(i<r1) temp[index++] = arr[i++];
	while(j<r2) temp[index++] = arr[j++];
	for (int i = 0; i < index; i++) {
		arr[l1 + i] = temp[i];
	}
}

void mergeSort(int arr[], int left, int right) {
	if (left < right) {
		int mid = (left + right) / 2;
		mergeSort(arr, left, mid);
		mergeSort(arr, mid + 1, right);
		merge(arr, left, mid, mid + 1, right);
	}
}

4.7其他高效算法

4.7.1 打表

4.7.2活用递推

? 4.7.2 PAT B1040/A1093

B1040

字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T);第二个 PAT 是第 3 位(P),第 4 位(A),第 6 位(T)。

现给定字符串,问一共可以形成多少个 PAT

输入格式:

输入只有一行,包含一个字符串,长度不超过105,只包含 PAT 三种字母。

输出格式:

在一行中输出给定字符串中包含多少个 PAT。由于结果可能比较大,只输出对 1000000007 取余数的结果。

输入样例:

APPAPT

输出样例:

2

A1093

The string APPAPT contains two PAT's as substrings. The first one is formed by the 2nd, the 4th, and the 6th characters, and the second one is formed by the 3rd, the 4th, and the 6th characters.

Now given any string, you are supposed to tell the number of PAT's contained in the string.

Input Specification:

Each input file contains one test case. For each case, there is only one line giving a string of no more than 105 characters containing only P, A, or T.

Output Specification:

For each test case, print in one line the number of PAT's contained in the string. Since the result may be a huge number, you only have to output the result moded by 1000000007.

Sample Input:

APPAPT

Sample Output:

2


? 4.7.3 随机选择算法


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值