链式表操作集(数据结构)

emmm,又遇到有趣的题目了

6-5 链式表操作集(20 分)

本题要求实现链式表的操作集。

函数接口定义:

Position Find( List L, ElementType X );
List Insert( List L, ElementType X, Position P );
List Delete( List L, Position P );

其中List结构定义如下:

typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;

各个操作函数的定义为:

Position Find( List L, ElementType X ):返回线性表中首次出现X的位置。若找不到则返回ERROR;

List Insert( List L, ElementType X, Position P ):将X插入在位置P指向的结点之前,返回链表的表头。如果参数P指向非法位置,则打印“Wrong Position for Insertion”,返回ERROR;

List Delete( List L, Position P ):将位置P的元素删除并返回链表的表头。若参数P指向非法位置,则打印“Wrong Position for Deletion”并返回ERROR。

裁判测试程序样例:

#include <stdio.h>
#include <stdlib.h>

#define ERROR NULL
typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;

Position Find( List L, ElementType X );
List Insert( List L, ElementType X, Position P );
List Delete( List L, Position P );

int main()
{
    List L;
    ElementType X;
    Position P, tmp;
    int N;

    L = NULL;
    scanf("%d", &N);
    while ( N-- ) {
        scanf("%d", &X);
        L = Insert(L, X, L);
        if ( L==ERROR ) printf("Wrong Answer\n");
    }
    scanf("%d", &N);
    while ( N-- ) {
        scanf("%d", &X);
        P = Find(L, X);
        if ( P == ERROR )
            printf("Finding Error: %d is not in.\n", X);
        else {
            L = Delete(L, P);
            printf("%d is found and deleted.\n", X);
            if ( L==ERROR )
                printf("Wrong Answer or Empty List.\n");
        }
    }
    L = Insert(L, X, NULL);
    if ( L==ERROR ) printf("Wrong Answer\n");
    else
        printf("%d is inserted as the last element.\n", X);
    P = (Position)malloc(sizeof(struct LNode));
    tmp = Insert(L, X, P);
    if ( tmp!=ERROR ) printf("Wrong Answer\n");
    tmp = Delete(L, P);
    if ( tmp!=ERROR ) printf("Wrong Answer\n");
    for ( P=L; P; P = P->Next ) printf("%d ", P->Data);
    return 0;
}

/* 你的代码将被嵌在这里 */

输入样例:

6
12 2 4 87 10 2
4
2 12 87 5

输出样例:

2 is found and deleted.
12 is found and deleted.
87 is found and deleted.
Finding Error: 5 is not in.
5 is inserted as the last element.
Wrong Position for Insertion
Wrong Position for Deletion
10 4 2 5

这一题算是在我之前写过的单链表的创建和简单使用的基础之上加深了,需要对链表的定义和使用非常熟悉才能很好地解决这道题,最起码的就是先了解带头结点和不带头结点之间的区别,可以看看下图:



言归正传,这题是不带头结点,直接建立指针 L = NULL;然后第一次连续插入插入X元素后(第一次连续插入其实就是在空链表的基础上创建单链表) 。第一次建立链表时:

scanf("%d", &N);
    while ( N-- ) 
{
        scanf("%d", &X);
        L = Insert(L, X, L);//每次插入,都将上一次插入的位置放回Insert()函数里面
        if ( L==ERROR ) 
printf("Wrong Answer\n");
    }

所以第一次进行连续插入时,List Insert( List L, ElementType X, Position P )里面得L和P都是指向同一个位置的,那么第一次插入就必须建立插入条件:

       List T = L;//
       if( L==P )//第一次进入的时候相当于建表,和后面的插入不一样。 
{
        T = (List)malloc(sizeof(struct LNode));
T->Data = X;
T->Next = P;
return T;

}

每次把T的位置返回主函数之后,为了能接着插入到T的后面,就会再次以形参变量放在 L = Insert(L, X, L);里面,如果能明白Insert()括号两个有 L 的意思就能接着看下面了,可能有人觉得直接用L指向下一个位置不可以吗?是可以,但是如果改变了L的起始位置,后面的函数就找不到开头了,这个很重要,一定要记得别随意改动头指针的位置,后面要用的,头结点的位置改了,你后面怎么重新遍历这个链表,因此我们必须习惯用一个临时创建的局部变量来指向L所指的位置,然后改变T的指向,让L保持位置不变。

代码的意思如下图:


等全部都插入完成后就进行下一步了,查找后并且将找到元素的删除掉,如何查找相信你能看懂下面查找的实现代码:

Position Find( List L, ElementType X )
{
List T = L;
if(!L)
return ERROR;
while(T)
{
if( T->Data==X )
return T;
T = T->Next;
}
return ERROR;

}

插入完后就是查找和删除,只要找到了,就直接删除掉,而主函数中,P = Find(L, X);而在Position Find();函数中返回的是要查找的元素对应的指针(就是地址)储存在了P中,然后把位置和链表一起放进Delete函数中,进行删除,代码如下:

List H = L;
while( L&&L->Next!=P )//P指向的不能使第一个,因为这里是删除第一个元素以后的。
    L = L->Next;
    if(!L)
    {
    printf( "Wrong Position for Deletion\n" );
    return ERROR;
    }
L->Next = P->Next;
free(P);

这个代码能删除第一个元素以后的元素,却无法直接删除第一个,我们还需要用另外一个代码来删除第一个元素,这就是为什么我在一开始提到有头结点和没有头结点的原因了,这两者功能实现上还是有区别的,那么删除第一个元素就如下面所示了:

if( L==P )

List T=L;
L = L->Next;
free(T);
return L;

}

但这个代码还有一点我想提,你可以直接用下面这个代码

if( L==P )

L = L->Next;
return L;

}

这种方式删掉第一个,那问题来了,你没有删除掉第一个,而是把它忽视了而已,尽管已经没有指针指向第一个元素,但你只是找不到它而已,他还是会在内存里面,所以好的算法就要节省空间,用一个指针指向要被删除的第一个元素,将它free(也就是删除的意思)掉。此外题目有一些坑,就是最后一个要被查找的元素不管存在与否都要用来插入到旧的链表中的,于是插入的时候我们就不符合第一次连续插入(L==P)的条件了,我们便需要重新开始遍历,直接插在链表的最后一个位置,实现的代码如下:

while( L&&L->Next!=P )  //在未找到NULL的前一个结点都要一直往后移

        L = L->Next;

if(!L)
{
printf( "Wrong Position for Insertion\n" );
return ERROR;

    L->Next = (List)malloc(sizeof(struct LNode));
    L->Next->Data = X;

    L->Next->Next = P;

那么到了这里,就是这个代码的全部内容了,具体的代码如下面所示:

#include <stdio.h>
#include <stdlib.h>

#define ERROR NULL
typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;

Position Find( List L, ElementType X );
List Insert( List L, ElementType X, Position P );
List Delete( List L, Position P );

int main()
{
    List L;
    ElementType X;
    Position P, tmp, g;
    int N;

    L = NULL;
    scanf("%d", &N);
    while ( N-- )
	{
        scanf("%d", &X);
        L = Insert(L, X, L);
        if ( L==ERROR )
		printf("Wrong Answer\n");
    }
    scanf("%d", &N);
    while ( N-- ) {
        scanf("%d", &X);
        P = Find(L, X);
        if ( P == ERROR )
            printf("Finding Error: %d is not in.\n", X);
        else {
            L = Delete(L, P);
            printf("%d is found and deleted.\n", X);
            if ( L==ERROR )
                printf("Wrong Answer or Empty List.\n");
        }
    }
    L = Insert(L, X, NULL);
    if ( L==ERROR ) printf("Wrong Answer\n");
    else
        printf("%d is inserted as the last element.\n", X);
    P = (Position)malloc(sizeof(struct LNode));
    tmp = Insert(L, X, P);
    if ( tmp!=ERROR ) printf( "Wrong Answer\n" );
    tmp = Delete(L, P);
    if ( tmp!=ERROR ) printf( "Wrong Answer\n" );
    for ( P=L; P; P = P->Next ) printf("%d ", P->Data);
    return 0;
}

/* 你的代码将被嵌在这里 */
Position Find( List L, ElementType X )
{
	List T = L;
	if(!L)
	return ERROR;
	while(T)
	{
		if( T->Data==X )
		return T;
		T = T->Next;
	}
	return ERROR;
}
List Insert( List L, ElementType X, Position P )
{
	List T = L;
    if( L==P )//第一次进入的时候相当于建表,和后面的插入不一样。 
	{
    T = (List)malloc(sizeof(struct LNode));	
	T->Data = X;
	T->Next = P;
	return T;
	}
	while( L&&L->Next!=P )  
	L = L->Next;
	
	if(!L)
	{
	printf( "Wrong Position for Insertion\n" );
	return ERROR;
	} 
    L->Next = (List)malloc(sizeof(struct LNode));
    L->Next->Data = X;
    L->Next->Next = P;
    
	return T;
}

List Delete( List L, Position P )
{
	if( L==P )
	{ 
	List T=L;
	L = L->Next;
	free(T);
	return L;
	}	
	List H = L;	
    while(L&&L->Next!=P)
        L = L->Next;
    if(!L)
    {
    printf( "Wrong Position for Deletion\n" );
    return ERROR;
    }
    
	L->Next = P->Next;
	free(P);
	
	return H;
}


当然,如果上面的代码实在不看懂什么,看看下面的行不行吧:

Position Find( List L, ElementType X )
{  
    while(L!=NULL)
	{  
        if(L->Data == X) 
        return L;  
        L = L->Next;  
    }  
    return ERROR;  
}  
List Insert( List L, ElementType X, Position P )
{   
    List head = L, T = L;//保存表头    
    if( P==L )
	{  
	List T = (List)malloc(sizeof(struct LNode));  
    T->Data = X;   
    T->Next = P;  
    return T;  
    }  
    while(L!=NULL)
	{//一般情况,查找时要找到所要插入元素的前一个才能插入所以找的是P==L->Next;  
        if(P == L->Next)
		{    
            L->Next = (List)malloc(sizeof(struct LNode));
            L->Next->Data = X;
            L->Next->Next = P;  
            return head;//返回表头  
        }  
        L = L->Next; 
    }  
    printf("Wrong Position for Insertion\n");//没有找到说明位置非法返回ERROR  
    return ERROR;  
}  
List Delete( List L, Position P ){  
    //当删除表头时:  
    if(L == P){  
        L = L->Next;  
        free(P);
        return L;  
    }  
    List head = L;//记录头  
    //一般情况下,也是要找所删元素的前一个元素  
    while(L!=NULL){  
        if(L->Next==P){  
            List temp = L->Next->Next;  
            L->Next = temp;//L->Next指向跳过L->NEXT的后面一串 
			free(P); 
            return head;  
        }  
        L = L->Next;  
    }   
    printf("Wrong Position for Deletion\n");  
    return ERROR;  
}  
思维方式开始转变一下,上面的两种实现相同功能的代码你也可以找不同。


编译器:DEV  C++

  • 18
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值