您的位置:首页 > 理论基础 > 数据结构算法

数据结构-线性表

2019-06-11 16:02 851 查看

例1、假设有两个线性表LA和LB分别表示两个集合A和B,现要求一个新集合A=A U B(集合的并)。

算法思想:扩大线性表LA,将表LB中不在LA中出现的元素插入到LA中。只要从线性表LB中依次取出每个元素,按值在线性表LA中查找,若没查到则插入之。

算法描述:

[code]void union (Linear_List LA ,Linear_List LB)            //假定Linear_List是线性表类型
{
    n = ListLength(LA);                                //求LA的表长
    for(i=1;i<=ListLength(LB),i++)                    //遍历Linear_List LB 的元素
    {
        x = GetNode(LB,i);                            //取LB中第i个元素赋值给x
        if(LocateNode(LA,x) == 0)                    //按值查找,与表LA的值一一比对
        {
            InsertList(LA,++n,x);                    //在表LA的值之后插入x的值
        }    
    }
}


例2、删除线性表L中重复的元素

算法思想:从表L中的第一个元素(i=1)开始,逐个检查i位置以后的任一位置j,若两元素值相同,则从表中删除第j个元素,。。。。直到i移到当前表L的最后一个位置为止。

算法描述:

[code]void purge (Linear_List L)
{
    i =1 ;                          //假设从第一个元素开始,i=1
    while (i<ListLength (L))        // 当 元素i 在表L内
    {
        x = GetNode(L,i);           //取表L第i个元素的值赋值给x
        j=i+1;                      //假设查找从第i+1开始
        while(j<=ListLength(L))     //当 元素j 在表L内
        {
            y=GetNode(L,j);         //取表L第j个元素的值赋值给y
            if(x==y)                //如果x等于y
            {
                DeleteList(L,j);    //删除表L第j个元素
            }
            else
            {
                j++;                //否则j+1;
            }
        }
        i++;                        //当j+1后,i相应也+1
    }
}


例3、顺序线性表插入操作

算法描述:

[code]void InsertList(seqlist *L ,int i ,DataType x)                
//在顺序表L中第i个位置之前插入一个新元素x
{
    int j ;
    if(i<1  ||  i>L->length+1)
    {
        printf("position error");
        return ;
    }
    if(L->length >= ListSize)
    {
        printf("overflow");
        return ;
    }
    for(j=l->length-1;j>=i-1;j--)
    {
        L->data[j+1]=L->data[j];                            //从最后一个元素开始逐一后移
        L->data[i-1]=x;                                     //在第i个元素之前插入新元素x
        L->length++;                                        //实际表长+1
    }
}


例4、顺序表删除操作

算法描述:

[code]DataType DeleteList (SeqList *L ,int i)                        
//在顺序表L中删除第i个元素,并返回被删除元素
{
    
    
    int j ;
    DataType x;                                                
//DataType是一个通用类型标识符,在使用时再定义实际类型
    if(i<1 || i->length)
    {
        printf("position error");
        exit(0);                                            //出错退出处理
    }
    x = L->data[i];                                         //保存被删除元素
    for(j=i;j<=L->length ;j++)
    {
        L->data[j-1]=L->data[j];                            //元素前移
    }
    L->length--;                                            //实际表长减1
    return x;                                               //返回被删除的元素
}

例5、已知一长度为n的顺序存储的线性表,试写一算法将该线性表逆置。

算法思想:先以表长的一半作为循环控制次数,将表中最后一个元素同顺数第一个元素交换,将倒数第二个元素同顺数第二个元素交换,依此类推,直至交换完为止。

算法描述:

[code]SeqList Converts (SeqList L)
{
    DataType x ;
    int i ,k ;
    k=L.length/2;
    for(i=0;i<k;i++)
    {
        x=L.data[i];                             //把表L第i个元素赋值给x
        L.data[i]=L.data[L.length -i-1];         //把表L第L.length-i-1 个元素赋值给 第i个元素
        L.data[L.length-i-1]=x;                  //把值x赋值给表L第L.length-i-1 个元素
    }
    return L;                                    //返回线性表L
}


例6、试写一算法,实现在顺序表中查找出最大值和最小值的元素及所在位置。

算法思想:如果在查找最大值和最小值的元素时各扫描一遍所有元素,则至少要比较2n次,可以使用一次扫描找出最大值和最小值的元素。另外,在算法中要求带回求得的最大值和最小值元素及其所在位置,可用4个指针变量参数间接得到,也可以用外部变量实现,因为函数本身只可返回一个值。下面是采用指针变量参数来实现。

算法描述:

[code]void MaxMin (SeqList L ,DataType *max ,DataType *min ,int *k ,int *j)
{
    int i ;
    *max=L.data[0];
    *min=L.data[0];
    *k=*j=1;                                //先假设第一个元素既是最大值,也是最小值。
    for(i=1;i<L.length ;i++)
    {
        if(L.data[i]>*max)
        {
            *max =L.data[i];
            *k=i;
        }
        else if (L.data[i]<*min)
        {
            *min =L.data[i];
            *j=i;    
        }
    }
}


例7、用头插法建立单链表

算法思想:头插法建表是从一个空表开始,重复读入数据,生成新结点,将读入的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。

算法描述:假设线性表中结点的数据域为字符型

[code]LinkList CreateListF()
{
    LinkList head;
    ListNode *p;
    char ch;
    head =NULL;                                        //置空单链表
    ch=getchar();                                      //读入第一个字符
    while(ch!='\n')                                    //读入字符不是结束标志符时作循环
    {
        p =(ListNode *)malloc(sizeof(ListNode));       //申请新结点
        p->data =ch;                                   //数据域赋值
        p->next =head;                                 //指针域赋值
        head =p ;                                      //头指针指向新结点
        ch =getchar();                                 //读入下一个字符
    }
    return head;                                       //返回链表的头指针
}

例8、用尾插法建立(不带头结点)单链表

算法思想:将新结点插入在当前链表的表尾上,因此需要增设一个尾指针rear,使其始终指向链表的尾结点。

算法描述:假设线性表中结点的数据域为字符型

[code]LinkList CreateListR()
{
    LinkList head ,rear ;
    ListNode *p;
    char ch;
    head =NULL; 
    rear=NULL;                                             //置空单链表
    ch = getchar();                                        //读入第一个字符
    while(ch!='\n')                                        //读入字符不是结束标识符时作循环
    {
        p=(ListNode *)malloc(sizeof(ListNode));            //申请新结点
        p->data =ch;                                       //数据域赋值
        if(head==NULL)
        {
            head =p ;                                      //新结点*p插入空表
        }
        else
        {
            rear->next =p ;                         //新结点*p插入到非空表的表为结点*rear之后
            
        }
        rear =p;                                    //表尾指针指向新的表尾结点
        ch =getchar();                              //读入下一个字符
    }
    if(rear!=NULL)
    {
        rear->next =NULL;                          //终端结点指针域置空
    }
    return head;
}


例9、用尾插法建立(带头结点)的单链表

算法思想:为简化算法,方便操作,可在链表的开始结点之前附加一个结点,并称其为头结点,

算法描述:

[code]LinkList CreateListR1()                  
 //尾插法建立带头结点的单链表算法
{
        LinkList head =(ListNode *)malloc(sizeof(ListNode));         //申请头结点
        ListNode *p,*r ;
        DataType ch;
        r=head;                                                      //尾指针初始指向头结点
        while(ch=getchar())!='\n')
        {
            p=(ListNode *)malloc(sizeof(ListNode));                  //申请新结点
            p->data =ch;
            r->next =p ;                                             //新结点连接到尾结点之后
            r= p ;                                                   //尾指针指向新结点
        }
        r->next =NULL;                                               //终端结点指针域置空
        return head;     
        
}


例10、查找运算(带头结点),按结点序号查找

算法思想:在单链表中要查找第i个结点,就必须从链表的第1个结点(开始结点,序号为1)开始,序号为0是头结点,p指向当前结点,j为计数器,其初始值为1,当p扫描下一个结点时,计算器加1。当j=i时,指针p所指向的结点就是要找的结点。

算法描述:

[code]ListNode * GetNodei (LinkList head ,int i)                
//head是带头结点的单链表的头指针,i为要查找的结点序号
                                                        
//若查找成功,则返回查找结点的存储地址(位置),否则返回NULL
    ListNode *p ;
    int j ;
    p=head ->next ;
    j=1;                                       //使p指向第一个结点,j置1
    while(p!=NULL && j<i)                     //顺指针向后查找,直到p指向第i个结点或p为空为止
    {
        p=p->next ;
        ++j;
    }
    if(j==i)
    {
        return p ;
    }
    else
    {
        return NULL;
    }


    
    
例11、查找运算(带头结点),按结点值查找

算法思想:在单链表中按值查找结点,就是从链表的开始结点出发,顺链逐个将结点的值和给定值k进行比较,若遇到相等的值,则返回该结点的存储位置,否则返回NULL。按值查找算法要比按序号查找更为简单。

算法描述:

[code]ListNode *LocateNodek (LinkList head ,DataType k)          
{

//head为带头结点的单链表的头指针,k为要查找的结点值
//若查找成功,则返回查找结点的存储地址(位置),否则返回NULL
    ListNode *p =head->next ;                           //p指向开始结点
    while(p&&p->data! =k)                               //循环直到p等于NULL或p->data等于k为止
    {
        p=p->next;                         //指针指向下一个结点
    }
    return p;                              //若找到值为k的结点,则p指向该结点,否则p为NULL
}

例12、插入运算

算法思想:先使p指向ai-1的位置,然后生成一个数据域值为x的新结点*s,再进行插入操作。

算法描述:

[code]void InsertList(LinkList head ,int i ,DataType x)  
{          
//在以head为头指针的带头结点的单链表中第i个结点的位置上
                                                          
 //插入一个数据域为x的新结点
    ListNode *p ,*s ,int j ;
    p=head;
    j=0;
    while (p!=NULL && j<i-1)                                //使p指向第i-1个结点
    {
        p=p->next;
        ++j;
    }
    if(p==NULL)                                             //插入位置错误
    {
        printf("ERROR\n ");
        return ;
    }
    else
    {
        s=(ListNode *)malloc(sizeof(ListNode));             //申请新结点
        s->data =x;
        s->next =p->next;
        p->next =s;
    }
}


    
    
例13、删除运算

算法思想:删除运算就是将链表的第i个结点从表中删去。由于第i个结点的存储地址是存储在第i-1个结点的指针域next中,因此要先使p指向第i-1个结点,然后使得p->next指向第i+1个结点,再将第i个结点释放掉。

算法描述:

[code]DataType DeleteList(LinkList head, int i)            
//在以head为头指针的带头结点的单链表中删除第i个结点
{
    ListNode *p,*s ;
    DataType x;
    int j ;
    p=head;
    j=0;
    while(p! =NULL&&j<i-1)                            //使p指向第i-1个结点
    {
        p=p->next;
        ++j;
    }
    if(p==NULL)                                       //删除位置错误
    {
        printf(“位置错误\n”);
        exit(0);                                      //出错退出处理
        
    }
    else
    {
        s=p->next;                                    //s指向第i个结点
        p->next =s->next;                             //使p->next指向第i+1个结点 
        x=s->data;                                    //保存被删除结点的值
        free(s);
        return x;                                     //删除第i个结点,返回结点值
    }
    
}


    
例14、试写一算法,将一个头结点指针为a的带头结点的单链表A分解成两个单链表A和B,其中头结点指针分别为a和b,使得A链表中含有原链表A中的序号为奇数的元素,而B链表中含有原链表为偶数的元素,并保持原来的相对顺序。

算法思想:根据题目要求,需要遍历整个链表A,当遇到序号为奇数的结点时将其链接到A表上,序号为偶数结点时,将其链接到B表中,一直到遍历结束。

算法描述:

[code]void split (LinkList a ,LinkList b)                  
 //按序号奇偶分解单链表,注意b在调用该算法前是一个带头结点的空链表
{
    ListNode *p ,*r ,*s ;
    p=a->next;                //p指向表头结点
    r=a;                      //r指向A表的当前结点
    s=b;                      //s指向B表的当前结点
    while(p!=NULL)
    {
        r->next =p ;          //序号为奇数的结点链接到A表上
        r=p;                  //r总是指向A链表的最后一个结点
        p=p->next;
        if(p)
        {
            s->next =p ;      //序号为偶数的结点链接到B表中
            s=p;              //s总是指向B链表的最后一个结点
            p=p->next;        //p指向原链表A中的偶数序号的结点
        }
    }
    r->next =s->next =NULL;
    
}

例15、假设头指针为La和Lb的单链表(带头结点)分别为线性表A和B的存储结构,两个链表都是按结点数据值递增有序的。试写一算法,将这两个单链表合并为一个有序链表Lc。

算法思想:根据题目要求需设立三个指针pa、pb、pc,其中pa和pb分别指向La表和Lb表中当前待比较插入的结点,而pc则指向Lc表当前的最后一个结点,若pa->data<=pb->data,则将pa所指向的结点之后,否则将pb所指向的结点链接到pc所指的结点之后。

算法描述:

[code]LinkList MergeList(LinkList La ,LinkList Lb)        //归并两个有序链表La和Lb为有序链表Lc
{
    ListNode * pa , *pb ,*pc ;
    ListList Lc;
    pa =La ->next ;
    pb =Lb ->next ;                                 //pa和pb分别指向两个链表的开始结点
    Lc =pc =La ;                                    //用La的头结点作为Lc的头结点
    while (pa!=NULL&&pb! =NULL)
    {
        if(pa->data<=pb->data)
        {
            pc->next =pa ;
            pc=pa;
            pa=pa->next;
            
        }
        else
        {
            pc->next=pb;
            pc=pb;
            pb=pb->next;
        }
    }
    pc->next =pa!=NULL?pa:pb;             //插入链表剩余部分
    free (Lb);                            //释放Lb的头结点
    return Lc;                            //返回合并后的表
}

例16、已知有一个结点数据域为整型的,且按从大到小顺序排列的头结点指针为L的非空单循环链表,试写一算法插入一个结点(其数据域为x)至循环链表的适当位置,使之保持链表的有序性。

算法思想:要解决这个算法问题,首先就是要解决查找插入位置问题,查找就要使用循环语句,从表头开始逐个结点比较。判断整个循环链表是否比较结束,就是判断当前结点的指针域是否等于头指针。另外,由于链表是递减有序的,在查找插入位置时,循环条件是当前结点值是否要大于要插入的结点值,所以,若用q指向表中当前结点,那么循环条件应该是q->data>x&&q!=L ,还要使用一个指针p指向插入位置的前驱结点。

算法描述:

[code]void InsertList(LinkList L ,int x )             //将值为x的新结点插入到有序循环链表中适当的位置
{
    ListNode *s ,*p ,*q ;
    s =(ListNode *)malloc (sizeof(ListNode));   //申请结点存储空间
    s->data =x ;
    p=L;
    q=p->next;                                 //q指向开始结点
    while(q->data>x&&q!=L)                    //查找插入位置
    {
        p=p->next;                             //P指向q的前驱结点
        q=p->next;                             //q指向当前结点
        
    }
    p->next =s;                                //插入*s结点
    s->next =q;                                
}

例17、假设有一个头结点指针为head的循环双向链表,其结点类型结构包含三个域:prior、data和next。其中data为数据域,next为指针域,指向其后继结点,prior也为指针域,其值为空(NULL),因此该双向链表其实也是一个单循环链表。试写一算法,把其表修改成真正的双向循环链表。

算法思想:根据题目要求,就是要将表中的每个prior域填上相应的前驱结点地址。只要弄清楚双向循环链表的结构,其算法就非常简单。其实只需要以p指向头结点开始,循环使用语句p->next->prior=p ;和 p=p->next;即可实现题目要求。

算法描述:

[code]void trans(DLinkList head)
{
    DLNode *p ;
    p =head ;                    //使p指向头结点
    while(p->next! =head)        //依次从左向右,对每个结点的prior赋值
    {    
        p->next->prior =p ;      //p所指结点的直接后继结点的前驱就是p
        p=p->next;               //p指针指向下一个结点
    }
    head—>prior =p ;             //head 的前驱指向表的终端结点
}


例18、设有两个顺序表A和B,且都递增有序。试写一算法,从A中删除与B中相同的那些元素(也就是计算A-B)。

算法思想:扫描整个B表,顺序取表中每一个元素,然后与表A中从某下标开始的元素进行比较(因为两表都是有序的,不必每次从头开始,用一个变量k标识上一次比较结束的位置),当B中某个元素值大于或等于A中某个元素时,比较结束。记住A表的当前下标值K,之后再比较两元素值是否相等,若相等,则从表A中删除该元素,而后继续B中的下一个元素与A中第k个元素开始向后比较;否则,继续,直到B中所有元素比较完为止。


算法描述:

[code]void SubList(SeqList *A ,SeqList B)
{
    int i,j,k;
    k=1;                                     //记住A表的下标位置
    for(i=1;i<=B.length ;i++)
    {
        for(j=k,j<=A->length ;j++)
        {
            if (B.data[i]>=A->data[j])       //因为表是有序的,不必比较到最后
            {
                k=j;
                break;                       //用k记住A表的当前位置,以便下一次比较从k开始
            }
            else
            {
                continue;
            }
            if(B.data[i]==A->data[k])        //相等表示有重复的
            {
                DeleteList(A,k);             //调用删除函数,删除A中第k个元素
            }
        }
    }
}

例19、已知head是指向一个带头结点的单链表的头指针,p指向该链表的任一结点。试写一算法,将p所指向的结点与其后继结点位置交换。

算法思想:如果p存在后续结点,看它是否为头结点,如果是,则交换后还要改变该链表的head;若不是头结点,则直接交换,如果p指向最后一个结点,说明其没有后续结点,则不交换。

算法描述:

[code]void ExchangeNode(LinkList head ,ListNode *p)            //交换p所指结点与其后续结点存储位置
{
    ListNode *q ,*r ,*s ;
    q = p->next;                    //q指向p的后续结点
    if(q!=NULL)                     //表示p不是指向最后一个结点
    {
        if(p==head)                 //若p指向头结点,则将该链表的前两个结点交换位置
        {
            head=head->next;
            s=head->next;
            head-next =p ;
            p->next =s;
        }
        else                        //若p指向第二个结点之后的结点
        {
            r=head ;               //查找p的前驱结点
            while(r->next! =p)
            {
                r=r->next;
            }
            r->next =q;             //交换p和q的位置
            p->next =q->next;
            q->next=p;
        }
    }
    
    else
    {
        printf("p不存在后续结点,无须进行交换");
    }
}

例20、已知两单链表A和B分别表示两个集合,其元素值递增有序。试写一算法,求A和B的交集C,要求C同样以元素值递增的单链表形式存储。

算法思想:两集合的交集是指两个单链表的元素值相同的结点的集合,为了操作方便,先让单链表C带头结点。


算法描述:

[code]void Bemixed(LinkList a , LinkLink b ,LinkList c)        //求A和B的交集C,分别用单链表表示集合
{
    ListNode *p,*q,*r,*s ;
    c=(ListNode *)malloc(sizeof(ListNode));                //生成c表头结点
    r=c;
    p=a;
    q=b;
    while(p!=NULL&&q!=NULL)
    {
        if(p->data<q->data)                      //因为表是有序的,A表元素值小,则后移一个位置
        {
            p= p->next;
        }
        else
        if(p->data>q->data)                      //同上理由,B表元素值小
        {
            q=q->next;
        }
        else                                    //找到一个值相同的结点,在c中生成一个新结点
        {
            s=(ListNode *)malloc(sizeof(ListNode));
            s->data =p->data;
            s->next =NULL;
            r->next =s;                         //把s结点链接到C表的尾部
            r=s;                                //r始终指向C表的最后一个结点
            p=p->next;                          //使p、q指向其后续结点
            q=q->next;
        }
        
    }
}

例21、设有一个带头结点的双向循环链表,head为链表的头指针。试写一算法,实现在值为x的结点之前插入一个值为y的结点。

算法思想:因为链表是双向循环链表,所以不需要用指针指向x之前的结点,但要先从开始结点用循环找到要插入的位置,也就是x结点的位置。

算法描述:

[code]void DinsertB(DlinkList head ,DataType x ,DataType y)            
//在带头结点的双循环链表的值为x的结点之前插入一个结点
{
    DlistNode *p ,*s ;
    s=(DlistNode *)malloc(sizeof(DlistNode));
    s->data =y;
    p=head->next;
    while(P!=head&&p->data!=x)
    {
        p=p->next;
        if(p==head)
        {
            printf("没有值为x的结点");
        }
        else
        {
            s->prior =p->prior;
            s->next=p;
            p->prioe=s;
            p->prior->next=s;
        }
    }
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: