一.单链表:
1.1定义:
对于链表来说,总得有个头有个尾,链表也不例外。我们把链表中的第一个结点的存储位置叫做头指针,最后一个结点指针为空(NULL)。
头指针
头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
无论链表是否为空,头指针均不为空。
头指针是链表的必要元素。
头结点
头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。
有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
头结点不一定是链表的必须要素。
typedef struct Node{ElemType data; // 数据域struct Node* Next; // 指针域} Node, * LinkList;
1.2 实例
InitList(*L): 初始化操作,建立一个空的链表L。
ListEmpty(L): 判断链表是否为空表,若线性表为空,返回true,否则返回false。
ClearList(*L): 将链表清空。
GetElem(L,i,*e): 将链表L中的第i个位置元素值返回给e。
LocateElem(L,e): 在链表L中查找与给定值e相等的元素,如果查找成功,返回该元素在 表中序号表示成功;否则,返回0表示失败。
ListInsert(*L,i,e): 在链表L中第i个位置插入新元素e。
ListDelete(L,i,e): 删除链表L中第i个位置元素,并用e返回其值。
ListLength(L): 返回链表L的元素个数。
1.3 eg:
list.h
#include<stdio.h>#include <stdlib.h>#include<assert.h>typedef int DataType;typedef struct ListNode{DataType data;struct ListNode* next;}Node, * LinkList;LinkList InitList(void); //初始化LinkList BuyNode(DataType data); //创建新结点void Printflist(LinkList list); //打印void PushFront(LinkList list, DataType data); //头插 (有头节点的)void PushBack(LinkList list, DataType data); //尾插void PopFront(LinkList list); //头删(有头节点的)void PopBack(LinkList list); //尾删LinkList Find(LinkList plist, DataType data); //查找bool GetElem(LinkList list, int pos, DataType* e); //用e返回L中第pos个数据元素的值bool ListInsert(LinkList list, int pos, DataType data); //在L中第pos个位置之前插入新的数据元素e,L的长度加1void Removelist(LinkList list, DataType data); //删除第一个指定元素bool ListDelete(LinkList list, int pos, DataType* e); //删除第pos个数据元素,并用e返回其值,L的长度-1void ClearList(LinkList list); //释放链表
list.c
#include"list.h"LinkList InitList(void)//初始化{LinkList list = (LinkList)malloc(sizeof(Node));assert(list);list->data = 0;list->next = NULL;return list;}LinkList BuyNode(DataType data)//创建新结点{LinkList NewNodelist = (LinkList)malloc(sizeof(Node));if (NewNodelist == NULL){printf("fail");exit(EXIT_FAILURE);}NewNodelist->data = data;NewNodelist->next = NULL;return NewNodelist;}void Printflist(LinkList list)//打印{LinkList cur = NULL;cur = list;if (cur == NULL){printf("list is mepty");}else{while (cur){printf("%d->", cur->data);cur = cur->next;}printf("over \n");}}void PushFront(LinkList list, DataType data)//头插 (有头节点的){LinkList NewNodelist = BuyNode(data);NewNodelist->next = list->next;list->next = NewNodelist;}void PushBack(LinkList list, DataType data)//尾插{assert(list);LinkList NewNode = NULL;NewNode = BuyNode(data);while (list->next){list = list->next;}list->next = NewNode;}void PopFront(LinkList list) //头删(有头节点的){LinkList del = NULL;assert(list);if (list == NULL){printf("list is empty");exit(EXIT_FAILURE);}del = list->next;list->next = del->next;free(del);del = NULL;}void PopBack(LinkList list)//尾删{LinkList cur = NULL;LinkList prev = NULL;assert(list);cur = list;if (cur == NULL){printf("list ic empty");exit(EXIT_FAILURE);}if (cur->next == NULL)//1.只有一个结点{free(cur);cur = NULL;}else//2.有大于一个的结点{while (cur->next){prev = cur;cur = cur->next;}free(cur);prev->next = NULL;}}LinkList Find(LinkList list, DataType data)//查找某个第一个元素,并返回指向该元素的结点的指针{LinkList cur = list;assert(list);if (cur == NULL)//1.如果链表为空{printf("list is empty");exit(EXIT_FAILURE);}while (cur)//2.链表不为空{if (cur->data == data){return cur;}cur = cur->next;}printf("no exist");return NULL;}/* 初始条件:顺序线性表L已存在,1<=pos<=ListLength(L) *//* 操作结果:用e返回L中第pos个数据元素的值 */bool GetElem(LinkList list, int pos, DataType* e){int j;LinkList cur;//list为头指针,必不为空,数据为头节点(一般存放数据大小)cur = list->next;j = 1;while (cur && j < pos){cur = cur->next;++j;}if (!cur || j > pos){return false;}*e = cur->data;return true;}/* 初始条件:顺序线性表L已存在,1<=pos<=ListLength(L) *//* 操作结果:在L中第pos个位置之前插入新的数据元素e,L的长度加1 */bool ListInsert(LinkList list, int pos, DataType data){int j;LinkList cur;LinkList NewNodelist = BuyNode(data);cur = list;j = 1;while (cur && j < pos) // 用于寻找第i个结点{cur = cur->next;j++;}if (!cur || j > pos){return false;}NewNodelist->next = cur->next;cur->next = NewNodelist;return true;}void Removelist(LinkList list, DataType data) //删除第一个指定元素{LinkList cur = NULL;LinkList prev = NULL;LinkList del = NULL;assert(list);cur = list;if (list == NULL)//1.空链表{printf("list is empty");exit(EXIT_FAILURE);}while (cur)//2.非空链表{if (cur->data == data){if (cur == list) //2.1删除的结点为第一个结点{del = cur;list = cur->next;free(del);}else //2.2删除的不是第一个结点{del = cur;prev->next = cur->next;free(del);}return;}else{prev = cur;cur = cur->next;}}}/* 初始条件:顺序线性表L已存在,1<=pos<=ListLength(L) *//* 操作结果:删除L的第pos个数据元素,并用e返回其值,L的长度-1 */bool ListDelete(LinkList list, int pos, DataType* e){int j;LinkList p, q;p = list;j = 1;while (p->next && j < pos){p = p->next;++j;}if (!(p->next) || j > pos){return false;}q = p->next;p->next = q->next;*e = q->data;free(q);return true;}void ClearList(LinkList list)//释放链表{LinkList cur = list;LinkList del = NULL;assert(list);while (cur){del = cur;cur = cur->next;free(del);del = NULL;}list = NULL;}
main.c
#include"list.h"void Test(){int* e = (int*)malloc(sizeof(int));LinkList list = InitList();PushBack(list, 1);PushBack(list, 2);PushBack(list, 3);PushBack(list, 4);PushBack(list, 5);PushBack(list, 6);//PopFront(list);//PopBack(list);ListDelete(list, 1,e);assert(e);printf_s("返回的元素是%d\r\n",*e);Printflist(list);free(e);e = NULL;}int main(){Test();system("pause");return 0;}
二、双向链表
1.定义
typedef struct DualNode{ElemType data;struct DualNode *prior; //前驱结点struct DualNode *next; //后继结点} DualNode, *DuLinkList;

既然单链表可以有循环链表,那么双向链表当然也可以有。
2.插入操作
其实并不复杂,不过顺序很重要,千万不能写反了。
代码实现:
s->next = p;s->prior = p->prior;p->prior->next = s;p->prior = s;
关键在于交换的过程中不要出现矛盾,例如第四步先被执行了,那么p->prior就会提前变成s,使得插入的工作出错。
3.删除操作

p->prior->next = p->next;p->next->prior = p->prior;free(p);
三、静态链表
1.静态链表的游标实现法
2.静态链表存储结构
#include<stdio.h>#include <stdlib.h>#include<assert.h>#define MAXSIZE 100typedef struct{int data; // 数据int cur; // 游标(Cursor)} Component, StaticLinkList[MAXSIZE];bool InitList(StaticLinkList space){int i;for (i = 0; i < MAXSIZE - 1; i++)space[i].cur = i + 1;space[MAXSIZE - 1].cur = 0;return true;}//首先是获得空闲分量的下标:int Malloc_SLL(StaticLinkList space){int i = space[0].cur;if (space[0].cur)space[0].cur = space[i].cur;// 把它的下一个分量用来作为备用。return i;}/* 在静态链表L中第i个元素之前插入新的数据元素e */bool ListInsert(StaticLinkList L, int i, int e){int j, k, a;k = MAXSIZE - 1; // 数组的最后一个元素if (i<1 || i>Malloc_SLL(L) + 1){return false;}j = Malloc_SLL(L);if (j){L[j].data = e;for (a = 1; a <= i - 1; a++){k = L[k].cur;}L[j].cur = L[k].cur;L[k].cur = j;return true;}return false;}/* 删除在L中的第i个数据元素 */bool ListDelete(StaticLinkList L, int i){int j, k;if (i<1 || i>Malloc_SLL(L)){return false;}k = MAXSIZE - 1;for (j = 1; j <= i - 1; j++){k = L[k].cur; // k1 = 1, k2 = 5}j = L[k].cur; // j = 2L[k].cur = L[j].cur;Free_SLL(L, j);return true;}/* 将下标为k的空闲结点回收到备用链表 */void Free_SLL(StaticLinkList space, int k){space[k].cur = space[0].cur;space[0].cur = k;}/* 返回L中数据元素个数 */int ListLength(StaticLinkList L){int j = 0;int i = L[MAXSIZE - 1].cur;while (i){i = L[i].cur;j++;}return j;}int main(){StaticLinkList space;InitList(space);system("pause");return 0;}
3.静态链表优缺点总结
优点:
在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点。
缺点:
没有解决连续存储分配(数组)带来的表长难以确定的问题。失去了顺序存储结构随机存取的特性。
总的来说,静态链表其实是为了给没有指针的编程语言设计的一种实现单链表功能的方法。
