Data structure third experiment problem solving Report

Posted by iMiles on Wed, 09 Feb 2022 17:28:11 +0100

1, Longest path of binary tree

Given a binary tree T, find the length of the longest path in T, and output the value of each node on this path. If there are multiple longest paths, output the rightmost one.

Input format:
The first line, an integer n, indicates that the binary tree has n nodes, 1 ≤ n ≤ 100000

Line 2, 2n+1 integers, separated by spaces, represents the extended first root sequence of T, - 1 represents the null pointer, and nodes are represented by numbers 1 to n.

Output format:
Line 1, an integer length, which represents the length of the longest path in T.

Line 2, length+1 integers, separated by spaces, represents the longest path on the far right.

Input sample:
A set of inputs is given here. For example:

5
1 2 -1 -1 3 4 -1 -1 5 -1 -1
Output example:
The corresponding output is given here. For example:

2
1 3 5

Solution: the binary tree is built recursively. We can use recursion to find the depth of each node, find the longest path and go to the deepest point of each step.
Next, I give two algorithms for solving the longest path:

#include<iostream>
#include<queue>
using namespace std;
typedef struct tree{
	int data;
	int depth;
	struct tree* left;
	struct tree* right;
}Tree,*BTree;
int a[100005];
queue<int> q;
int length(BTree root){
	BTree p;
	p=root;
	if(!p) return 0;
	else{
		int dl=length(p->left);
		int dr=length(p->right);
		if(dl>dr) {
			p->depth=dl+1;
		    return dl+1;
		}
		else{
		   p->depth=dr+1;
		   return dr+1;
		}
	}
}
int h(BTree t){
	if(t==NULL) return -1;
	return max(h(t->left),h(t->right))+1;
}
void longest_path(BTree root){
	if(root==NULL) return;
	else{
		printf("%d",root->data);
		if( length(root->left) > length(root->right) ){
			if(root->left!=NULL) printf(" ");
		    longest_path(root->left);
		}
		else{
			if(root->right!=NULL) printf(" ");
		   longest_path(root->right);
		   }
	}
}
void longest_path2(BTree t){
	int i=0;
	while(t!=NULL){
		//a[i++]=t->data;
		//printf("%d",t->data);
		q.push(t->data);
		if(t->right==NULL|| length(t->left) > length(t->right) ){
			//if(t->left) printf(" ");
			t=t->left;
		}
		else{
			//if(t->right) printf(" ");
			t=t->right;
		}
	}
}

void create_tree(Tree* &t){  //Create a binary tree 
	int ch;
	cin>>ch;
	if(ch==-1){
		t=NULL;
	}
	else{
		t=new Tree;
		t->data=ch;
		create_tree(t->left);
		create_tree(t->right);
	}
} 
void preorder(BTree t){
	if(t==NULL) return;
	else{
		cout<<t->data<<" "<<t->depth<<endl;
		preorder(t->left);
		preorder(t->right);
	}
}
void longest_path3(BTree t){
	int i=0;
	while(t!=NULL){
		q.push(t->data);
		if(t->left==NULL||(t->left!=NULL&&t->right!=NULL&&h(t->right)>=h(t->left))){
		//	if(t->left) printf(" ");
			t=t->right;
		}
		else{
		//	if(t->right) printf(" ");
			t=t->left;
		}
		
	}
}
int main(){
	Tree* T,t;
	int L,n,i,j;
	scanf("%d",&n);
	create_tree(T);
	//preorder(T);
	L=length(T);
	printf("%d\n",L-1);
	longest_path2(T);
//	for(i=0;i<L;i++){
//		printf("%d",a[i]);
//		if(i!=L-1) printf(" ");
//	}
    while(!q.empty()){
    	printf("%d",q.front());
    	q.pop();
    	if(!q.empty())
    	printf(" ");
	}
    printf("\n");
    return 0;
}

Algorithm 1:

void longest_path(BTree root){
	if(root==NULL) return;
	else{
		printf("%d",root->data);
		if( length(root->left) > length(root->right) ){
			if(root->left!=NULL) printf(" ");
		    longest_path(root->left);
		}
		else{
			if(root->right!=NULL) printf(" ");
		   longest_path(root->right);
		   }
	}
}

This recursive algorithm repeatedly calculates the depth of each node every time, which is inefficient and has few test points.

Algorithm 2:

void longest_path2(BTree t){
	int i=0;
	while(t!=NULL){
		//a[i++]=t->data;
		//printf("%d",t->data);
		q.push(t->data);
		if(t->right==NULL|| length(t->left) > length(t->right) ){
			//if(t->left) printf(" ");
			t=t->left;
		}
		else{
			//if(t->right) printf(" ");
			t=t->right;
		}
	}
}

This is an improved algorithm on Algorithm 1. Instead of calling its own recursion, it uses the form of iterating only for node depth, which is more efficient than algorithm 1.
Note: we can save the data in an array or a queue, or directly output it when calling the function.

2, Hierarchical traversal of forest

Given a forest F, find the hierarchical traversal sequence of F. The forest is given by its first root sequence and the degree of each node in the sequence.

Input format:
The first line, an integer n, represents the number of nodes in the forest, 1 ≤ n ≤ 100000

Line 2, n characters, separated by spaces, represents the first root sequence of forest F. Characters are upper and lower case letters and numbers.

The third line, n integers, separated by spaces, represents the corresponding degree of each node in the first root sequence of forest F.

Output format:
1 line, n characters, separated by spaces, representing the hierarchical traversal sequence of forest F.

Input sample:
A set of inputs is given here. For example:

14
A B C D E F G H I J K L M N
4 0 3 0 0 0 0 2 2 0 0 0 1 0
Output example:
The corresponding output is given here. For example:

A M B C G H N D E F I L J K

After seeing this topic, my first practice is to build a binary tree according to the degree of nodes, but it is a little laborious (I haven't done it right). After referring to the practice of my classmates, I adopt the idea of adjacency linked list storage similar to graph. A vector array is opened. The array subscript is the first sequence subscript of the node, and the vector space of the array stores the child nodes of the node. When all the nodes in the sequence are accessed, a virtual tree is established. We use the queue to save the corresponding nodes (in order to realize hierarchical traversal), and then output while traversing.

#include<iostream>
#include<stack> 
#include<queue>
#include<vector>
#define maxsize 100002
using namespace std;
int degree[maxsize];
char data[maxsize];
vector<int> a[maxsize];
queue<int> q;
int pointer=1; //Pointer to the element of the operation
void build_tree(int x){//Virtual tree building (similar to graph storage) 
	int i;
	for(i=0;i<degree[x];i++){
		a[x].push_back(++pointer);
		build_tree(pointer);
	}
}
int main(){
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		cin>>data[i];
	}
	for(i=1;i<=n;i++){
		cin>>degree[i];
	}
	while(pointer<=n){
	    a[0].push_back(pointer);//a[0] preserves the roots of each number in the forest, but does not connect these trees because they are relatively independent
		build_tree(pointer);
		pointer++; //Similarly to pointer + +, the next step is to process the next node of the processed node
	}
	q.push(0);  //First subscript the array of the root of the master tree into the queue, and then all nodes can be accessed
    while(!q.empty()){
    	int j=q.front();
    	q.pop();
    	for(vector<int>::iterator i=a[j].begin();i!=a[j].end();i++){
    		q.push(*i);
		}
        if(j) { //Character data is not saved in a[0], so it is skipped during output
            cout<<data[j];
            printf("%c",!q.empty()?' ':'\n');
        }
	}
}  	

3, Paper tape cutting

There is a slender paper tape with a length of L units and a width of 1 unit. Now cut the tape into n segments. Each cutting divides the current paper tape into two sections, and the cutting position is in integer units. The cutting cost is the total length of the current cutting paper tape. The longest paper tape that fails to meet the final requirements shall be selected for each cutting. If there are multiple such paper tapes, any one shall be selected for cutting. How to cut, in order to complete the task, and the total cost is the least.

Input format:
The first line, an integer n, represents the number of segments cut, 1 ≤ n ≤ 100000

The second line, n integers Li, separated by spaces, indicates the length of each segment to be cut, 1 ≤ Li ≤ 200000000, 1 ≤ i ≤ n

Output format:
The first line, an integer, represents the minimum total cost.

In line 2, several integers separated by spaces represent the cost of each cutting when the total cost is the smallest.

Input sample:
A set of inputs is given here. For example:

5
5 6 7 2 4
Output example:
The corresponding output is given here. For example:

54
24 13 11 6

Solution idea: this algorithm has been mentioned in Huffman coding. It can be realized by using limited queue, or by using heap when selecting nodes every time, and it can also continuously use sorting to pick out two smaller data.
Or three algorithms are given (not all of them pass all test points)
Algorithm 1:

Solution I 
int L[100005],out[100005];
bool cmp(int a,int b){
	return a<b;
}
void InsertSort(int a[],int begin,int end){//Direct insert sort 
	int i,j,tmp;
	for(i=begin;i<end;i++){
		j=i-1;
		tmp=a[i];//Subsequent operations a[i] are contaminated and need to be saved in a tmp 
		while(j>=0&&tmp<a[j]){
			a[j+1]=a[j];
			j--;
		}
		a[j+1]=tmp;
	}
} 
int main(){
	int n,i,j=0,cnt=0;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&L[i]);
	}
	sort(L,L+n,cmp);
	for(i=0;i<n-1;i++){
		out[j]=L[i]+L[i+1];
		L[i+1]=out[j];
		j++;
		//sort(L+i+1,L+n,cmp);
		InsertSort(L,i+1,n);
	}
	for(i=0;i<j;i++){
		cnt+=out[i];
	}
	printf("%d\n",cnt);
	for(i=j-1;i>=0;i--){
		printf("%d",out[i]);
		if(i) printf(" ");
	}
}

The idea of this algorithm is to sort the data after reading it, so that at most one data column does not comply with the increment. Take the smallest two data and add them to cover the original two data, and then insert and sort the new data column until the paper tape is cut into N copies. The algorithm time is mainly consumed in sorting. Insert sorting is carried out in a large loop, plus additional fast sorting and two traversals. The time complexity is O(n^2).
Algorithm 2:

//Solution II 
typedef struct paper{
	int length;
	struct paper* left;
	struct paper* right;
}Paper;
Paper *L[100005];
bool cmp(Paper *a,Paper *b){
	return a->length<b->length;
}
int out[100005];
queue<Paper*> q;
int main() {
	int n,i,j=0,cnt=0;
	Paper* p;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		L[i]=new Paper;
		//scanf("%d",&L->length);
		cin>>L[i]->length;
		L[i]->left=L[i]->right=NULL;
	}
	sort(L,L+n,cmp);
	for(i=0;i<n-1;i++){  //Constructing Huffman tree 
		Paper* t=new Paper;
		t->left=L[i];
		t->right=L[i+1];
		t->length=L[i]->length+L[i+1]->length;
		for(j=i+2;j<n;j++){
			if(t->length>L[j]->length) L[j-1]=L[j];
			else break;
		}
		L[j-1]=t;
	}
	q.push(L[n-1]);
	j=0;
	while(!q.empty()){
		p=q.front();
		q.pop();
		if(p->left!=NULL&&p->right!=NULL){
			q.push(p->left);
			q.push(p->right);
			cnt+=p->length;
			out[j++]=p->length;
		}
	}
	sort(out,out+j);
//	for(i=0;i<j;i++){
//		cnt+=out[i];
//	}
	printf("%d\n",cnt);
	for(i=j-1;i>=0;i--){
	    printf("%d",out[i]);
	    if(i) printf(" ");
	}
	if(n!=1) printf("\n");
}

Algorithm 2 is completely adapted from Huffman's algorithm. The core algorithm is consistent with the description in the data structure book. I won't repeat it here. Time complexity O(n^2)

Algorithm 3:

//Solution 3: 
priority_queue< int, vector<int>, greater<int> > pq;
int out[100005];
int main(){
	int n,i,L,cnt=0;
	int l1,l2;
	long long sum=0;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&L);
		pq.push(L);
	}
	while(pq.size()>1){
		l1=pq.top();
		pq.pop();
		l2=pq.top();
		pq.pop();
		out[cnt++]=l1+l2;
		sum+=l1+l2;
		pq.push(l1+l2);
	}
	printf("%lld\n",sum);
	
	for(i=cnt-1;i>=0;i--){
		printf("%d",out[i]);
		if(i) printf(" ");
	}
	if(cnt) printf("\n");
}

Algorithm 3 draws lessons from xuanchen's priority queue algorithm. The priority queue is based on the heap time, and the sorting speed is better than that of the system, especially if the data column has been processed in advance to increase it.

##4, Sequence product

Two increasing sequences, A and B, are n in length. Let Ai and Bj be the product, 1 ≤ i,j ≤ n. please output the first n of the n*n products from small to large.

Input format:
The first line, an integer n, represents the length of the sequence, 1 ≤ n ≤ 100000

In line 2, n integers Ai, separated by spaces, represent sequence A, 1 ≤ Ai ≤ 40000, 1 ≤ i ≤ n

In line 3, n integers Bi, separated by spaces, represent sequence B, 1 ≤ Bi ≤ 40000, 1 ≤ i ≤ n

Output format:
1 line, n integers, separated by spaces, indicating the first N in the sequence product from small to large.

Input sample:
A set of inputs is given here. For example:

5
1 3 5 7 9
2 4 6 8 10
Output example:
The corresponding output is given here. For example:

2 4 6 6 8


Problem solving idea: it is a pure violence algorithm at the beginning. If the card has n digital intervals, using the priority queue or directly building the heap will timeout. If the data is calculated only once and sorted only once, a large amount of data needs to be saved, which will exceed the memory limit.

#include<iostream>
#include<deque>
#include<vector> 
#include<algorithm>
#define MAX 100005
using namespace std;
int a[100002],b[100002],c[100002];
vector<int> C;
int main(){
	int i,n,cnt=0,j,mul,cmp=100000000;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	for(i=0;i<n;i++){
		scanf("%d",&b[i]);
	}
	for(i=0;i<n;i++){
		for(j=0;j<n;j++){
			mul=a[i]*b[j];
			if(mul>cmp) break;
			else
			C.push_back(mul);
		}
		cmp=a[i]*b[j-1];
	}
    int L=C.size();
    sort(C.begin(),C.end());
    for(i=0;i<n;i++){
    	printf("%d",C[i]);
    	if(i!=n-1) printf(" ");
	}
}

Although this method limits the boundary of each data entry, 5MB will still get stuck.
Later, we directly gave up using vector to store data and used a pointer to maintain the data range.

#include<iostream>
#include<deque>
#include<vector> 
#include<algorithm>
#define MAX 100005
using namespace std;
int a[100002],b[100002],c[500002];
int main(){
	int i,n,cnt=0,j,mul,mmax=100000000;
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	for(i=0;i<n;i++){
		scanf("%d",&b[i]);
	}
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            mul=a[i]*b[j];
            if(mul>mmax){
               break;
            }
            c[cnt++]=mul;
        }
        if(j==0) break;
        else mmax=c[n-1];
        if(cnt>=2*n){
            stable_sort(c,c+cnt);
            cnt=n;
        }
    }
    stable_sort(c,c+cnt);
    for(i=0;i<n;i++){
       printf("%d",c[i]);
        if(i!=n-1) printf(" ");
    }
    printf("\n");
}

Here we discuss when a[i]*b[j] will enter the array. It is no longer mindless saving: when a[i] is multiplied by a column of b, there are n data in total. When i=0, all N data are saved. At this time, c[n-1] saves the maximum value of this data mmax. When the next data stream comes in, the product is compared with mmax. If it is greater than mmax, the calculation of the subsequent product will stop, Exit the internal circulation directly. Because the series a[i] and b [J] are incremental series, there is no need to calculate later to find the first a[i]*b[j] larger than mmax. If a[i]*b[0] is larger than mmax, it means that all future data do not meet the requirements, and the calculation operation is discarded.
At the same time, the array c [] is also maintained. When there are too many data, reorder and intercept the data covered by cnt=n again. At the same time, mmax will also be updated, so as to avoid the risk that the last data of a[0]*b[j] is too large and the subsequent data product will be saved continuously.