1、链表一般分为:
(1)单向链表
(2)双向链表
(3)环形链表
2、链表概念:
(1)链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,
(2)且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作。
(3)链表的每个元素称为一个节点,每个节点都可以存储在内存中的不同的位置,为了表示每个元素与后继元素的逻辑关系,以便构成“一个节点链着一个节点”的链式存储结构,
(4)除了存储元素本身的信息外,还要存储其直接后继信息,因此,每个节点都包含两个部分,第一部分称为链表的数据区域,用于存储元素本身的数据信息,这里用data表示,
(5)它不局限于一个成员数据,也可是多个成员数据,第二部分是一个结构体指针,称为链表的指针域,用于存储其直接后继的节点信息,这里用next表示,next的值实际上就是下一个节点的地址,当前节点为末节点时,next的值设为空指针
struct link { int data; struct link *next; };
像上面这种只包含一个指针域、由n个节点链接形成的链表,就称为线型链表或者单向链表,链表只能顺序访问,不能随机访问,链表这种存储方式最大缺点就是容易出现断链
3、链表与数组比较
数组(包括结构体数组)的实质是一种线性表的顺序表示方式,它的优点是使用直观,便于快速、随机地存取线性表中的任一元素,但缺点是对其进行 插入和删除操作时需要移动大量的数组元素,同时由于数组属于静态内存分配,定义数组时必须指定数组的长度,程序一旦运行,其长度就不能再改变,实际使用个数不能超过数组元素最大长度的限制,否则就会发生下标越界的错误,低于最大长度时又会造成系统资源的浪费,因此空间效率差
4、单向链表的建立、插入、删除、打印
#include "stdafx.h" #include <stdio.h> #include <stdlib.h> struct link *AppendNode(struct link *head); void DisplyNode(struct link *head); void DeletMemory(struct link *head); struct link *DeleteNode(struct link *head, int nodeData); struct link *InsertNode(struct link *head, int nodeData); struct link { int data; struct link *next; }; int main(void) { int i = 0; char c; struct link *head = NULL; //链表头指针 printf("Do you want to append a new node(Y/N)?"); scanf_s(" %c", &c,1); int nDelegeData; while (c == 'Y' || c == 'y'|| c =='N' ||c == 'n') { if (c == 'Y' || c == 'y') { head = AppendNode(head);//向head为头指针的链表末尾添加节点 } else { printf("Input node data "); scanf_s(" %d", &nDelegeData); head = DeleteNode(head, nDelegeData);//删除一个节点, } DisplyNode(head); //显示当前链表中的各节点的信息 printf("Do your want to append a new node or delete a node (Y/N)"); scanf_s(" %c", &c, 1); i++; } printf("%d new nodes have been apended", i); DeletMemory(head); //释放所有动态分配的内存 system("pause"); return 0; } /* 函数功能:新建一个节点并添加到链表末尾,返回添加节点后的链表的头指针 */ struct link *AppendNode(struct link *head) { struct link *p = NULL, *pr = head; int data; p = (struct link *)malloc(sizeof(struct link));//让p指向新建的节点 if (p == NULL) //若新建节点申请内存失败,则退出程序 { printf("No enough memory to allocate "); exit(0); } if (head == NULL) //若原链表为空表 { head = p; //将新建节点置为头节点 } else //若原链表为非空,则将新建节点添加到表尾 { while (pr->next != NULL)//若未到表尾,则移动pr直到pr指向表尾 { pr = pr->next; //让pr指向下一个节点 } pr->next = p; //让末节点的指针指向新建的节点 } printf("Input node data "); scanf_s("%d", &data); //输入节点数据 p->data = data; //将新建节点的数据域赋值为输入的节点数据值 p->next = NULL; //将新建的节点置为表尾 return head; //返回添加节点后的链表的头指针 } /* 函数的功能:显示链表中所有节点的节点号和该节点中的数据项的内容*/ void DisplyNode(struct link *head) { struct link *p = head; int j = 1; while (p != NULL) //若不是表尾,则循环打印节点的数值 { printf("%5d%10d ", j, p->data);//打印第j个节点数据 p = p->next; //让p指向下一个节点 j++; } } //函数的功能:释放head所指向的链表中所有节点占用的内存 void DeletMemory(struct link *head) { struct link *p = head, *pr = NULL; while (p != NULL) //若不是表尾,则释放节点占用的内存 { pr = p; //在pr中保存当前节点的指针 p = p->next;//让p指向下一个节点 free(pr); //释放pr指向的当前节点占用的内存 } } //根据节点的数据删除某个节点 struct link *DeleteNode(struct link *head, int nodeData) { struct link *p = head, *pr = head; if (head == NULL) { printf("Linked table is empty! "); return 0; } while (nodeData != p->data && p->next != NULL) { pr = p; /* pr保存当前节点 */ p = p->next; /* p指向当前节点的下一节点 */ } if (nodeData == p->data) { if (p == head) /* 如果待删除为头节点 (注意头指针和头结点的区别)*/ { head = p->next; } else /* 如果待删除不是头节点 */ { pr->next = p->next; } free(p); /* 释放已删除节点的内存 */ } else /* 未发现节点值为nodeData的节点 */ { printf("This Node has not been found"); } return head; } //插入节点 /* 函数功能:向单向链表中插入数据 按升序排列*/ struct link *InsertNode(struct link *head, int nodeData) { struct link *p = head, *pr = head, *temp = NULL; p = (struct link *)malloc(sizeof(struct link)); if (p == NULL) { printf("No enough meomory! "); exit(0); } p->next = NULL; /* 待插入节点指针域赋值为空指针 */ p->data = nodeData; if (head == NULL) /* 若原链表为空 */ { head = p; /* 插入节点作头结点 */ } else /* 原链表不为空 */ { while (pr->data < nodeData && pr->next != NULL) { temp = pr; /* 保存当前节点的指针 */ pr = pr->next; /* pr指向当前节点的下一节点 */ } if (pr->data >= nodeData) { if (pr == head) /* 在头节点前插入新节点 */ { p->next = head; /* 新节点指针域指向原链表头结点 */ head = p; /* 头指针指向新节点 */ } else { pr = temp; p->next = pr->next; /* 新节点指针域指向下一节点 */ pr->next = p; /* 让前一节点指针域指向新节点 */ } } else /* 若在表尾插入新节点 */ { pr->next = p; /* 末节点指针域指向新节点*/ } } return head; }