文章目录
例6.7
读入一串整型数据,直到输入一个特定值为止,把这些整形数据逆序排列,输出经过重新排列后的数据,要求每个功能都用一个函数实现
思路:根据题意,分解成三个函数,分别是读入一串整数(数组,数组规模,输入结束标记),对这组整数逆序排列(数组,数组规模),输出数组(数组,数组规模),由此确定函数原型
#include <iostream>
using namespace std;
#define MAX 10
//函数原型声明
int ReadIntegerArray(int array[], int max, int flag);
void ReverseIntegerArray(int array[], int size);
void PrintIntegerArray(int array[], int size);
int main(){
int IntegerArray[MAX], flag, CurrentSize;
cout << "请输入一个结束标记(整数):";
cin >> flag;
CurrentSize = ReadIntegerArray(IntegerArray, MAX, flag);
ReverseIntegerArray(IntegerArray, CurrentSize);
PrintIntegerArray(IntegerArray, CurrentSize);
return 0;
}
/**
接收用户的输入
**/
int ReadIntegerArray(int array[], int max, int flag){
int size = 0;
cout << "请输入数组元素,以" << flag << "结束:";
while(size < max){
cin >> array[size];
if(array[size] == flag)
break;
else
++size;
}
return size;
}
/**
对数组内容逆序排列
**/
void ReverseIntegerArray(int array[], int size){
int i, tmp;
for(i=0; i<size/2; ++i){
tmp = array[i];
array[i] = array[size -i -1];
array[size -i -1] = tmp;
}
}
/**
输出数组
**/
void PrintIntegerArray(int array[], int size){
int i;
if(size == 0) return;
cout << "逆序是:" << endl;
for(i=0; i<size; ++i){
cout << array[i] << " ";
}
}
例5.12 函数版本
要求统计一组正整数的和,这组正整数可以是八进制、十进制、十六进制表示,当时方法是采用字符串保存这组数字,然后从中区分出一个个正整数,代码集中在了mian函数,现在对其进行优化,将相关代码封装成函数,如:把字符串表示的不同基数的正整数转换成一个整型数;把输入的字符串字母转换成大写
#include <iostream>
using namespace std;
int convertToInt(char s[], int start);
void convertToUpper(char s[]);
int main(){
char str[81];
int sum = 0, i = 0;
cout << "请输入一组整数,用空格分开:";
cin.getline(str, 81);
convertToUpper(str);
while(str[i] == ' ' && str[i] !='\0') //跳过前置空格
++i;
while(str[i] !='\0'){
sum += convertToInt(str, i);
while(str[i] != ' ' && str[i] != '\0') //跳过刚才已处理的i
++i;
while(str[i] == ' ' && str[i] != '\0') //跳过空格
++i;
}
cout << "整数和为:" << sum << endl;
return 0;
}
void convertToUpper(char s[]){
for(int i=0;s[i]!='\0';++i){
if(s[i]>='a' && s[i]<='z'){
s[i] = s[i] - 'a' + 'A';
}
}
}
int convertToInt(char s[], int start){
int data = 0, base;
if(s[start]!='0')
base = 10;
else if(s[start+1] == 'X'){
base = 16;
start += 2;
}
else{
base = 8;
++start;
}
switch(base){
case 10:
while(s[start]!=' ' && s[start]!='\0'){
data = data * 10 + s[start++] - '0';
}
break;
case 8:
while(s[start]!=' ' && s[start]!='\0'){
data = data * 8 + s[start++] - '0';
}
break;
case 16:
while(s[start]!=' ' && s[start]!='\0'){
data = data * 16;
if(s[start]>='A' && s[start]<='F')
data += s[start++] - 'A' + 10;
else
data += s[start++] - '0';
}
}
return data;
}
利用函数模板计算两个数最大值
#include <iostream>
using namespace std;
template <class T>
T Max(T a, T b){
return a>b ? a : b;
}
int main(){
cout << "Max(3,5) = " << Max(3,5) <<endl;
cout << "Max(3.3,2.5) = " << Max(3.3,2.5) <<endl;
cout << "Max('d','r') = " << Max('d','r') <<endl;
return 0;
}
汉诺塔问题
将n个盘子从start借助temp移动到finish
#include <iostream>
using namespace std;
void Hanoi(int n, char start, char finish, char temp);
int main(){
Hanoi(3,'A','B','C');
return 0;
}
void Hanoi(int n, char start, char finish, char temp){
if(n==1)
cout << start << "->" << finish << ' ';
else{
Hanoi(n-1, start, temp, finish);//从第一根柱子移动到第三根
cout << start << "->" << finish << ' ';
Hanoi(n-1, temp, finish, start);//再从第三根柱子移动到第二根
}
}
例6.11 用递归函数打印一个十进制整数的函数定义及使用
先递归到最后打印第一位数字,然后回调,利用%不断打印最后一位
#include <iostream>
using namespace std;
void printInt(int num);
int main(){
int num;
cout << "请输入一个整数:" << endl;
cin >> num;
printInt(num);
cout << endl;
return 0;
}
void printInt(int num){
if(num < 10){ //递归终止条件
cout << static_cast<char>(num + '0');
}else{
printInt(num/10);
cout << static_cast<char>(num%10 + '0');
}
}
在此基础上进一步扩展,根据输入,打印不同进制的整数
#include <iostream>
using namespace std;
void printInt(int num, int base);
static char DIGIT[17] = "0123456789abcdef";
int main(){
int num,base;
cout << "请输入一个整数:" << endl;
cin >> num;
cout << "请输入要打印的进制:" << endl;
cin >> base;
printInt(num, base);
cout << endl;
return 0;
}
void printInt(int num, int base){
if(num < base)
cout << DIGIT[num];
else{
printInt(num/base, base);
cout << DIGIT[num%base];
}
}
例6.12 设计函数打印n个字符的全排列
#include <iostream>
#include <string.h>
using namespace std;
void PermuteWithFixedPrefix(char str[], int k);
void swap(char str[], int k, int i);
int main(){
int n;
cout << "请输入n:";
cin >> n;
cin.get();
char str[n+1];
cout << "请输入" << n << "个字符:";
cin.getline(str,n+1);
cout << "全排列结果为:\n";
PermuteWithFixedPrefix(str, 0);
return 0;
}
void PermuteWithFixedPrefix(char str[], int k){
int i;
if(k == strlen(str))
cout << str << endl;
else{
for(i=k; i<strlen(str); ++i){
swap(str, k, i);
PermuteWithFixedPrefix(str, k+1);
swap(str, k, i);
}
}
}
void swap(char str[], int k, int i){
int tmp;
tmp = str[k];
str[k] = str[i];
str[i] = tmp;
}
例6.13 8皇后问题
思路:用一个int数组表示第i列放在col[i]行,三个一维布尔数组分别表示第i行能不能放,第i条右高左低的对角线位置能不能放,第i条左高右低的对角线位置能不能放(这个表达出来比较麻烦,需要观察一下),然后递归回溯遍历,从第1列开始放,直到放到最后一列
#include <iostream>
using namespace std;
void queen_all(int k);
int col[9];
bool row[9], digLeft[17], digRight[17];
int main(){
int j;
for(j=0;j<=8;j++)
row[j] = true;
for(j=0;j<=16;j++)
digLeft[j] = digRight[j] = true;
queen_all(1);
return 0;
}
void queen_all(int k){
int i,j;
char awn;//是否继续寻找的标记
for(i=1; i<9; i++){
if(row[i] && digLeft[k+i-1] && digRight[8+k-i]){
col[k] = i; //第k列的皇后放在第i行
row[i] = digLeft[k+i-1] = digRight[8+k-i] = false;
if(k == 8){ //找到一个可行解
for(j=1;j<=8;j++){
cout << "第" << j << "列皇后" << "-" << "第" << col[j] << "行" << '\n';
}
cout << endl << "是否继续寻找(Q--退出,其他键继续:)";
cin >> awn;
if(awn == 'Q' || awn == 'q')
exit(0);
}else{
queen_all(k+1);
}
row[i] = digLeft[k+i-1] = digRight[8+k-i] = true; //回溯
}
}
}
例6.14 分书问题
有编号0、1、2、3、4的5本书,准备分给5个人A、B、C、D、E,每个人的阅读兴趣用一个二维数组描述:
like[i][j]=true //i喜欢书j
like[i][j]=false //i不喜欢书j
编写程序,输出所有皆大欢喜的分书方案
#include <iostream>
using namespace std;
void trynext(int i);
bool like[5][5] = {
{
false,false,true,true,false
},
{
true,true,false,false,true
},
{
false,true,true,false,true
},
{
false,false,false,true,false
},
{
false,true,false,false,true
}
};
int take[5] = {-1,-1,-1,-1,-1};
int n=0;
int main(){
trynext(0);
return 0;
}
void trynext(int i){
int j, k;
for(j=0; j<5; ++j){
if(like[i][j] && take[j]==-1){
take[j] = i;
if(i==4){
n++;
cout << "\n第" << n << "种方案:" << endl;
cout << "书\t人" << endl;
for(k=0;k<5;k++){
cout << k << '\t' << char(take[k]+'A') << endl;
}
}
else{
trynext(i+1);
}
take[j]=-1;//回溯 寻找下一方案
}
}
}
例6.15 快速排序
#include <iostream>
using namespace std;
void quicksort(int a[], int low, int high);
int divide(int a[], int low, int high);
int main(){
int a[] = {5,7,3,0,4,2,1,9,6,8};
cout << "排序前:" << "\n";
for(int x:a){
cout << x << " ";
}
cout << endl;
quicksort(a,0,9);
cout << "排序后:" << "\n";
for(int x:a){
cout << x << " ";
}
}
void quicksort(int a[], int low, int high){
int mid;
if(low >= high)
return;
mid = divide(a,low,high);
quicksort(a,low,mid-1);//排左一半
quicksort(a,mid+1,high);//排右一半
}
//分段函数,将数组分成两段
int divide(int a[], int low, int high){
int k = a[low];
while(low != high){
while(low<high && a[high]>=k)
--high;
if(low<high){
a[low] = a[high];
++low;
}
while(low<high && a[low]<=k)
++low;
if(low<high){
a[high] = a[low];
--high;
}
}
a[low] = k;
return low;
}
例6.16 最大连续子序列和
如果所有整数都是负的,最长子序列和返回0
思路:可以利用分治法实现,分三种情况讨论,最大子序列和在左半部分,最大子序列和在右半部分,最大子序列和横跨左右部分,取其中的最大值
#include <iostream>
#include <limits.h>
using namespace std;
int NEGMAX = INT_MIN;
int maxSum(int a[], int left, int right);
int max3(int a, int b, int c);
int main(){
int a[] = {4, -3, 5, -2, -1, 2, 6, -2};
int res = maxSum(a,0,7);
cout << "最长连续子序列和:" << res << endl;
}
int maxSum(int a[], int left, int right){
int maxLeft,maxRight,center;
int leftSum = 0, rightSum = 0;
int maxLeftTmp = NEGMAX, maxRightTmp = NEGMAX; //NEGMAX最大负整数
if(left == right)
return a[left] > 0 ? a[left] : 0;
center = (left + right) / 2;
maxLeft = maxSum(a, left, center);//计算左半部分最大子序列和
maxRight = maxSum(a, center+1, right);//计算右半部分最大子序列和
//找从前半部分开始到后半部分结束的最大连续子序列和
//从中间开始右到左遍历
for(int i = center; i>=left; --i){
leftSum+=a[i];
if(leftSum > maxLeftTmp)
maxLeftTmp = leftSum;
}
//从中间下一个开始左到右遍历
for(int i = center+1; i<=right; ++i){
rightSum+=a[i];
if(rightSum > maxRightTmp)
maxRightTmp = rightSum;
}
return max3(maxLeft, maxRight, maxLeftTmp+maxRightTmp);
}
int max3(int a,int b,int c){
int max;
max = a>b?a:b;
return (max>c?max:c);
}
例6.17 硬币找零问题
对于一种货币,有面值为C1,C2…Cn(分)的硬币,最少需要多少个硬币来找出K分钱的零钱
动态规划解法:
从1分钱开始不断构建表格,coinsUsed[j]表示找零钱j需要的最小的硬币数,初始化coinsUsed[0]=0,coins[maxChange]是最终答案
#include <iostream>
using namespace std;
void makechange(int coins[], int differentCoins, int maxChange, int coinUsed[]);
int coinsUsed[10000];
int main(){
int coins[] = {1,5,10,21,25};
int maxChange;
cout << "请输入要找的零钱(单位:分):";
cin >> maxChange;
makechange(coins,5,maxChange,coinsUsed);
cout << "找"<< maxChange << "分零钱的最少硬币数:" << coinsUsed[maxChange] << endl;
}
void makechange(int coins[], int differentCoins, int maxChange, int coinUsed[]){
coinUsed[0] = 0;
for(int cents = 1; cents<= maxChange; cents++){
int minCoins = cents;//都用1分找零时硬币数最大
for(int j=1;j<differentCoins;j++){
if(coins[j] > cents) //如果coins[j]大于要找的零钱则直接跳过进入下一次循环
continue;
//减去当前硬币后零钱需要的最少硬币数+当前硬币
if(coinUsed[cents - coins[j]]+1<minCoins){
minCoins = coinUsed[cents - coins[j]] + 1;
}
}
coinUsed[cents] = minCoins;
}
}