双向链表的综合应用

实验二 双向链表的综合应用

1、 程序代码如下:(将所有的代码都copy过来,并指出分别是哪个文件的代码)

LLAPP.H头文件源码如下:

/*--- llapp.h ----------------------------- Listing 2-4a -------
 *  Application-specific data for linked list in lldriver.c (2-5)
 *  Used in conjunction with llapp.c (Listing 2-4b).
 *-------------------------------------------------------------*/
#ifndef LLAPP_H
#define LLAPP_H 1

/*
 *  Our first list's nodes consist of a pointer to
 *  a word and a count of occurrences.
 */

struct NodeData1 {
    char *word;
    unsigned int u;
};

typedef struct NodeData1 * pND1;

extern void * CreateData1 ( void * );
extern int    DeleteData1 ( void * );
extern int    DuplicatedNode1 ( Link, Link );
extern int    NodeDataCmp1 ( void *, void * );


/*
 *  Our second list's nodes consist of a
 *  pointer to a word.
 */

struct NodeData2 {
    char *word;
};

typedef struct NodeData2 * pND2;

extern void * CreateData2 ( void * );
extern int    DeleteData2 ( void * );
extern int    DuplicatedNode2 ( Link, Link );
extern int    NodeDataCmp2 ( void *, void * );

#endif

LLGEN.H头文件源码如下:

/*--- llgen.h ----------------------------- Listing 2-2 ---------
 *  Declarations for generic doubly linked lists.
 *  Used in conjunction with llgen.c (Listing 2-3).
 *-------------------------------------------------------------*/
#ifndef LLGEN_H       /* make sure it's included only once */
#define LLGEN_H    1

//定义双向链表的结构体
struct Node {
    struct Node     *prev;  /* link to previous node */
    struct Node     *next;  /* link to next node */
    void            *pdata; /* generic pointer to data */
};
//为结构体的指针取别名
typedef struct Node *Link;

/* a linked list data structure */
struct List {
    Link            LHead;
    Link            LTail;
    unsigned int    LCount;
    void * ( * LCreateData )     ( void * );
    int    ( * LDeleteData )     ( void * );
    int    ( * LDuplicatedNode ) ( Link, Link );
    int    ( * LNodeDataCmp )    ( void *, void * );
};

/* The four functions specific to an individual linked list are:

   LCreateData:  is passed a pointer to an application-defined
                 object and is expected to return a pointer to
                 whatever is to be stored in the linked list.

   LDeleteData:  is passed a pointer to the object an application
                 has stored in a linked list. LDeleteData must
                 destroy the object.

   LDuplicatedNode: is passed two pointers. The first pointer is
                    to a node that you would like to add to a
                    linked list and the second is to a node that
                    is already in the list but is a duplicate of
                    the first pointer.
                    LDuplicatedNode returns:
                            0 -> do nothing to list
                            1 -> destroy duplicate
                            2 -> add duplicate to list

   LNodeDataCmp: is passed pointers to two application data
                 objects and must compare them, returning a
                 number that is < 0, zero, or > 0, depending on
                 the relationship between the first and second
                 objects.
*/

/*--- generic linked-list primitives ---*/
//函数声明
int  AddNodeAscend  ( struct List *, void * );
int  AddNodeAtHead  ( struct List *, void * );
struct List * CreateLList (
                void * ( * ) ( void * ),       /* create data */
                int    ( * ) ( void * ),       /* delete data */
                int    ( * ) ( Link, Link ),   /* duplicate   */
                int    ( * )  ( void *, void * )); /* compare */
Link CreateNode     ( struct List * , void * );
int  DeleteNode     ( struct List *, Link );
Link FindNode       ( struct List *, void * );
Link FindNodeAscend ( struct List *, void * );
Link GotoNext       ( struct List *, Link );
Link GotoPrev       ( struct List *, Link );
#endif

LLAPP.C文件源码如下:

/*--- llapp.c ----------------------------- Listing 2-4b --------
 *  Application-specific functions for linked-list examples.
 *  Replace these routines with your own.
 *-------------------------------------------------------------*/

#include <stdlib.h>         /* for free() */
#include <string.h>         /* for strcmp() and strdup() */

#include "llgen.h"
#include "llapp.h"
#pragma warning(disable : 4996)
/* data is a pointer to a string */
void * CreateData1 ( void * data )
{
    struct NodeData1 * new_data;

    /*--- 从堆中申请结点所需空间 ---*/
    if ((new_data = malloc ( sizeof ( struct NodeData1 ))) == NULL)
        return ( NULL );

    /*--- 将字符串数据拷贝到结点所占的空间 ---*/
    new_data->u    =  1;
    new_data->word =  strdup ( (char *) data );

    if ( new_data->word == NULL )   /* error copying string */
    {
        free ( new_data );
        return ( NULL );
    }
    else
        return ( new_data ); /* return a complete structure */
}

int DeleteData1 ( void * data )
{
    /*
     * 将结点中的字符串数据所占空间释放
     */
     free ( ((pND1) data)->word );
     return ( 1 );
}
/*---------------------------------------------------------------
 * This function determines what to do when inserting a node
 * into a list if an existing node with the same data is found
 * in the list. In this case, since we are counting words, if a
 * duplicate word is found, we simply increment the counter.
 *
 * Note this function should return one of the following values:
 *      0       an error occurred
 *      1       delete the duplicate node
 *      2       insert the duplicate node
 * Any other processing on the duplicate should be done in this
 * function.
 *-------------------------------------------------------------*/

int DuplicatedNode1 ( Link new_node, Link list_node )
{           
    /* 将结点中表示该单词出现次数的属性加1 */
    pND1 pnd = list_node->pdata;
    pnd->u += 1;
    return ( 1 );
}

int NodeDataCmp1 ( void *first, void *second )
{
    /* 比较两个结点中字符串的相对大小 */
    return ( strcmp ( ((pND1) first)->word,
                      ((pND1) second)->word ));
}

/*=== Now the functions for the second linked list ===*/

void * CreateData2 ( void * data )
{
    struct NodeData2 * new_data;

    /*--- allocate the data structure ---*/
    if ((new_data = malloc ( sizeof ( struct NodeData2 ))) == NULL)
        return ( NULL );

    /*--- 将要存入的字符串拷贝到结点的相应属性中 ---*/
    new_data->word =  strdup ( (char *) data );

    if ( new_data->word == NULL )   /* error copying string */
    {
        free ( new_data );
        return ( NULL );
    }
    else
        return ( new_data );
}

int DeleteData2 ( void * data )
{
    /*
     * In this case, NodeData2 consists of a pointer.
     * The string must be freed manually.
     */

     free ( ((pND2) data)->word );
     return ( 1 );
}

/* this list inserts duplicated nodes */
int DuplicatedNode2 ( Link new_node, Link list_node )
{
    return ( 2 );
}

int NodeDataCmp2 ( void *first, void *second )
{
    //返回两个结点中的字符串数据比较结果
    return ( strcmp ( ((pND2) first)->word,
                      ((pND2) second)->word ));
}

LLGEN.C文件源码如下:

/*--- llgen.c ------------------------------ Listing 2-3 --------
 *  Generic primitive functions for doubly linked lists.
 *  Contains no application-specific functions.
 *  Functions are in alphabetical order.
 *------------------------------------------------------------*/

#include <stdlib.h>
#include <string.h>

#define IN_LL_LIB   1   /* in the library of primitives */

#include "llgen.h"

/*--- Aliases to make the code more readable ---*/

#define LLHead (L->LHead)      /* The head of the current list */
#define LLTail (L->LTail)      /* The tail of the current list */
#define NodeCount (L->LCount)  /* Nodes in the current list */

#define CreateData     (*(L->LCreateData))
#define DeleteData     (*(L->LDeleteData))
#define DuplicatedNode (*(L->LDuplicatedNode))
#define NodeDataCmp    (*(L->LNodeDataCmp))

/*----------------------------------------------------
 * 应用头插法向双向链表插入结点
 *--------------------------------------------------*/
int AddNodeAtHead ( struct List *L, void *nd )
{
    Link pn;

    pn = CreateNode ( L, nd );
    if ( pn == NULL )
        return ( 0 );

    /*--- 向链表中添加结点 ---*/
    if ( LLHead == NULL )   /* 当链表为空时,让头指针和尾指针都指向该结点 */
    {
        LLHead = LLTail = pn; /*--- yes ---*/
    }
    else                      /*--- no  ---*/
    {
        LLHead->prev = pn; /* 将该结点作为双向列表新的头结点 */
        pn->next = LLHead; /* put Head next */
        LLHead = pn;       /* then point Head to us */
    }
    //将结点数量加1
    NodeCount += 1;
    return ( 1 );
}

/*----------------------------------------------------
 * 向链表元素升序的双向列表中添加结点
 *--------------------------------------------------*/
int AddNodeAscend ( struct List *L, void *nd )
{
    Link        pn;         /* to node we're creating */
    Link        prev, curr; /* our current search */
    struct Node dummy;      /* a dummy node */
    int         compare;

    pn = CreateNode ( L, nd );
    if ( pn == NULL )
        return ( 0 );

    /* 将dummy作为链表的头结点以统一对链表的操作 */
    dummy.next = LLHead;
    dummy.prev = NULL;
    if ( dummy.next != NULL )
        dummy.next->prev = &dummy;

    prev = &dummy;
    curr = dummy.next;
    //从头结点向后遍历双向链表
    for ( ; curr != NULL; prev = curr, curr = curr->next )
    {
        //将要插入的结点中的数据与结点中的原有数据进行比较
        compare = NodeDataCmp ( pn->pdata, curr->pdata );
        if ( compare <= 0 )
            break; /* new node equals or precedes curr */
    }
    //找到了插入的位置,且不是成为最后一个元素
    if ( curr != NULL && compare == 0 )
    {
        compare = DuplicatedNode ( pn, curr );
        if ( compare == 2 )
            /* do nothing -- will get inserted */;
        else
        {
            /* 删除新加入的头结点 */
            LLHead = dummy.next;
            LLHead->prev = NULL;

            /* 删除重复的结点元素 */
            if ( compare == 1 )
            {
                DeleteData( pn->pdata );
                free ( pn );
            }
            return ( 1 );
        }
    }
    //将结点加入链表
    prev->next = pn;
    pn->prev = prev;
    pn->next = curr;
    if ( curr != NULL )
        curr->prev = pn;
    else
        LLTail = pn; /* this node is the new tail */
    //将结点数量加1
    NodeCount += 1;

    /* now, unhook the dummy head node */
    LLHead = dummy.next;
    LLHead->prev = NULL;
    return ( 1 );
}

/*---------------------------------------------------------------
 * 初始化链表,若初始化失败则返回NULL,否则返回链表指针
 *-------------------------------------------------------------*/
struct List * CreateLList (
                void * ( * fCreateData ) ( void * ),
                int    ( * fDeleteData ) ( void * ),
                int    ( * fDuplicatedNode ) ( Link, Link ),
                int    ( * fNodeDataCmp )  ( void *, void * ))
{
    //完成空链表的初始化操作
    struct List * pL;

    pL = (struct List *) malloc ( sizeof ( struct List ));
    if ( pL == NULL )
        return NULL;

    pL->LHead = NULL;
    pL->LTail = NULL;
    pL->LCount = 0;
    //将函数指针赋值给结构体中的属性
    pL->LCreateData = fCreateData;
    pL->LDeleteData = fDeleteData;
    pL->LDuplicatedNode = fDuplicatedNode;
    pL->LNodeDataCmp = fNodeDataCmp;

    return ( pL );
}

/*---------------------------------------------------------------
 * 创建一个新的结点并完成结点中指针元素的初始化工作
 *-------------------------------------------------------------*/
Link CreateNode ( struct List *L, void *data )
{
    Link new_node;

    new_node = (Link) malloc ( sizeof ( struct Node ));
    if ( new_node == NULL )
        return ( NULL );

    new_node->prev = NULL;
    new_node->next = NULL;

    /*--- now call the application-specific data allocation ---*/
    new_node->pdata = CreateData( data );
    if ( new_node->pdata == NULL )
    {
        free ( new_node );
        return ( NULL );
    }
    else
        return ( new_node );
}

/*---------------------------------------------------------------
 *  删除双向链表中的特定结点
 *-------------------------------------------------------------*/
int DeleteNode ( struct List *L, Link to_delete )
{
    Link pn;

    if ( to_delete == NULL )        /* Double check before */
        return ( 0 );               /* deleting anything.  */
    //需要删除的结点是头结点
    if ( to_delete->prev == NULL )  /* we're at the head */
    {
        LLHead = to_delete->next;   /* update head */
        LLHead->prev = NULL;        /* update next node */
    }
    //需要删除的结点是最后一个结点
    else if ( to_delete->next == NULL )
    {                               /* we're at the tail */
        pn = to_delete->prev;       /* get the previous node */
        pn->next = NULL;
        LLTail = pn;                /* update tail */
    }

    else                        /* we're in the list */
    {
        //需要删除的结点位于链表中间位置
        pn = to_delete->prev;       /* get the previous node */
        pn->next = to_delete->next; /* update previous node to */
                                    /* point to the next one. */
        pn = to_delete->next;       /* get the next node */
        pn->prev = to_delete->prev; /* update it to point to */
                                    /* the previous one. */
    }

    DeleteData ( to_delete->pdata );  /* delete the data */
    free ( to_delete );               /* free the node */
    //将结点数量减1
    NodeCount -= 1;

    return ( 1 );
}

/*---------------------------------------------------------------
 *  根据结点内容查找链表中具有相同内容的结点位置
 *-------------------------------------------------------------*/
Link FindNode ( struct List *L, void *nd )
{
    Link pcurr;             /* the node we're examining */

    if ( LLHead == NULL )        /* empty list */
        return ( NULL );
    //从头开始对链表按数据内容进行查找
    for ( pcurr = LLHead; pcurr != NULL; pcurr = pcurr->next)
    {
        if ( NodeDataCmp ( nd, pcurr->pdata ) == 0 )
            return ( pcurr );
    }
    return ( NULL );             /* could not find node */
}

Link FindNodeAscend ( struct List *L, void *nd )
{
    Link    pcurr;          /* the node we're examining */
    int cmp_result;

    if ( LLHead == NULL )        /* empty list */
        return ( NULL );
    //在链表元素为升序排列的链表中查找元素
    for ( pcurr = LLHead; pcurr != NULL; pcurr = pcurr->next)
    {
        cmp_result =  NodeDataCmp ( nd, pcurr->pdata );

        if ( cmp_result < 0 )
             return ( NULL );    /* too far */

        if ( cmp_result == 0 )   /* just right */
             return ( pcurr );
    }

    return ( NULL );             /* could not find node */
}

/*---------------------------------------------------------------
 *  返回当前元素的下一个元素的指针
 *-------------------------------------------------------------*/

Link GotoNext ( struct List *L, Link pcurr )
{
    if ( pcurr->next == NULL || pcurr == LLTail )
        return ( NULL );
    else
        return ( pcurr->next );
}
/*---------------------------------------------------------------
 *  返回指向当前元素前驱的指针
 *-------------------------------------------------------------*/
Link GotoPrev ( struct List *L, Link pcurr )
{
    if ( pcurr->prev == NULL || pcurr == LLHead )
        return ( NULL );
    else
        return ( pcurr->prev );
}

LLDRIVER.C文件源码如下:

/*--- lldriver.c -------------------------- Listing 2-5 -------
 *  Reads in text words from the file specified on the command
 *  line and places them into two linked lists. Then exercises
 *  a variety of linked-list activities, printing the results
 *  at every step.
 *  Must be linked to linked-list primitives in Listings 2-2
 *  through 2-4b.
 *-------------------------------------------------------------*/

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

#include "llgen.h"          /* Header for generic linked lists */
#include "llapp.h"          /* Header for appl.'s linked lists */
#pragma warning(disable : 4996)
 //Ctrl + K Ctrl + D
int judgeCycleStr(char word[])
{
	Link    w1, w2;      /* cursors used to walk lists */
	struct  List* L2;
	//获取输入的字符串长度
	int word_len = strlen(word);
	//初始化链表
	L2 = CreateLList(CreateData2,      /* in llapp.c */
		DeleteData2,      /*     "      */
		DuplicatedNode2,  /*     "      */
		NodeDataCmp2);   /*     "      */

	if (L2 == NULL)
	{
		fprintf(stderr, "Error creating linked list\n");
		exit(EXIT_FAILURE);
	}
	//判断输入的字符串是否由数字和字母构成
	for (int i = 0; i < word_len; i++)
	{
		if ((word[i] >= '0' && word[i] <= '9') || (word[i] >= 'A' && word[i] <= 'Z') || (word[i] >= 'a' && word[i] <= 'z'))
		{
			continue;
		}
		else
		{
			printf("输入的字符中存在既不是字母也不是数字的字符!!!\n");
			return -1;
		}
	}
	if (strlen(word) > 0)
	{
		word[strlen(word)] = 0;
	}

	/*--- 读取文件中的一行数据并存入链表 ---*/
	for (int i = 0; i < word_len; i++)
	{
		char item[2] = { 0 };
		item[0] = word[i];
		/* 将结点按照头插法插入链表 */
		if (!AddNodeAtHead(L2, item))
		{
			fprintf(stderr, "Warning! Error while adding node to L2.\n");
		}
	}
	/* 遍历链表 */
	printf("L2 contains %u items:\n", L2->LCount);
	for (w1 = L2->LHead; w1 != NULL; w1 = w1->next)
	{
		printf("  %s\n", ((pND2)(w1->pdata))->word);
	}
	/* 分别从两端开始遍历链表 */

	printf("L2 contains %u items:\n", L2->LCount);
	w1 = L2->LHead;
	w2 = L2->LTail;
	//对双向链表进行遍历,判断键盘输入的字符串是否首尾对称
	for (int i = 0, j = L2->LCount - 1; i < j; w1 = w1->next, w2 = w2->prev, i++, j--)
	{
		//对节点中存储的内容进行比较,若发现有两个结点的数据不一致则返回0
		if (strcmp(((pND2)(w1->pdata))->word, ((pND2)(w2->pdata))->word) != 0)
		{
			return 0;
		}
	}
	return 1;
}

int main(int argc, char* argv[])
{
	char    word[64];       /* the raw word from the file */
	int     result;
	while (scanf_s("%s", word, 64)) 
	{
		result = judgeCycleStr(word);
		printf("result:%d\n", result);
	}
	return 0;

}

2、 对应于以下几种指定情况,请将程序运行结果截图。

  1. 若键盘输入“1234554321”,则程序运行结果截图如下:

image-20201130210126722

  1. 若键盘输入“321321”,则程序运行结果截图如下:

image-20201130210215199

  1. 若键盘输入为“ab1ab”,则程序运行结果截图如下:

image-20201130210306320

  1. 若键盘输入为“ab1ba”,则程序运行结果截图如下:

image-20201130210346675

posted @ 2020-12-09 00:41  Coulson123  阅读(180)  评论(0)    收藏  举报