例解单链表的基本运算(上)

    本文所有代码采用C语言实现。

    参考文献: 《数据结构(C语言版)》  严蔚敏 吴伟民 编著 

    开发平台:Ubuntu 11.04

    编译器:gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)

 

    线性结构:在数据元素的非空有限集中,有且仅有一个开始结点(没有直接前趋)和一个终止结点(没有直接后继),除开始结点和终止结点之外,所有其他结点有且仅有一个直接前趋结点和一个直接后继结点。

    线性表是一种典型的线性结构。

    链式存储结构:逻辑上相邻的两个数据元素其存储的内存地址不要求紧邻。

    1、单链表的定义

    链式存储的线性表就叫做链表。

    由于链表并不是使用一段连续的内存来依次存储其所有结点的,所以每个结点都必须使用一个指针域(用来指向其直接后继结点)来构建数据元素之间的逻辑关系。

    而每个结点只包含一个指针域的链表就叫做单链表。

    单链表的存取必须从头指针开始进行,头指针指向开始结点的存储地址,同时,终止结点的指针域必须为NULL,如下图所示:

 

    有时候,我们会在单链表的开始结点之前附加一个头结点,头结点的指针域指向开始结点,它的数据域可以不存储任何信息,也可以存储一些其他的附加信息。

    2、单链表的运算

    例子公用代码: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NAMELEN 20

typedef struct student {
    char name[NAMELEN];
    unsigned long num;
    char sex; //M = male, F = female
    unsigned short age;
    unsigned short marks;
}data_type;

typedef struct list_node {
    data_type data;
    struct list_node *next;
}list_node_t, *list_node_p;

static void init_data(list_node_t *tmp)
{
    char *line = NULL;
    size_t len = 0;
    
    printf("Please enter the student's information from name, num, "
	    "sex, age to marks.\ne.g.: Richard Tang 201201034 F 19 93\n");

    if (getline(&line, &len, stdin) < 0)
	perror("getline");
   
    /* %[a-z,A-Z, ], a nonstandard extension of GNU libc */
    if (sscanf(line, "%[a-z,A-Z, ] %lu %c %hu %hu", tmp->data.name, &tmp->data.num, 
		&tmp->data.sex, &tmp->data.age, &tmp->data.marks) != 5)
	printf("invalid data, the number of input items "
		"is not equal to five.\n");
    
    free(line);
}

    (1)、创建

    2.1.1 头插法创建单链表

    每一个刚创建的新结点都插入到当前链表的头指针后,如下图所示:

 

例解单链表的基本运算(上)_第1张图片

    例子代码可用来创建学生基本信息的单链表,当输入quit后则停止继续创建。 

static list_node_t *create_list_at_head(void)
{
    char cond[5];
    list_node_p tmp, head = NULL;

    printf("Press Enter to begin to create a linked list.");
    
    while (strncmp(fgets(cond, 5, stdin), "quit", 4)) {
	/* Remove the extra characters for fgets() */
	stdin->_IO_read_ptr = stdin->_IO_read_end;

	tmp = (list_node_t *)malloc(sizeof(list_node_t));
	if (!tmp)
	    perror("allocate dynamic memory");

	init_data(tmp);
	
	tmp -> next = head;
	head = tmp;
	
	printf("Have created a node.\nPress Enter to continue "
	    "(or type \"quit\" to quit): ");
    }
    
    return head;
}

    2.1.2 尾插法创建单链表

    从头指针开始,每个新创建的结点都插入到当前链表的表尾,如下图所示:

 

    参考代码如下: 

static list_node_t *create_list_at_tail(void)
{
    char cond[5];
    list_node_p head = NULL;
    list_node_t *tmp, *work = NULL;

    printf("Press Enter to begin to create a linked list.");
    
    while (strncmp(fgets(cond, 5, stdin), "quit", 4)) {
	/* Remove the extra characters for fgets() */
	stdin->_IO_read_ptr = stdin->_IO_read_end;
	
	tmp = (list_node_t *)malloc(sizeof(list_node_t));
	if (!tmp)
	    perror("allocate dynamic memory");

	init_data(tmp);
	tmp -> next = NULL;

	if (head == NULL)
	    head = tmp;
	else
	    work -> next = tmp;
	
	work = tmp;
    
	printf("Have created a node.\nPress Enter to continue "
	    "(or type \"quit\" to quit): ");
    }
    
    return head;
}

    2.1.3 使用尾插法创建带有头结点的单链表

    使用头结点使得对开始结点和其他结点的操作并无二致,因此代码也就更直观和简单了,如下图所示:

 

例解单链表的基本运算(上)_第2张图片

    参考代码如下: 

static list_node_t *create_list_at_tail_with_headnode(void)
{
    char cond[5];
    list_node_p head = (list_node_t *)malloc(sizeof(list_node_t));
    head -> next = NULL;
    list_node_t *tmp, *work = head;

    printf("Press Enter to begin to create a linked list.");
    
    while (strncmp(fgets(cond, 5, stdin), "quit", 4)) {
	/* Remove the extra characters for fgets() */
	stdin->_IO_read_ptr = stdin->_IO_read_end;
	
	tmp = (list_node_t *)malloc(sizeof(list_node_t));
	if (!tmp)
	    perror("allocate dynamic memory");

	init_data(tmp);
	tmp -> next = NULL;
	
	work -> next = tmp;
	work = tmp;
    
	printf("Have created a node.\nPress Enter to continue "
	    "(or type \"quit\" to quit): ");
    }
    
    return head;
}

    (2)、查找

    2.2.1 按序号查找

    单链表的按序号查找只能从链表的头指针出发,顺着指针域逐个结点地往下搜索,直到找到想要的结点为止。 

    参考代码如下: 

static list_node_t *get_node_with_headnode(list_node_p head, int i)
{
    list_node_p p = head;
    int j = 0;

    while (p -> next && j < i) {
	p = p -> next;
	j++;
    }

    if (i == j)
	return p;
    else 
	return NULL;
}

    2.2.2 按值查找

    例子中以学生的学号为键值进行查找,这样做的好处是能保证键值的唯一性(可在创建链表的代码中增加对学号输入重复的纠错能力)。 

    参考代码如下: 

static list_node_t *locate_node_with_headnode(list_node_p head, unsigned long key)
{
    list_node_p p = head -> next;

    while (p && p->data.num != key)
	p = p -> next;

    return p;
}

你可能感兴趣的:(list,struct,ubuntu,null,存储,extension)