经典单链表(动态链表)具体实现方案:给每一个元素配置一个指针,每个指针都指向相邻的下一个元素。(“链”字的由来)
| 一个结点(节点)的构成: | 数据域 | 指针域(Next 域) |
| 头指针: | 类型跟指针一样,但是它的特点是永远指向链表中的第一个结点 | |
| 结点们 | 头结点: | 有时为了方便操作,特意在链表开头留一个空结点(代表它数据域不正常利用) |
| 首元结点: | 特指链表开头第一个存有了数据的结点 | |
| 其它结点: | 链表中其它的结点 | |
- #include
- #include
-
- #define ElemType char
- //typedef char ElemType
-
- /*结点结构体*/
- typedef struct Node{
- ElemType data; //数据域
- struct Node* next; //指针域
- }Node, *PNode;
- /*单链表结构体*/
- typedef struct LinkList {
- PNode first; //指向头结点
- PNode last; //指向尾结点
- size_t size; //记录有效结点数
- }LinkList;
- /*初始化*/
- void init(LinkList* ll)
- {
- ll->first = ll->last = (Node*)malloc(sizeof(Node)); //为ll的头、尾结点申请内存空间
- if (ll->first == NULL)
- return;
- ll->first->next = NULL; //初始化头结点的指针域
- ll->size = 0; //初始化ll的结点数
- }
- /*判空*/
- int empty(LinkList* ll)
- {
- if (ll->size == 0)
- return 1;
- else
- return 0;
- }
- /*长度*/
- size_t size(LinkList* ll)
- {
- return ll->size;
- }
- /*尾插*/
- void push_back(LinkList* ll, ElemType value)
- {
- PNode p = (Node*)malloc(sizeof(Node)); //申请内存空间
- if (p == NULL)
- return;
- p->data = value; //设置值
- p->next = NULL; //初始化next域
-
- ll->last->next = p; //将结点放到链表尾部
- ll->last = p; //包裹指针下移
- ll->size++; //增加结点数
- }
- /*头插*/
- void push_front(LinkList* ll, ElemType value)
- {
- PNode p = (Node*)malloc(sizeof(Node)); //申请内存空间
- if (p == NULL)
- return;
- p->data = value; //设置值
-
- p->next = ll->first->next;
- ll->first->next = p;
- if (ll->size == 0)
- {
- ll->last = p;
- }
- ll->size++; //增加结点数
- }
- /*尾删*/
- void pop_back(LinkList* ll)
- {
- if (ll->size == 0) //判断链表是否还有结点?
- return; //直接退出
- PNode p = ll->first; //缓冲变量p
- while (p->next != ll->last) //先找到倒数第二个结点,再代替
- p = p->next; //下移指针
- free(ll->last); //释放内存
- ll->last = p; //取代
- ll->last->next = NULL; //初始化尾结点的指针域
- ll->size--; //减少结点数
- }
- /*头删*/
- void pop_front(LinkList* ll)
- {
- if (ll->size == 0)
- return;
- PNode p = ll->first->next; //临时保存首元结点的地址
- ll->first->next = p->next; //解除连接
- free(p); //释放内存
- if (ll->size == 1)
- ll->last = ll->first;
- ll->size--;
- }
- /*值查找*/
- Node* find(LinkList* ll, ElemType key)
- {
- PNode p = ll->first->next;
- while (p!=NULL && p->data!=key)
- p = p->next;
- return p;
- }
- /*输出*/
- void display(LinkList* ll)
- {
- PNode p = ll->first->next;
- while (p != NULL)
- {
- printf("%c -> ", p->data);
- p = p->next;
- }
- printf("Nul.\n");
- }
- /*反转:beg、mid、end*/
- LinkList* iteration_reverse(LinkList* ll)
- {
- if (ll->first == NULL || ll->first->next == NULL)
- {
- return ll;
- }
- else
- {
- Node* beg = NULL;
- Node* mid = ll->first->next;
- Node* temp = ll->first->next;
- Node* end = temp->next;
- while (1)
- {
- mid->next = beg; //指针域换向
- if (end == NULL)
- {
- //push_back(&ll, NULL);
- break;
- }
- beg = mid;
- mid = end;
- end = end->next;
- }
- ll->first->next = mid;
- return ll;
- }
- }
- int main() {
- LinkList ll = { NULL, NULL, 0 };
- init(&ll);
- printf("此单链表是否为空?(1代表true,0代表false)\n%d\n", empty(&ll));
- push_back(&ll, 'h');
- push_back(&ll, 'e');
- push_back(&ll, 'l');
- push_back(&ll, 'l');
- push_back(&ll, 'o');
- printf("插入hello的单链表的长度:%d\n", size(&ll));
- display(&ll);
- LinkList* list = iteration_reverse(&ll);
- display(list);
- printf("插入hello的单链表的长度:%d\n", size(list));
- //free(ll.first); //释放
- //free(ll.last); //释放
- }
| 链表 | 顺序表 | |
|---|---|---|
| 内存申请时机 | 需要提前申请 | 什么时候存储,什么时候申请 |
| 复用性 | 一次开辟,永久使用(除了动态数组) | 随用随开 |
| 空间利用率 | 链表 < 顺序表(链表明显容易产生空间碎片) | |
| 时间复杂度 | 链表适合操作元素,顺序表适合访问查找元素 | |
单链表结构2:
- #include
- #include
-
- //链表中节点的结构
- typedef struct link {
- int elem;
- struct link* next;
- }Link;
- /*初始化*/
- Link* initLink()
- {
- int i; //缓冲变量i
- //1、创建头指针
- Link* p = NULL;
- //2、创建头结点
- Link* temp = (Link*)malloc(sizeof(Link));
- temp->elem = 0;
- temp->next = NULL;
- //头指针指向头结点
- p = temp;
- //3、每创建一个结点,都令其直接前驱结点的指针指向它
- for (i = 1; i < 5; i++) {
- //创建一个结点
- Link* a = (Link*)malloc(sizeof(Link));
- a->elem = i;
- a->next = NULL;
- //每次 temp 指向的结点就是 a 的直接前驱结点
- temp->next = a;
- //temp指向下一个结点(也就是a),为下次添加结点做准备
- temp = temp->next;
- }
- return p;
- }
- /*插入*/
- void insertElem(Link* p, int elem, int add)
- {
- int i;
- Link* c = NULL;
- Link* temp = p;//创建临时结点temp
- //首先找到要插入位置的上一个结点
- for (i = 1; i < add; i++) {
- temp = temp->next;
- if (temp == NULL) {
- printf("插入位置无效\n");
- return;
- }
- }
- //创建插入结点c
- c = (Link*)malloc(sizeof(Link));
- c->elem = elem;
- //① 将新结点的 next 指针指向插入位置后的结点
- c->next = temp->next;
- //② 将插入位置前结点的 next 指针指向插入结点;
- temp->next = c;
- }
- /*删除*/
- int delElem(Link** p, int elem) //p为原链表,elem 为要删除的目标元素
- {
- Link* del = NULL, *temp = *p;
- //删除首元结点需要单独考虑
- if (temp->elem == elem) {
- (*p) = (*p)->next;
- free(temp);
- return 1;
- }
- else
- {
- int find = 0;
- //1、找到目标元素的直接前驱结点
- while (temp->next) {
- if (temp->next->elem == elem) {
- find = 1;
- break;
- }
- temp = temp->next;
- }
- if (find == 0) {
- return -1;//删除失败
- }
- else
- {
- //标记要删除的结点
- del = temp->next;
- //2、将目标结点从链表上摘除
- temp->next = temp->next->next;
- //3、释放目标结点
- free(del);
- return 1;
- }
- }
- }
- /*更新*/
- int amendElem(Link* p, int oldElem, int newElem) //p 为有头结点的链表,oldElem 为旧元素,newElem 为新元素
- {
- p = p->next;
- while (p) {
- if (p->elem == oldElem) {
- p->elem = newElem;
- return 1;
- }
- p = p->next;
- }
- return -1;
- }
- //p为原链表,elem表示被查找元素
- int selectElem(Link* p, int elem) {
- int i = 1;
- //带头结点,p 指向首元结点
- p = p->next;
- while (p) {
- if (p->elem == elem) {
- return i;
- }
- p = p->next;
- i++;
- }
- return -1;//返回-1,表示未找到
- }
- //输出链表中各个结点的元素
- void display(Link* p) {
- p = p->next;
- while (p) {
- printf("%d ", p->elem);
- p = p->next;
- }
- printf("\n");
- }
- //释放链表
- void Link_free(Link* p) {
- Link* fr = NULL;
- while (p->next)
- {
- fr = p->next;
- p->next = p->next->next;
- free(fr);
- }
- free(p);
- }
- int main() {
- Link* p = initLink();
- printf("初始化链表为:\n");
- display(p);
- printf("在第 3 的位置上添加元素 6:\n");
- insertElem(p, 6, 3);
- display(p);
- printf("删除元素4:\n");
- delElem(p, 4);
- display(p);
- printf("查找元素 2:\n");
- printf("元素 2 的位置为:%d\n", selectElem(p, 2));
- printf("更改元素 1 的值为 6:\n");
- amendElem(p, 1, 6);
- display(p);
- Link_free(p);
- return 0;
- }
即头尾颠倒,方向翻转