线性表题解

NO.1:集合合并

题目要求

这里是题干:

问题: 线性表的合并A=A∪B
设:有两个集合A和B分别用两个线性表LA和LB表示

求:一个新的集合 A=A∪B

输入:两个集合

输出:按照要求合并的集合

注意:提交代码的时候,仅需提交你编写的那三个基本操作函数即可。

 然后看下给出的输入输出样例:

测试输入期待的输出时间限制内存限制
测试用例 1以文本方式显示
  1. 5↵
  2. 1 2 3 4 5↵
  3. 6↵
  4. 3 4 5 6 7 8↵
以文本方式显示
  1. Output La:1,2,3,4,5,↵
  2. Output Lb:3,4,5,6,7,8,↵
  3. Result La:1,2,3,4,5,6,7,8,↵
1秒64M
测试用例 2以文本方式显示
  1. 0↵
  2. 4↵
  3. 1 2 3 4↵
以文本方式显示
  1. Output La:↵
  2. Output Lb:1,2,3,4,↵
  3. Result La:1,2,3,4,↵
1秒64M
测试用例 3以文本方式显示
  1. 5↵
  2. 8 9 0 1 2↵
  3. 6↵
  4. 8 9 0 3 2 1↵
以文本方式显示
  1. Output La:8,9,0,1,2,↵
  2. Output Lb:8,9,0,3,2,1,↵
  3. Result La:8,9,0,1,2,3,↵
1秒64M

 预设代码

#include <stdio.h>  
#include <stdlib.h>  
#define LIST_MAX_SIZE  100  //空间初始大小  
#define OK 1  
#define ERROR 0  
  
typedef int ElemType;       //元素的数据类型  
typedef int Status;         //状态。函数返回值   
typedef struct {  
//  ElemType elem[ LIST_MAX_SIZE ]; // 存储空间  
    ElemType * elem;    // 存储空间  
    int  length;        // 当前元素个数  
    int  listsize;      // 能够保存的最大元素数量   
} SqList;  
  
// 以下为函数原型   
Status InitList( SqList & );  
Status ListInsert( SqList &, int, ElemType );   //这是需要你编写的基本操作   
Status GetElem( SqList, int, ElemType & );  //这是需要你编写的基本操作   
int    ListLength( SqList );        //这是需要你编写的基本操作  
Status ListTraverse( SqList &, void (*)( ElemType ) );  
void   ListUnion( SqList &, SqList );  
void   out( ElemType );  
int    equal(ElemType, ElemType );   
Status LocateElem(SqList, ElemType, Status (*)(ElemType,ElemType));  
  
// 以下为函数定义  
Status InitList( SqList & L )   // 建立一个空的线性表 L  
{  
    L.elem = (ElemType *)malloc(LIST_MAX_SIZE*sizeof(ElemType));  
//  if ( !L.elem )  exit(-1);   // 失败则终止程序   
    L.length    = 0;            // 空表长度为0  
    L.listsize  = LIST_MAX_SIZE;  
    return OK;  
}  
  
Status ListTraverse( SqList &L, void (*visit)( ElemType ) )  
{   // 依次对L的每个元素调用函数visit()。若visit()失败,则操作失败  
    int i, L_len = ListLength( L );  
    ElemType e;  
      
    for ( i = 1;  i <= L_len; i++ )  {  
        GetElem(L, i, e);  
        (*visit)( e );  
    }  
    return OK;  
}  
  
int equal(ElemType x, ElemType y)  
{   return x==y;  
}  
  
Status LocateElem( SqList L, ElemType e,  
                   Status (*compare)(ElemType,ElemType) )  
{   //在L中查找与元素 e 满足compare() 的第 1 个元素  
    //返回 L 中第 1 个与 e 满足关系compare( ) 的元素的位序  
    int i = 1;  
    ElemType * p;  
    while ( i<=L.length )  //  
        if  ( (*compare)(e,L.elem[i-1]) ) break;  
        else  i++;  
    if ( i <= L.length )  return i;  // 找到 e,返回位序i  
    else return 0;      //若没有找到,则返回0  
}  
  
void out( ElemType e )  
{   printf("%d,", e);  
}  
  
void ListUnion( SqList &La,  SqList Lb ) //求 A=A∪B  
{   int La_len, Lb_len, i;  
    ElemType e;  
   
    La_len = ListLength( La );       // 求线性表的长度  
    Lb_len = ListLength( Lb );  
    for ( i = 1;  i <= Lb_len;  i++ )  {  
        GetElem(Lb, i, e);  // 取Lb中第i个数据元素赋给e  
        if ( !LocateElem( La, e, equal ) )   
            ListInsert ( La, ++La_len, e ); // La中不存在和 e 相同的数据元素,则插入  
    }  
}  
  
int main()  
{   SqList La, Lb;  
    int n, i;  
    ElemType e;  
      
    InitList( La );  
    InitList( Lb );  
    scanf("%d", &n);        //读入集合A   
    for ( i=0; i<n; i++ )  
    {   scanf("%d", &e);  
        ListInsert( La, i+1, e );  
    }  
    scanf("%d", &n);        //读入集合B   
    for ( i=0; i<n; i++ )  
    {   scanf("%d", &e);  
        ListInsert( Lb, i+1, e );  
    }  
    printf("Output La:");  
    ListTraverse( La, out );  
    printf("\nOutput Lb:");  
    ListTraverse( Lb, out );  
    ListUnion( La, Lb );  
    printf("\nResult La:");  
    ListTraverse( La, out );  
    printf("\n");  
    return OK;  
}  
  
/**************** 
Status ListInsert( SqList &L, int i, ElemType e ) 
{   //在顺序线性表L中第 i (1≤i≤L.length+1)个位置之前插入元素e, 
 
    Here is wating for you. 
 
} 
 
Status GetElem(SqList L, int i, ElemType &e) 
{ 
 
    Here is wating for you. 
 
} 
 
int ListLength(SqList L) 
{    
 
    Here is wating for you. 
 
} 

信息梳理

题目比较简单,所以就简单梳理一下预设代码中的信息。

首先是创建了线性表的结构体SqList,里面包含三个元素:

typedef struct {  
    ElemType * elem;    // 存储空间  
    int  length;        // 当前元素个数  
    int  listsize;      // 能够保存的最大元素数量   
} SqList; 

 然后是它写好的基本操作:

Status InitList( SqList & );  
Status ListTraverse( SqList &, void (*)( ElemType ) );  
void   ListUnion( SqList &, SqList );  
void   out( ElemType );  
int    equal(ElemType, ElemType );   
Status LocateElem(SqList, ElemType, Status (*)(ElemType,ElemType));  

所以这道题是不需要你去实现合并操作的,你只需要补全基本操作就即可:

Status ListInsert( SqList &, int, ElemType );   //这是需要你编写的基本操作   
Status GetElem( SqList, int, ElemType & );  //这是需要你编写的基本操作   
int    ListLength( SqList );        //这是需要你编写的基本操作 

代码实现

因为是基本操作所以就直接放代码了,如果是线性表不熟悉的可以去看一看线性表的介绍:

Status ListInsert( SqList &L, int i, ElemType e )   
{   //在顺序线性表L中第 i (1≤i≤L.length+1)个位置之前插入元素e,   
    if(L.length==0){  
        L.elem[0]=e;  
    }  
    else{  
        for(int j=L.length;j>=i;j--){  
            L.elem[j]=L.elem[j-1];  
        }  
        L.elem[i-1]=e;  
    }  
    L.length++;  
}   
   
Status GetElem(SqList L, int i, ElemType &e)   
{   e=L.elem[i-1];  
    return OK;  
}   
   
int ListLength(SqList L)   
{   return L.length;  
}  

NO.2:链表原地逆序

题目要求

请编写函数 void inverse( LinkList ),实现单链表的原地置逆。即利用原来的结点将线性表:L =(a1, a2, …… , an)

变换为:L =( an,  …… , a2, a1)

预设代码 

注意:不要直接划走!!!继续往下看!!!

#include <iostream>  
using namespace std;  
  
typedef int ElemType;  
typedef struct node  
{   ElemType    data;  
    struct node * next;  
} NODE;  
typedef NODE * LinkList;  
  
void output( LinkList );  
void change( int, int, NODE * );  
LinkList createList( ElemType );  
void inverse( LinkList );   
  
LinkList createList( ElemType finish )  //finish:数据结束标记   
{  
    ElemType x;  
    NODE *newNode;   
    LinkList first = new NODE;   // 建立头结点  
    first->next = NULL;  
    first->data = finish;  
    cin >> x;                 // 约定以finish结束连续输入  
    while ( x != finish )  
    {  
        newNode = new NODE;      // 建立新结点  
        newNode->data = x;  
        newNode->next = first->next; // ①  
        first->next = newNode;       // ②  
        cin >> x;  
    }  
    return first;  
}  
  
void output( LinkList head )  
{   cout << "List:";  
    while ( head->next != NULL )  
    {   cout << head->next->data << ",";  
        head = head->next;  
    }  
    cout << endl;  
}  
  
int main(int argc, char** argv)   
{  
    LinkList head;  
  
    head = createList( -1 );  
    output( head );  
    inverse( head );  
    output( head );  
    return 0;  
}  

PS:这里说明下哈!这些题目的预设代码大多是用C++的,的是仔细看的话会发现,其实跟C语言差不多,并且C语言在C++环境下是可以直接编译的,所以如果老师要求用C++的话,用C语言也是完全没有问题的!

信息梳理

然后进行一个简单的信息梳理,首先是链表结点(包括指针域和数据域),同时注意这个链表是带有头结点的:

typedef struct node  
{   ElemType    data;  
    struct node * next;  
} NODE;  
typedef NODE * LinkList; 

 然后是定义的三个基本操作,我们需要完成其中的void inverse( LinkList ) 

void output( LinkList );  
void change( int, int, NODE * );  
LinkList createList( ElemType );  
void inverse( LinkList );   

然后注意以下就是LinkList createList( ElemType )它是使用头插法的。

代码实现

这是一道经典的链表题,其难点在于”原地“二字。意思就是要求在该链表的基础上实现置逆,而不能再去开辟新的链表空间,而这样的现实意义就是:尽可能地实现空间的复用,是空间效率更高!

 直接从整个来链表看的话,似乎有点难下手,我们不妨将其减治:

我们假设只有两个结点,那么问题就很简单了,我们需要将1、2结点的连接断开同时将2指向1,以及将1指向空,这样两步操作。然后我们试着画一下三个结点:

发现同样是两步操作!!!好像问题变的简单起来了!!!我们不妨加上头结点看一看:

头结点好像有点不一样,每次置逆后,它要指向新链表的第一个结点。所以我们先不动头结点,每处理一次结点,就将其指向第一个结点。所以我们用一个结点*current来表示当前遍历的结点,又因为涉及到指针链接的断开,我们需要用一个结点*next来保存当前结点的后继,用*pre存好每次处理后的第一个结点:

LinkList pre=NULL;  
LinkList current=head->next;  //第一个遍历的结点当然是第一个结点
LinkList next=NULL; 

 所以我们这样写:

void inverse( LinkList head )  
{   LinkList pre=NULL;  
    LinkList current=head->next;  
    LinkList next=NULL;  
    while(current){  
        next=current->next;  
        current->next=pre;  
        pre=current;  
        current=next;  
    }  
    head->next=pre;  
}  

next=current->next; 

//这是保存当前结点的后继

current->next=pre;
pre=current;  

//将当前结点指向第一个结点,并更新第一个结点

current=next;  

//然后更新next,进入下一次循环

然后当current为空时,循环停止。退出循环后,将我们之前没处理的头结点head指向现在链表的第一个结点pre。

试着运行一下:

过啦!!!

源码

集合合并

链表原地置逆​​​​​​​

这两道题两周之前的题了(最近题有点多)(●'◡'●)。我抓紧把这两周的题解补上,也算是对知识的巩固吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值