2021-05-23

数据结构荣誉课第三次上机实验

一、二叉树最长路径值:

题意:根据先根序列构建二叉树并求出最长最右的路径。

思路:

刚开始做此题时我采用递归调用求出树的高度,后在输出最长路径时每次都求一边左子树高度和右子树高度,再作比较,这样的代码,最后拿到了80分,时间复杂度是o(nlogn),第三个样例超时。于是我在树的节点中加了一个树高,这样在建树过程中就把每个节点的高度求出来,到时候比较时就是o(1)级了,建树时时间复杂度为o(logn)。

代码:

1)、节点:

typedef struct BTN{

 int iterm;
 BTN* left;
 BTN* right;
 int high;
 BTN(int n):iterm(n),high(0){}
}btn,BtNode,*BinaryTree;

2)、判断输出:

 for(int j=0;j<x;j++)
    {
        if(a.hi(p->left)==a.hi(p->right))
                {
                    p=p->right;
                    printf(" %d",p->iterm);
                }
        else if(a.hi(p->left)>a.hi(p->right))
                {
                    p=p->left;
                     printf(" %d",p->iterm);
                }
            else if(a.hi(p->left)<a.hi(p->right))
                {
                    p=p->right;
                     printf(" %d",p->iterm);
                }
    }

3)、完整代码:


#include<iostream>
#include<algorithm>
#include<string.h>
#include<stack>
#include<malloc.h>
#include<queue>
using namespace std;
typedef char ElemType;
int  b[100000],i=0;
typedef struct BTN{

 int iterm;
 BTN* left;
 BTN* right;
 int high;
 BTN(int n):iterm(n),high(0){}
}btn,BtNode,*BinaryTree;
class tlist{
BTN*root;
public:
    tlist():root(0){ };
    inline BTN * CBT(int stop);//建树1已知数的先根序列加空结点位置
    void pretravel(BTN*r);//先根遍历
    inline int max(int a,int b){if(a>b)  return a;else return b;}
    inline int GetHeight( BTN* BT );
    inline int hi(BTN *root);
};
int tlist:: hi(BTN*t)
{
    if ( !t ) return -1;
    return t->high;
}
BTN * tlist ::CBT(int stop)//树的操作类//涉及树的构建,树的遍历,指定节点查找
{
    BTN  *t1,*t,*t2;
   int iterm;
    scanf("%d",&iterm);
    if(iterm==stop) {t=NULL;return t;}
    else{
        if(!(t=new BTN(iterm) )) return NULL;
        t1=CBT(stop);
        t->left=t1;
        t2=CBT(stop);
        t->right=t2;
        t->high = max( hi(t->left), hi(t->right) ) + 1;
        return root=t;

    }
}
int tlist:: GetHeight(BTN* BT )
{
     if(BT==NULL)
    {
        return -1;
    }
    else{
       return 1+max(GetHeight(BT->left),GetHeight(BT->right));
    }
}


int main()
{
    tlist a;
    int n;
    scanf("%d",&n);
    BTN*p;
    p=a.CBT(-1);
    int x;
    x=a.GetHeight(p);
    printf("%d\n%d",p->high,p->iterm);

    i++;
    for(int j=0;j<x;j++)
    {
        if(a.hi(p->left)==a.hi(p->right))
                {
                    p=p->right;
                    printf(" %d",p->iterm);
                }
        else if(a.hi(p->left)>a.hi(p->right))
                {
                    p=p->left;
                     printf(" %d",p->iterm);
                }
            else if(a.hi(p->left)<a.hi(p->right))
                {
                    p=p->right;
                     printf(" %d",p->iterm);
                }
    }

    return 0;

}

二、森林的层次遍历

题意:已知森林的先序序列及其对应的节点数求其层次遍历序列

思路:

做此题前一定要仔细审题,才开始我审成树的先序,然后一直段错误.................。此题有两种做法,一种是根据先序及节点数采用儿子兄弟链表法将森林转换为二叉树(栈),然后借助队列层次遍历。

另一种是我学习了其他同学代码后,才想到利用先根序列递归建树,这样简洁快速,并同时运用vector来保存每个节点儿子,这个是做题时高效的,首推此法。

代码

1)、非递归建树代码:(不推荐太麻烦但仅供提供思路,利用栈建树,队列)

#include<iostream>
#include<fstream>
#include<sstream>
#include<stack>
#include<queue>
using namespace std;
struct treenode{
   char iterm;
   int num;
   treenode* leftc;
   treenode* rightb;
   treenode(char n):iterm (n),num(0),leftc(0),rightb(0){}
}*a[100050];
typedef treenode* ptn;
typedef treenode CBT;
 class tlist{
public:
    ptn root;
    tlist() :root(0){}

};
stack <treenode*> q0;
 int i=0;
CBT*  levelorder(CBT*t);
int main()
{   char ch;
    int n,s;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>ch;
        a[i]=new treenode(ch);
    }
     for( i=0;i<n;i++)
    {
        cin>>s;
        a[i]->num=s;
    }
if(n==1) {cout<<a[0]->iterm;return 0;}
else{
    int k=i-1;
    for(;k>0;)
    {
       while(a[k]->num==0)
       {
           q0.push(a[k]);
           k--;
       }
        ptn p0;
        p0=q0.top();
        q0.pop();
        a[k]->leftc=p0;
        a[k]->num--;
        ptn p1=NULL;
       while(a[k]->num!=0)
        {
        p1=q0.top();
        q0.pop();
        p0->rightb=p1;
        p0=p1;
        a[k]->num--;
        }
        q0.push(a[k]);
        k--;
    }
  ptn root=new treenode(0);
    root->leftc=a[0];
    ptn qr;
     int l=1;
     if(q0.size()>1)
        {   while(!q0.empty())
                {if(l==1) { qr=q0.top();q0.pop();l++;}
                 else { ptn tmp;
                      tmp=q0.top();
                      q0.pop();
                       qr->rightb=tmp;
                       qr=tmp;
                      }
                }
            levelorder(root);
            return 0;
        }
 else { levelorder(a[0]);}
    return  0;
 }
    }
CBT*  levelorder(CBT*t)
{
    int sum=0;
    typedef CBT* BinTree;
    queue<BinTree> q;
   if(t!=NULL)
   {
       BinTree p;
       q.push(t);
       while(!q.empty())
       {   p=q.front();
           q.pop();
         printf("%c ",p->iterm);
           p=p->leftc;
           sum++;
           cout<<sum<<" ";
           if(sum==(i)){ break;}
           while(p!=NULL)
           {
               q.push(p);
               p=p->rightb;
           }
       }
        printf("%c",q.front()->iterm);
   }
    cout<<endl;
    return t;
}

2)、递归建树:

#include<stack>
#include<queue>
#include<vector>
#include<iostream>
#define maxa 100010
using namespace std;
char iterm [maxa];//若开在main函数内会堆栈溢出。
vector <int> a[maxa];//开向量存储儿子节点
int num[maxa];//将num,iterm开成全局变量不会使堆栈溢出
queue <int> q;
int pi=0;
void CBT(int x);//递归建树
int main()
{
    int n=0,i=0;
    scanf("%d",&n);
    for( i=1;i<=n;i++)
    cin>>iterm[i];
    pi++;
    for( i=1;i<=n;i++)
    {
        cin>>num[i];


    }
    while(pi<=n)
    {
        a[0].push_back(pi);//由于队列栈都是在双端队列基础上封装的,此将元素写入队尾
        CBT(pi);
        pi++;

    }
    q.push(0);
    while(!q.empty())
    {
        int temp=q.front();
        q.pop();
        for(vector<int>::iterator pointer=a[temp].begin();pointer!=a[temp].end();pointer++)//向量遍历器
        {

            q.push(*pointer);

        }
    if(temp){
        cout<<iterm[temp];
        if(!q.empty())
            printf(" ");
            else printf("\n");
    }

    }


}
void CBT(int x)//递归建树
{
    int i;
    for( i=0;i<num[x];i++)
        {
            a[x].push_back(++pi);
            CBT(pi);
        }
}

 

 

三、纸带切割

 

题意:求出可达成切割目标的最小切割代价,以及每次切割时的代价;

思路:通过观察,你可已发现输出序列,为反哈夫曼树,于是我们可以利用哈夫曼编码方式解决。考试时,在这里我找了不必要的麻烦,其实没必要建树。我们只是模仿该过程即可于是,经过思考我采用堆方式优化排序求和(也可采用优先队列,不过要自己写优先级)的方式,即每求一次和时将此和放入队中下沉,最后再取堆顶元素。

代码:

1)、建堆以及维护堆:

void swap(long long int *x,long long int*y)
{
    long long int t;
    t=*x;
    *x=*y;
    *y=t;
}
void down1(long long int x ) // h[x]下沉
{
long long int i = x, y ;
while ( 2*i <= hlen && h[ 2*i ] <h[ i ] ||2*i+1<=hlen && h[ 2*i + 1 ]<h[ i ] ) {
y = 2 * i;
if ( 2*i+1<=hlen && h[ 2*i+1 ]<h[ 2*i ] ) y++;
swap(&h[i], &h[y]);
i = y;
}
}
void build1() {
hlen = n;
for(int i=1 ; i<=n ; i++) h[ i ]=a[ i ];
for(int i=n/2 ; i>=1 ; i--) down1( i );
}

void insert( int x ) // 插入x
{
hlen++;
h[hlen] = x;
down1 (hlen);
}

2)、完整代码:

#include<stdio.h>
long long int n=0,hlen=0,a[1000000],h[1000000];
void build1();
void down1(long long int x );
void swap(long long int *x,long long int*y);
void up(int x) ;
void insert( int x );


int main()
{   long long int a0[100000],a1,x=0,y=0,s=0;
   scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    build1();
    long long int s1=1,k=0;
    for(int i=1 ; i<n ; i++)
    {x=h[1];
    swap(&h[1], &h[hlen]);
    hlen--;
    down1(1);
    y=h[1];
    a0[k]=x+y;
    s+=a0[k];
    insert( a0[k] );
    k++;
    swap(&h[1], &h[hlen]);
    hlen--;
    down1(1);

    }
    printf("%lld\n",s);
    for(int i=k-1;i>=0;i--)
    {
        printf("%lld",a0[i]);
        if(i>0) printf(" ");
    }
//    printf("%d %d\n%d %d",a1,s2,a0,s1);
    return 0;
}
void swap(long long int *x,long long int*y)
{
    long long int t;
    t=*x;
    *x=*y;
    *y=t;
}
void down1(long long int x ) // h[x]下沉
{
long long int i = x, y ;
while ( 2*i <= hlen && h[ 2*i ] <h[ i ] ||2*i+1<=hlen && h[ 2*i + 1 ]<h[ i ] ) {
y = 2 * i;
if ( 2*i+1<=hlen && h[ 2*i+1 ]<h[ 2*i ] ) y++;
swap(&h[i], &h[y]);
i = y;
}
}
void build1() {
hlen = n;
for(int i=1 ; i<=n ; i++) h[ i ]=a[ i ];
for(int i=n/2 ; i>=1 ; i--) down1( i );
}

void insert( int x ) // 插入x
{
hlen++;
h[hlen] = x;
down1 (hlen);
}
/*
5
5 6 7 2 4
*/

四、序列乘积:

题意:求两个等长递增序列的乘积中从小到大的前n个。

思路:

当时做的时候,首先用暴力法尝试,即乘完后快排,只拿了40分,后又优化:a,b数组存储两个递增序列, 缩小乘积数组c的长度使其长度为a的两倍,初始化时将a[1]与b数组

全部元素相乘后输入c中然后,每次乘n次,将结果赋予c[n+1]~c[n+2],归并排序一次(也可快排,或桶排序),均拿了60分,最后超时。于是课后参考其他同学,采用优先队列的方法写了一遍,才通过。我们利用单调序列的性质,跟刚才一样的初始化,后每次去最小,再从其右边取即可。

代码:

1)、参考节点:

struct node
{
    int xn,yn;
    lli multi;
    bool friend operator<(const node&s1,const node&s2 )//定义优先级
    {
        return  s1.multi>s2.multi;
    }
    node(int x,int y,lli v):xn(x),yn(y),multi(v){}

};

2)、关键代码,每次去队头后,入队对头右边元素:

   for(int i=1;i<=n;i++)
       {
           node temp=s.top();//队头为最小值输出
           printf("%lld",temp.multi);
           s.pop();
           if(temp.yn<n) s.push(node(temp.xn,temp.yn+1,a[temp.xn]*b[temp.yn+1]));//取完后直接让上一最小值右边元素入队;
           if(i<n) printf(" ");
       }

3)、完整代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long lli;
#define maxa 100005
int n;
lli a[maxa];//将a,b开成全局变量不会使堆栈溢出,若开在main函数内会堆栈溢出。
lli b[maxa];

struct node
{
    int xn,yn;
    lli multi;
    bool friend operator<(const node&s1,const node&s2 )//定义优先级
    {
        return  s1.multi>s2.multi;
    }
    node(int x,int y,lli v):xn(x),yn(y),multi(v){}

};
priority_queue<node>s;//声明优先队列
int main()
{
    scanf("%d",&n);
    lli temp;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
        {

            scanf("%lld",&b[i]);
            if(i==1)
                {
                    for(int j=1;j<=n;j++)
                        s.push(node(j,1,a[j]*b[1]));//初始化
                }
        }
        for(int i=1;i<=n;i++)
       {
           node temp=s.top();//队头为最小值输出
           printf("%lld",temp.multi);
           s.pop();
           if(temp.yn<n) s.push(node(temp.xn,temp.yn+1,a[temp.xn]*b[temp.yn+1]));//取完后直接让上一最小值右边元素入队;
           if(i<n) printf(" ");
       }

    return 0;
}
/*
5
1 3 5 7 9
2 4 6 8 10
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值