目录
第三章 排序与查找
3.1 排序
例题3.1 排序(华科复试上机题)
1、思路
- 排序这一块儿挺重要的,初试结束之后就没再看过了,经典排序算法的实现忘得差不多了,用这一题重新捡起来。
- C++ algorithm 库中的sort函数:sort(first,last,comp) // first和last为待排序序列的起始地址arr和结束地址arr+n,comp为排序方式,默认升序。如果要降序排列或自定义排列,可以编写比较函数来实现。
- 去年备考的笔记还在,翻出来复习一遍之后再用不同的排序方法都实现一遍。
2、解答
①直接插入排序
// 1.直接插入排序
void insertSort(int a[], int n) {
int i, j;
// 将第2~n个元素依次插入已排好序的队列中
for ( i = 1 ; i < n ; i++ ) {
int temp;
// 只有a[i]<a[i-1](已排序好的最后一个元素)时才需要移动
if ( a[i] < a[i - 1] ) {
// temp暂存待排序的a[i]
temp = a[i];
// 所有大于temp的元素都后移一位
for ( j = i - 1 ; a[j] > temp ; --j ) {
a[j + 1] = a[j];
}
// 将待排序元素插入最终位置
a [j + 1] = temp;
}
}
}
②折半插入排序
//2.折半插入排序
void insertSort2(int a[], int n) {
int i, j, low, high, mid,temp;
// 将第2~n个元素依次插入已排好序的队列中
for ( i = 2; i <= n ; i++ ) {
temp = a[i];
// 折半查找的两个端点值
low = 1, high = i-1;
// while循环条件:low<=high
while ( low <= high ) {
mid = (low + high) / 2;
if ( temp < a[mid] )
high = mid-1; // 查找左半子表
else
low = mid+1; // 查找右半子表
}
for ( j = i - 1 ; j >= high + 1 ; --j ) {
a[j + 1] = a[j];
}
// 将待排序元素插入最终位置
a [high + 1] = temp;
}
}
③希尔排序
//3.希尔排序
void shellSort(int a[], int n) {
int i, j, dk;
for ( dk = n / 2 ; dk >= 1 ; dk = dk / 2 ) { // 步长变化
for ( i = dk + 1 ; i <= n ; i++ ) {
// 将a[i]插入有序增量序列
if ( a[i] < a[i - dk] ) {
a[0] = a[i];
// 注意循环条件
for ( j = i - dk ; j > 0 && a[j] > a[0] ; j -= dk ) {
a[j + dk] = a[j];
}
a[j + dk] = a[0];
}
}
}
}
④冒泡排序
//4.冒泡排序
void bubbleSort(int a[],int n){
int i,j;
bool flag;
for ( i = 0 ; i < n ; i++ ) {
// 标志本趟冒泡是否发生交换
flag = false;
for ( j = n-1 ; j > i ; j-- ) {
int temp;
if ( a[j] < a[j-1] ) {
temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
flag = true;
}
}
// 本次冒泡没有发生交换,说明已有序
if (!flag)
return;
}
}
⑤快速排序
一个大坑:写的时候把pivot写成了a[pivot],结果怎么都不对,自己来回对了几遍也没发现问题……
//5.快速排序
int Partition(int a[], int low, int high) {
// 选取第一个元素作为枢轴元素
int pivot = a[low];
while (low < high) {
// high所指向的元素大于等于枢轴元素,--high
while (low < high && a[high] >= pivot ) --high;
// 比枢轴元素小的移到左边
a[low] = a[high];
// low所指向的元素小于等于枢轴元素,++low
while (low < high && a[low] <= pivot ) ++low;
// 比枢轴元素大的移到右边
a[high] = a[low];
}
// 跳出循环时low=high,枢轴元素存在到a[high]
a[low] = pivot;
return low;
}
void quickSort(int a[], int low, int high) {
if ( low < high ) {
int pivotpos = Partition(a, low, high);
quickSort(a, low, pivotpos - 1);
quickSort(a, pivotpos + 1, high);
}
}
例题3.2 成绩排序(清华大学复试上机题)
1、思路
- 处理多个学生学号和成绩的时候,发现直接输入不行。看了解析发现这里是对结构体进行排序,需要先将学生抽象为一个结构体,用C++更方便。
- 排序的要求:
- 成绩相等:比较学号,从小到大排序
- 成绩不等:比较成绩,从小到大排序
- 排序利用C++ alogrithm库中的sort函数可以实现,只需要编写自定义排序函数comp,特别注意comp函数的写法。
2、源代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
// 定义学生结构体
struct Student {
int id;
int score;
};
// 自定义比较函数
bool comp (Student a, Student b) {
// 成绩相等比较学号
if (a.score == b.score)
return a.id < b.id;
else
// 成绩不等比较成绩
return a.score < b.score;
}
int main() {
int n;
scanf("%d\n", &n);
Student arr[n];
for (int i = 0 ; i < n ; i++) {
scanf("%d %d\n", &arr[i].id, &arr[i].score);
}
sort(arr, arr + n, comp);
for (int i = 0 ; i < n ; i++) {
printf("%d %d\n", arr[i].id, arr[i].score);
}
return 0;
}
例题3.3 成绩排序(清华大学复试上机题)
1、思路
- 按照上一题一样的思路哐哐写,写了两个自定义排序函数,用户输入0调用降序,输入1调用升序。
- 不出意外没通过,看了其他人解法的评论区发现sort排序是不稳定排序,而题目要求相同成绩按照先录入者排序在前的规则处理。
- stable_sort(first,last,cmp) 函数可以实现稳定排序。
- 由于学生信息中包含姓名,所以输入输出选用C++ 的cin和cout会更方便。
2、总结
- 书上给的解法是在学生结构体中多定义了一个int型id属性,输入学生信息时依次id赋值,比较成绩时相同成绩再继续比较id,id小的即先录入的,符合题目要求。
- 也可以自己编写稳定的排序算法(比如冒泡排序)来实现。
3、源代码
// version 1 : stable_sort()实现
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
// 定义学生结构体
struct Student {
string name;
int score;
};
// 升序或降序
int flag;
// 自定义排序函数
bool cmp(Student a, Student b) {
if (flag == 0) {
return a.score > b.score;
} else
return a.score < b.score;
}
int main() {
int n;
while ( cin >> n ) {
cin >> flag;
Student s[n];
for ( int i = 0 ; i < n ; i++) {
cin >> s[i].name >> s[i].score;
}
stable_sort(s, s + n, cmp);
for ( int i = 0 ; i < n ; i++) {
cout << s[i].name << " " << s[i].score << endl;
}
}
}
// version 2 : 给Student结构体添加id属性实现稳定排序
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
// 定义学生结构体
struct Student {
string name;
int score;
int id; // 标识录入的先后顺序
};
// 升序或降序
int flag;
// 自定义排序函数
bool cmp(Student a, Student b) {
if (flag == 0) {
// 分数相等,id小的在前
if (a.score == b.score) {
return a.id < b.id;
} else {
// 分数不等,按分数降序排列
return a.score > b.score;
}
} else {
if (a.score == b.score) {
return a.id < b.id;
} else {
// 分数不等,按分数升序排列
return a.score < b.score;
}
}
}
int main() {
int n;
while ( cin >> n ) {
cin >> flag;
Student s[n];
for ( int i = 0 ; i < n ; i++) {
cin >> s[i].name >> s[i].score;
s[i].id=i; // 给id赋值
}
stable_sort(s, s + n, cmp);
for ( int i = 0 ; i < n ; i++) {
cout << s[i].name << " " << s[i].score << endl;
}
}
}
习题3.1 特殊排序(华科复试上机题)
1、思路
- sort函数对输入的数进行排序,先输出排序后的最后一个元素(即最大值),再依次输出前n个元素。
- 我想复杂了,我想的是先找到最大值,再把最大值从数组中删掉,对剩下的元素用一次sort函数。
2、源代码
#include <iostream>
#include <algorithm>
using namespace std;
#define N 1010
int a[N];
int main() {
int n;
int i;
cin >> n;
for (i = 0 ; i < n ; i++) {
cin >> a[i];
}
// 对数组a升序排列
sort(a,a+n);
// 输出最后一个元素(即最大值)
cout << a[n-1] << endl;
// 判断是否有剩下的数
if( n==1 ) {
cout << "-1" << endl;
} else {
for (i = 0 ; i < n-1 ; i++) {
cout << a[i] << " "; // 依次输入前n-1个有序元素
}
cout << endl;
}
}
习题3.2 整数奇偶排序(北大复试上机题)
整数奇偶排序_牛客题霸_牛客网 (nowcoder.com)
1、思路
- 定义两个奇偶数组,输入时判断奇偶性,存入对应数组
- 对奇数组降序排列,偶数组升序排列,一次AC。
#include <iostream>
#include <algorithm>
using namespace std;
bool cmp(int a,int b) {
return a > b;
}
int main() {
int a[10];
int even[10],odd[10];
int i,j=0,k=0;
for (i = 0 ; i < 10 ; i++) {
cin >> a[i];
if (a[i]%2 == 0) {
even[j++] = a[i]; // a[i]是偶数,存入even
} else {
odd[k++] = a[i]; // a[i]是奇数,存入odd
}
}
sort(odd,odd+k,cmp); // 对奇数降序排列
for(i = 0 ; i < k ; i++) {
cout << odd[i] << " ";
}
sort(even,even+j); // 对偶数升序排列
for(i = 0 ; i < j ; i++) {
cout << even[i] << " ";
}
cout << endl;
}
2、其他方法
① 先把所有的按从小到大排序,然后遍历两次数组。第一遍从后往前,遇到奇数就输出;第二遍从前往后,遇到偶数就输出。(很巧妙,通过从后往前遍历实现奇数的降序排列)
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int a[10];
while(cin>>a[0]>>a[1]>>a[2]>>a[3]>>a[4]>>a[5]>>a[6]>>a[7]>>a[8]>>a[9]) {
// 对数组a升序排序
sort(a,a+10);
// 从后往前遍历排序好的a数组,遇到奇数就输出
for(int i = 9 ; i >= 0 ; --i) {
if (a[i]%2 == 1)
cout << a[i]<< " ";
}
// 从前往后遍历排序好的a数组,遇到偶数就输出
for(int i = 0 ; i < 10 ; ++i) {
if (a[i]%2 == 0)
cout << a[i]<< " ";
}
cout << endl;
}
}
② 在cmp函数中书写比较逻辑:传入两个整型参数a,b
- a、b都是奇数:降序排列
- a、b都是偶数:升序排列
- a、b一个奇数一个偶数:奇数排在偶数前面(最后一个return没看懂,私信了原作者,等一个回复orz)
#include <iostream>
#include <algorithm>
using namespace std;
bool cmp (int a,int b) {
if ( a%2==1 && b%2==1 ) // a、b都是奇数,降序排列
return a > b;
else if ( a%2==0 && b%2==0 ) // a、b都是偶数,升序排列
return a < b ;
else
return a%2 > b%2; // 这里没看懂orz
}
int main() {
int a[10];
for (int i = 0 ; i < 10 ; i++) {
cin >> a[i];
}
sort(a,a+10,cmp);
for (int i = 0 ; i < 10 ; i++) {
cout << a[i] << " ";
}
cout << endl;
}
习题3.3 小白鼠排队(北大复试上机题)
1、思路
- 与例题3.2类似,定义老鼠结构体,存储重量和帽子颜色两个属性。
- 编写自定义比较函数cmp,按重量降序排列。比较简单
2、源代码
#include <iostream>
#include <algorithm>
using namespace std;
struct Mouse { // 定义老鼠结构体
int weight;
string color;
};
bool cmp(Mouse a, Mouse b) { // 定义cmp比较函数,按重量降序排列
return a.weight > b.weight;
}
int main() {
int n;
cin >> n;
Mouse m[n];
for (int i = 0 ; i < n ; i++) {
cin >> m[i].weight >> m[i].color;
}
sort(m, m + n, cmp);
for (int i = 0 ; i < n ; i++ ) {
cout << m[i].color << endl;
}
}