复试上机题目练习


个人准备复试上机时,遇到的题,方便之后巩固

上机练习题型

全排列

运用递归方法,对n个数进行全排列:
(若要求对n个字母进行全排列,p[]类型可改为char,原代码中p[index]=i可改为p[idex] = i+‘a’-1或p[index] = i+‘A’-1)

#include<iostream>
using namespace std;

const int maxn = 11;
//p为当前排列,hashTable记录整数x是否已经在p中
int nn,p[maxn],hashTable[maxn] = {false};
//当前处理排列的第index位
void generateP(int index){
	int i= 1;
	if(index == nn+1){//递归边界,已经处理完排列的1~n位
		for(i = 1;i<=nn;i++){
			cout<<p[i];  //输出全排列
		}
		cout<<endl;
		return ;
	}else{
		for(i = 1;i<=nn;i++){
			if(hashTable[i]==false){  //处理不在p中的
				p[index] = i;        //i加入当前序列
				hashTable[i] = true;  //修改标记
				generateP(index+1);    //处理第index+1位
				hashTable[i]=false;   //递归的下边处理完了,返回到这里,还原状态
			}
		}
	}
}
int main(){
	nn = 3;
	generateP(1); //从1开始
	return 0;
}

运行结果

哈夫曼树最小带权路径

输入:
4
1,1,1,1
输出:最小带权路径
大体思路:建立小顶堆,将输入的顶点入堆,再每次取出堆中两个最小的元素即堆顶,作为左右孩子节点,将他们的和作为双亲,再放回堆中,如此反复。直到堆中只剩一个节点。

#include<iostream>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> > que;//优先队列.小顶堆

void main20192(){
	char a;
	int n,i = 0,num;
	while(cin>>n){
		while(que.empty()==false)que.pop();//清空堆中元素
		//输入   以,隔开
		for(i=1;i<=n;i++)
		{
			cin>>num;
			que.push(num);
			if(i!=n)cin>>a;//吸收最后一个回车
		} 
		int sum = 0;
		while(que.size()>1){
			//取出堆中两个最小的元素,作为同一节点的左右孩子,且双亲的权值是他们的和
			int a = que.top();
			que.pop();
			int b = que.top();
			que.pop();

			sum += a+b;//该父亲节点必为非叶子结点,累加
			que.push(a+b);//双亲的权值放回堆中
		}
		cout<<sum<<endl;
	}
}
int main(){
	main20192();
	return 0;
}

已知先序序列,中序序列求后序序列

先序的第一个字符为根节点,再去中序中找根节点的位置,并以此切分,得到左右子树的序列。左右子树再同理。此方法并没有建立出树,只是对字符串的划分

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

typedef struct BTNode{
	char val;
	struct BTNode *left;
	struct BTNode *right;
}BTnode;

int index = 0;
//核心代码
void getTree(string front,string mid){
	if(mid.length() == 0)
		return ;

	char root = front[index ++];//index每次递归加一,用以获取先序中的下一个根字符

	int i = mid.find(root);  //找到根在中序中的位置

	//切分中序序列,得到左右两部分
	string left = mid.substr(0,i);
	string right = mid.substr(i+1,mid.length()-1);

	//后序遍历:左右根
	getTree(front,left);  //继续递归左边
	getTree(front,right);  //递归右边
	cout<<root;
}
void wangdao3334(){
	string front;
	string mid;
	cout<<"先序遍历:";
	cin>>front;
	cout<<"中序遍历:";
	cin>>mid;
	getTree(front,mid);
}

int main(){
	wangdao3334();
	return 0;
}

以下代码是利用先序和中序序列,进行建树。再得到后序序列

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

struct node_0402_1{
	char val;
	struct node_0402_1* lchild;
	struct node_0402_1* rchild;
};

string xianxu,zhongxu;
//根据先序和后序序列,将树建立起来
node_0402_1* fun1_0402_1(int xs,int xe,int zs,int ze){
	if(xs>xe)
		return NULL;
	node_0402_1* root = new node_0402_1;
	root->val = xianxu[xs];

	int k;
	for(k = zs;k<=ze;k++){      //找到根在中序中的位置
		if(zhongxu[k] == xianxu[xs])
			break;
	}
	int left = k-zs;  //左子树的个数

	//左子树的先序区间为[xs+1,xs+left],中序为[zs,k-1]
	root->lchild = fun1_0402_1(xs+1,xs+left,zs,k-1);
	//右子树的先序区间为[xs+left+1,xe],中序为[k+1,ze]
	root->rchild = fun1_0402_1(xs+left+1,xe,k+1,ze);
	return root;
}

//后序遍历
void post_0402_1(node_0402_1* root){
	if(root == NULL)
		return ;
	
	post_0402_1(root->lchild);
	post_0402_1(root->rchild);
	cout<<root->val;
}


void cal_0402_1(){
	cin>>xianxu;
	cin>>zhongxu;
	//建树
	node_0402_1* root = fun1_0402_1(0,xianxu.length()-1,0,zhongxu.length()-1);
	//后序遍历
	post_0402_1(root);
	cout<<endl;
}

int main(){
	cal_0402_1();
	return 0;
}

求一个数的m进制

#include<iostream>
using namespace std;

/** 输入num1,num2,求和的m进制,m<=16 */
void wangdao4342(){
	char num[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

	int m,num1,num2;
	cin>>m>>num1>>num2;

	int sum = num1+num2;
	char a[10],index = 0;
	while(sum!=0){
		a[index ++] = num[sum%m];//余数。主要是为了处理余数大于10的情况
		sum /= m;  //继续求下一位
	}

	for(m = index-1;m>=0;m--){
		cout<<a[m];  //倒着输出
	}
	cout<<endl;
}
int main(){
	wangdao4342();
	return 0;
}

最小生成树

题目:平面上有若干个点,需要用一些线段来将这些点连接起来使任意两个点能够通过一系列的线段相连。给出所有点的坐标,求一种连接方式使所有线段的长度和最小,求该长度和。
输入:3
1.0 1.0
2.0 2.0
2.0 4.0
输出:3.41
大体思路:将平面上的点抽象为节点。节点间的直接相连的线段抽象为边,其长度为权值。即转成最小生成树的问题。

#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
//下标为节点编号,对应值为节点的双亲的编号。
int Tree_44_1[100];
//找某一节点的根节点
int findRoot_44_1(int x){
	if(Tree_44_1[x]==-1)return x;
	else{
		int tmp = findRoot_44_1(Tree_44_1[x]);
		Tree_44_1[x] = tmp;
		return tmp;
	}
}
//边的结构体
struct Edge_441{
	int a,b;//边的两个顶点编号
	double cost; //边的权值
	//重载小于号,使其按照边的权值进行升序排序
	bool operator < (const Edge_441 &A) const{
		return cost<A.cost;
	}
}edge441[100];
//点的结构体
struct Point{     
	double x,y;   //坐标
	//求两点间距离
	double getDistance(Point A){
		double tmp = (x-A.x)*(x-A.x)+(y-A.y)*(y-A.y);
		return sqrt(tmp);
	}
}list[100];

void cal_441(){
	int n,i = 1, j = 0;
	cin>>n;
	for(i = 1;i<=n;i++){   //输入点坐标
		cin>>list[i].x>>list[i].y;
	}
	int size = 0;  //抽象出的边的总数
	//建立起每两点之间的边
	for(i = 1;i<=n;i++){
		for(j = i+1;j<=n;j++){
			edge441[size].a = i;
			edge441[size].b = j;
			edge441[size].cost = list[i].getDistance(list[j]);   //权值为两点间距
			size ++ ;
		}
	}
	//按边的权值排序
	sort(edge441,edge441+size);
	//初始化。-1代表没有双亲节点
	for(i = 1;i<=n;i++)
		Tree_44_1[i] = -1;
		
	double ans = 0;
	for(i = 0;i<size;i++){
		int a = findRoot_44_1(edge441[i].a);
		int b = findRoot_44_1(edge441[i].b);
		if(a!=b){       //边的两个端点不在同一个集合内,合并。
			Tree_44_1[a] = b;
			ans += edge441[i].cost;    //累加长度和
		}
	}
	cout<<ans<<endl;
}
int main(){
	cal_441();
	return 0;
}

最短路径

在这里插入图片描述
使用弗洛伊德算法求解最短路径。

#include<iostream>
using namespace std;

int ans_442[100][100];  //二维数组。其初始值即为该图的邻接矩阵
void cal_442(){
	int N,M;
	cin>>N>>M;
	int i = 1,j = 1,k = 1;
	for(;i<=N;i++){
		for(;j<=N;j++){
			ans_442[i][j] = -1;   //对邻接矩阵初始化。用-1代表无穷
		}
		ans_442[i][i] = 0;
	}
	while(M--){
		int a,b,c;
		cin>>a>>b>>c;
		ans_442[a][b] = ans_442[b][a] = c; //对邻接矩阵赋值,无向图的邻接矩阵是对称的
	}
	for(;k<=N;k++){     //k从1~N,依次代表允许经过的中间节点。
		for(i = 1;i<=N;i++){
			for(j = 1;j<=N;j++){    //遍历所有ans_442[i][j],判断其值是保留还是更新
				//若两值中有一个值为无穷,则经k点不能更新
				if(ans_442[i][k] == -1 || ans_442[k][j]==-1)
					continue;
				//经k点,可以得到更短的路径,更新
				if(ans_442[i][j] == -1 || ans_442[i][k] + ans_442[k][j] < ans_442[i][j])
					ans_442[i][j] = ans_442[i][k] + ans_442[k][j];
			}
		}
	}
	//1,到n最近的距离
	cout<<ans_442[1][N]<<endl;
}
int main(){
	cal_442();
	return 0;
}

拓扑排序

判断图是否是有向无环图。使用拓扑排序。
在输入时,就初始化好入度数组和图的邻接链表。将图中入度为0的节点入队,在while循环中,对队列中每一个入度为0的节点,去除以其为弧尾的边,并修改相应节点的入度。当队列为空时,若所有节点都入度都为0,则图是,否则不是。

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

/** 判断该图是否为有向无环图 ——拓扑排序*/

vector<int > edge443[100]; //邻接链表。由于边不存在权值,只需保存与其邻接的节点编号即可。
queue<int > q443;  //保存入度为0的节点的队列

void cal_443(){
	//统计每个节点的入度
	int inDegree[100];
	int n,m;
	int i = 0;
	cin>>n>>m;

	//初始化节点。节点编号由0~n-1。
	for(;i<n;i++){
		inDegree[i] = 0;    //初始化入度信息,所有结点入度均为0
		edge443[i].clear();   //清空邻接链表
	}

	while(m--){
		int a,b;
		cin>>a>>b;  //a指向b
		inDegree[b] ++;   //b的入度增加
		edge443[a].push_back(b);  //b加入a的邻接链表
	}

	//清空队列
	while(!q443.empty())q443.pop();

	for(i = 0;i<n;i++){   //度为0的节点,入队
		if(inDegree[i]==0)q443.push(i);   
	}
	//计数器。累加已经确定拓扑序列的结点个数
	int cnt = 0;  
	while(q443.empty() == false){
		int nowP = q443.front();
		q443.pop();
		cnt++;  //被确定的节点个数+1
		//将该节点 以及 以其为弧尾的所有边都去除
		for(i = 0;i<edge443[nowP].size();i++){
			inDegree[edge443[nowP][i]]--;   //去除某条边后,该边所指后继节点的入度-1
			if(inDegree[edge443[nowP][i]] == 0){
				q443.push(edge443[nowP][i]);   //当节点的入度变为0,则入队
			}
		}
	}
	if(cnt==n)cout<<"YES"<<endl;
	else cout<<"No"<<endl;
}
int main(){
	cal_443();
	return 0;
}

最长不增子序列

在这里插入图片描述
简而言之,即输入一串数字,寻找其中的最长不增子序列的长度。递推关系如下:
在这里插入图片描述
输入:8
300 207 155 300 299 170 158 65
输出:6

#include<iostream>
using namespace std;

/** 这个最大不增子序列,不一定是连续的 */

//取最大值
int max_461(int a,int b){
	return a>b?a:b;
}

int list_461[26];   //按袭击事件顺序保存个导弹高度
int dp_461[26];   //保存以第i个导弹结尾的最长不增子序列高度

void cal_461(){
	int n,i = 0,j = 0;
	cin>>n;
	//输入各高度
	for(i = 0;i<n;i++){
		cin>>list_461[i];
	}

	//按照袭击时间顺序确定每一个dp[i]
	for(i =0;i<n;i++){
		int tmax = 1;    //最大值的初始值为1,即以其结尾的最长不增子序列长度至少为1

		for(j = 0;j<i;j++){
			//若j号导弹不比当前导弹低。
			if(list_461[j]>=list_461[i])
				//将当前导弹排列在以j号导弹结尾的最长不增子序列之后,计算其长度dp[j]+1,若大于当前最大值,则更新最大值
				tmax = max_461(tmax,dp_461[j]+1);
		}
		dp_461[i] = tmax;
	}

	//找到最大不增子序列的长度
	int ans = 1;
	for(i = 0;i<n;i++){
		ans = max_461(ans,dp_461[i]);
	}
	cout<<ans<<endl;
}
int main(){
	cal_461();
	return 0;
}

例2:给定一个无序的整数数组,找到其中最长上升子序列的长度。
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
(以下给出关键代码)

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

class Solution {
public:
	//nums为输入的无序数组,返回值为最长上升子序列的长度
    int lengthOfLIS(vector<int>& nums) {
		int n = nums.size();
		if(n == 0 || n==1)return n;

		//建立一个dp数组,长度为n,初始值为1
		vector<int > dp(n,1);

		int i = 1,j = 1;
		for(i = 0;i<n;i++){
			int tmax = 1;

			for(j = 0;j<i;j++){
				if(nums[j]<nums[i]){
					tmax = max(tmax,dp[j]+1);
				}
			}
			dp[i] = tmax;
		}
		int ans = 1;
		for(i = 0;i<n;i++){
			ans = max(ans,dp[i]);
		}
		return ans;
    }
private:
	int max(int a,int b){
		return a>b?a:b;
	}
};

最长公共子序列

寻找两个字符串中的最长公共子序列,但不一定连续。
递推关系如下:
在这里插入图片描述
输入:abcd
cxbydz
输出:2

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

int dp_462[100][100];
//取最大值
int max_462(int a,int b){
	return a>b?a:b;
}
void cal_462(){
	string s1,s2;
	cin>>s1>>s2;
	int l1 = s1.length(),l2 = s2.length();
	int i = 0,j = 0;
	for(;i<=l1;i++)
		dp_462[i][0] = 0;  //第一列,第一个字符串
	for(;j<=l2;j++)
		dp_462[0][j] = 0;  //第一行,第二个字符串
	//补全dp_462[][]中的值
	for(i = 1;i<=l1;i++){
		for(j = 1;j<=l2;j++){
			//当两个字符不相等,dp_462[i][j]为较大的那一个
			if(s1[i-1]!=s2[j-1]){
				dp_462[i][j] = max_462(dp_462[i][j-1],dp_462[i-1][j]);
			}else{     //若相等,dp_462[i][j]比dp_462[i-1][j-1]加一
				dp_462[i][j] = dp_462[i-1][j-1]+1;
			}
		}
	}
	cout<<dp_462[l1][l2]<<endl;
}
int main(){
	cal_462();
	return 0;
}

合并两个有序链表

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

此处给出关键代码。

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		if(l1 == NULL && l2!=NULL)return l2;
		if(l1 != NULL && l2==NULL)return l1;
		if(l1 == NULL && l2==NULL)return NULL;
		//递归
		if(l1->val <= l2->val){
			l1->next = mergeTwoLists(l1->next,l2);
			return l1;
		}else{
			l2->next = mergeTwoLists(l1,l2->next);
			return l2;
		}
    }
};

待续

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值