数据结构-王道2017-第2章 线性表-2.3 线性表的链式表示

1.线性表的链式存储又称为单链表,它是通过一组任意的存储单元来存储线性表中的数据元素,是非随机存取的存储结构,通常用“头指针”来标识一个单链表,如单链表L,头指针为“NULL”时则表示一个空表,此外,为了操作上的方便,在单链表第一个节点之前附加一个节点,称为头节点。头节点的数据域可以不设任何信息,也可以记录表长等相关信息。头指针的指针域指向线性表的第一个元素节点。

  头指针和头节点的区别:不管带不带头节点,头指针始终指向链表的第一个节点。而头节点是带头节点链表的第一个节点,结点内通常不存储信息。

 使用头节点的优点:

   1)开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理

   2)无论链表是否为空,其头指针是指向头节点的非空指针,空表中头节点的指针域为空,因此空表和非空表的处理也就统一了。

typedef struct LNode{
    ElemType data;        //数据域
    struct LNode *next;    //指针域 
}LNode, *LinkList;   

2. 单链表上基本操作的实现

   

//1.采用头插法建立单链表
LinkList creatList(LinkList &L) {
    LNode *s; int x;
    L = (LinkList)malloc(sizeof(LNode));  //创建头结点
    L->next = NULL;     //初始为空链表
    scanf("%d", &x);   //输入结点的值
    while (x != 9999) {   //输入9999表示结束
        s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;  //把头节点后面的链表加在新插入节点的后面
        L->next = s;        //修改头节点的指针到s
        scanf("%d", &x);
    }
    return L;
}

//打印链表
void printLinkList(const LinkList &L){
    LNode * s = L;
    while (s->next != NULL) {
        printf("%d ", s->next->data);
        s = s->next;
    }
}
//2.采用尾插法建立单链表
//必须增加一个尾部指针,使它始终指向当前链表的尾结点
LinkList creatList2(LinkList &L) {
    
    L = (LinkList)malloc(sizeof(LNode));
    LNode *r = L, *s;   //r为指向尾部结点的指针
    int x;
    scanf("%d", &x);
    while (x != 9999) {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        scanf("%d", &x);
    }
    r->next = NULL;    //如果不在这一步把指针置为NULL,那么指针的值是未知的而且不会是NULL,在遍历打印链表时(s->next != NULL)条件会满足,会继续访问未知内存的数据,从而使程序崩溃
    return L;

}
//3.按序号查找节点值
LNode* getElem(LinkList L, int i) {

    if (i == 0)  return L;
    if (i < 0) return NULL;  //值无效

    int j = 1;             //第一个结点
    LNode* p = L->next;   //p为第一个结点指针
    while (p&&j < i) {
        p = p->next;
        j++;
    }
    return p;   //当j==i时返回结点,如果i > 总长度,则返回NULL
}
//4.按值查找结点值
LNode* locateElem(const LinkList L, ElemType x) {
    LNode* p = L->next;
    while (p != NULL && p->data != x)
        p = p->next;
    return p;      //找到则返回指向该节点的指针,没有找到则返回NULL
 }
//5.插入结点操作(在结点i后插)
LNode* insertNode(LinkList& L, ElemType e,int i) {

    LNode* p = getElem(L, i);
    if (p == NULL)
        return NULL;

    LNode* s = (LNode*)malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;
    return s;
}
//6.删除结点操作
bool deleteElem(LinkList& L, int i) {
    LNode* p = getElem(L, i - 1);
    if (p == NULL)  return false;

    LNode* q = p->next;
    if (q != NULL) {       //q不是最后一个结点
        p->next = q->next;
        free(q);  //释放所占的内存空间
        return true;
    }        
    
    return false;   //节点不存在,删除失败
}
//如果是删除某一个节点*p,可以采用拷贝p的后继节点来给自身,然后删除p的后继节点来实现

 3.双链表

typedef struct LNode {
    ElemType data;        //数据域
    struct LNode  *pre, *next;   //前驱和后继指针  
}LNode, *LinkList;

其余除了在插入和删除方面需要更新前驱结点外,跟单链表差别不大

 

posted @ 2017-03-12 15:03  陈煜弘  阅读(320)  评论(0编辑  收藏  举报