数据结构-王道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;
其余除了在插入和删除方面需要更新前驱结点外,跟单链表差别不大