单链表
CONTENT
链表介绍
实现代码
写在最后
一、链表介绍
链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
单链表时一种在内存上节点物理空间并不连续的线性表,主要优势是空间利用率比较高。
二、实现代码
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> using namespace std; /**********线性表---单链表**********/ // 说明:本代码主观性比较强,仅供参考,欢迎交流和学习 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASBLE -1 #define OVERFLOW -2 typedef int Status; typedef int Elemtype; // 定义链表的节点 typedef struct LNode { Elemtype data; struct LNode *next; } LNode, *LinkList; // LNode可以代替 struct LNode // *LinkList --> *LNode --> struct LNode* 三者等价 // 基本操作: // 逆位序输入n个元素,建立代表头节点的单链性表L void CreatList_L(LinkList &L, int n); // 取出链表中第i个节点中的信息 Status GetElem_L(LinkList L, int i, Elemtype &e); // 在表头后第i个位置之前插入元素e Status ListInsert_L(LinkList L, int i, Elemtype e); // 删除链表的第i个元素并返回 Status ListDelet_L(LinkList L, int i, Elemtype &e); // 打印链表 Status ListPrint_L(LinkList L); // 合并两个有序的链表为一个链表 void MergeList_L(LinkList La, LinkList Lb, LinkList Lc); // 清空链表 void ListClear_L(LinkList L); // 获取链表长度 int ListLength_L(LinkList L); int main() { LinkList L; // LNode *L; // 两者等效 // 1 创建链表 CreatList_L(L, 3); // 2 取出链表第2个元素 Elemtype e; GetElem_L(L, 2, e); printf("链表第2个元素为:%d ", e); // 3 打印链表 ListPrint_L(L); // 4 计算链表长度 printf("L_Length = %d ", ListLength_L(L)); // 5 插入元素 ListInsert_L(L, 4, 4); printf("在第3个节点后插入元素后的链表为: "); ListPrint_L(L); // 6 删除节点 ListDelet_L(L, 2, e); printf("节点已删除,删除的元素为:%d ", e); printf("链表剩余元素为: "); ListPrint_L(L); // 7 清空链表 ListClear_L(L); printf("链表已清空 "); return 0; } // 逆位序输入n个元素,建立代表头节点的单链性表L // 头插法插入元素 void CreatList_L(LinkList &L, int n) { L = (LinkList)malloc(sizeof(LNode)); L->next = NULL; // 创建链表头,注意链表头也是一个结构体,但是表头里一般不储存数据 // 将表头的指针初始化为NULL表示链表的长度为零,因为没有后继 for (int i = n; i > 0; i--) { LinkList p = (LinkList)malloc(sizeof(LNode)); // 创建节点 scanf("%d", &p->data); // 输入插入元素 p->next = L->next, L->next = p; // 将p前继指向p后继的指针赋值给p使p->next指向p的后继 // 同时也将p的前继的指针指向p // 实现 p前继->p->p后继 // 同解如 ListInsert_L()最后 } } // 取出链表中第i个节点中的信息 Status GetElem_L(LinkList L, int i, Elemtype &e) { LinkList p = L->next; int j = 0; while (p && j < i - 1) p = p->next, j++; // 寻找在连表内的第i个元素 if (!(p->next) || j > i) return ERROR; // 若链表长度小于i,返回错误 e = p->data; // 获取元素信息 return OK; } // 在表头后第i个位置之后插入元素e Status ListInsert_L(LinkList L, int i, Elemtype e) { LinkList p = L; int j = 0; while (p && j < i - 1) // 当遇到尾节点时p == NULL 会结束函数 p = p->next, j++; // p = p->next 作用:将等钱当前节点移动到下一个节点 // j单纯用于计数,表示节点移动次数 if (!p || j >= i) return ERROR; // 若移动到的节点是NULL或者超出链表长度就返回ERROR LinkList node = (LinkList)malloc(sizeof(LNode)); // 创建新的节点 node->data = e; // 给新的节点写入元素 node->next = p->next; // KEY1 p->next = node; // KEY2 // 插入前:p->p后 (p后是插入前p后的第一个元素) // 插入中:node->p后 KEY1 // p->node KEY2 // 插入后:p->node->p后 // 同解如 CreatList_L()最后 return OK; } // 删除链表的第i个元素并返回 Status ListDelet_L(LinkList L, int i, Elemtype &e) { LinkList p = L; int j = 0; while (p->next && j < i - 1) p = p->next, j++; // 1. 寻找范围在链表内(while的条件中p->next限制只能在链表内找) // 2. 寻找第i个节点,并且让p指向它的前趋 // 3. 为什么要让p指向前驱? // 删除的原理是,让第i个节点的前驱的指针直接指向第i个节点的下一个节点 if (!(p->next) || j >= i) return ERROR; // 若未找到元素将返回错误 LinkList q; // 定义一个临时的指针,指向节点i q = p->next; // KEY1 p->next = q->next; // KEY2 e = q->data; // 写入数据 // KEY1: p->next指向节点i,所q->next也会指向节点i // KEY2:获取节点i的后继的节点地址赋值给q->next会使节点i的前驱与后继连接 // 删除前 p前驱->p->p后继 // 删除后 p前驱->p后继 free(q); // 释放q防止内存积压 return OK; } // 打印链表 Status ListPrint_L(LinkList L) { LinkList pMove = L->next; // 定义一个指针分别去找链表中的每一个元素 if (!(L->next)) { printf("NULL "); return ERROR; } while (pMove) { printf("%d ", pMove->data); // 输出的格式根据元素的不同更换 pMove = pMove->next; // 移动到下一个节点 } putchar(' '); return OK; } // 合并两个有序的链表为一个链表(使用前提La与Lb为有序表) void MergeList_L(LinkList La, LinkList Lb, LinkList Lc) { LinkList pa, pb, pc; // 创建三个指针,以读取链表 pa = La->next, pb = Lb->next; // 初始化La和Lb的指针 Lc = pc = La; // 以La为表头 while (pa && pb) if (pa->data <= pb->data) pc->next = pa, pc = pa, pa = pa->next; // pc是Lc的指针,不断为Lc连接新的节点,然后使pc后移到新的节点,最后又将pa移动到La的下一个节点 else pc->next = pb, pc = pb, pb = pb->next; // 同理 pc->next = pa ? pa : pc; // 插入链表剩余的部分,如果pa是空说明pc不空,就把pc的后半段插入Lc,否则pb的后半段插入Lc free(Lb); //节点Lb已经没有作用了,释放内存 } // 清空链表 void ListClear_L(LinkList L) { LinkList pClr, p; p = pClr = L->next; // p是移动指针不断移动到下一个节点,pClr应释放的节点 while (p) { p = p->next; free(pClr); pClr = p; } free(pClr); L->next = NULL; return; } // 获取链表长度 int ListLength_L(LinkList L) { LinkList p = L->next; int count = 0; while (p) p = p->next, count++; // 节点不为空时,执行循环并计次 return count; }
三、写在最后
代码个人观点比较强,若代码中存在bug欢迎各位reader指出。