双向链表的综合应用
实验二 双向链表的综合应用
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、 对应于以下几种指定情况,请将程序运行结果截图。
- 若键盘输入“1234554321”,则程序运行结果截图如下:

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

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

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


浙公网安备 33010602011771号