为了表示每一个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来讲,除了存储其自己的信息以外,还需存储一个指示其直接后继的信息。咱们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。这两部分信息组成数据元素的存储映像,称为结点。
n个结点链组成一个链表,即为线性表的链式存储结构,由于此链表的每一个结点中只包含一个指针域,因此叫作单链表。
对于线性表来讲,有头有尾,链表中的第一个结点的存储位置叫作头指针,链表的存取必须是从头指针开始进行。
有时候我了增长对链表操做的方便性,咱们会在链表的第一个结点(首元结点)前增长一个结点,称为头结点。头结点的数据域能够不存任何数据,指针域存储第一个结点的指针。
链式存储结构中咱们默认都带上头结点。
图片示意以下:函数
头指针和头结点的区别:
头指针:性能
头结点:spa
单链表结点中包含了数据域和指针域,以下图所示:
结点定义以下:3d
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 /* ElemType类型根据实际状况而定,这里假设为int */ typedef int ElemType; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef int Status; //定义结点 typedef struct Node{ ElemType data; // 存储在数据域中的数据 struct Node *next; // 直接后继结点的地址指针 }Node; typedef struct Node * LinkList;
思路:指针
Status InitList(LinkList *L){ //产生头结点,并使用L指向此头结点 *L = (LinkList)malloc(sizeof(Node)); //存储空间分配失败 if(*L == NULL) return ERROR; //将头结点的指针域置空 (*L)->next = NULL; return OK; }
假设要在单链表的两个数据元素A和B之间插⼊一个数据元素e,已知p为其单链表存储结构中指向结点A指针。以下图所示
思路:code
代码实现以下:blog
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L); 操做结果:在L中第i个位置以前插入新的数据元素e,L的长度加1; */ Status ListInsert(LinkList *L,int i,ElemType e){ int j; LinkList p,s; p = *L; j = 1; //寻找第i-1个结点 while (p && j<i) { p = p->next; ++j; } //第i-1个元素不存在 if(!p || j>i) return ERROR; //生成新结点s s = (LinkList)malloc(sizeof(Node)); //将e赋值给s的数值域 s->data = e; //将p的后继结点赋值给s的后继 s->next = p->next; //将s赋值给p的后继 p->next = s; return OK; }
要删除单链表中指定位置的元素,同插入元素同样,首先应该找到该位置的前驱结点,以下图所示在单链表中删除元素B时,应该首先找到其前驱结点A,为了在单链表中实现元素A,B,C之间的逻辑关系的变化,仅需修改结点A中的指针域便可.
思路:图片
代码实现以下:内存
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 操做结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ Status ListDelete(LinkList *L,int i,ElemType *e){ int j; LinkList p,q; p = *L; j = 1; //查找第i-1个结点,p指向该结点 while (p->next && j<i) { p = p->next; ++j; } //当i>n 或者 i<1 时,删除位置不合理 if (!(p->next) || j>i) return ERROR; //q指向要删除的结点 q = p->next; //将q的后继赋值给p的后继 p->next = q->next; //将q结点中的数据给e *e = q->data; //让系统回收此结点,释放内存; free(q); return OK; }
在单链表中,咱们不能像顺序存储结构那样直接经过下标直接获取数据,咱们没办法一开始就知道,必须得从头开始找,进行遍历。
思路:get
代码实现以下:
/* 初始条件: 顺序线性表L已存在,1≤i≤ListLength(L); 操做结果:用e返回L中第i个数据元素的值 */ Status GetElem(LinkList L,int i,ElemType *e){ int j = 1; //声明结点p; LinkList p; //将结点p 指向链表L的首元结点; p = L->next; //p不为空,且计算j不等于i,则循环继续 while (p && j<i) { //p指向下一个结点 p = p->next; ++j; } //若是p为空或者j>i,则返回error if(!p || j > i) return ERROR; //e = p所指的结点的data *e = p->data; return OK; }
思路:
循环:
代码实现以下:
/* 随机产生n个元素值,创建带表头结点的单链线性表L(前插法)*/ void CreateListHead(LinkList *L, int n){ LinkList p; //创建1个带头结点的单链表 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; //循环前插入随机数据 for(int i = 0; i < n;i++) { //生成新结点 p = (LinkList)malloc(sizeof(Node)); //i赋值给新结点的data p->data = i; p->next = (*L)->next; //将结点P插入到头结点以后; (*L)->next = p; } }
思路:
循环:
代码实现以下:
/* 随机产生n个元素值,创建带表头结点的单链线性表L(后插法)*/ void CreateListTail(LinkList *L, int n){ LinkList p,r; //创建1个带头结点的单链表 *L = (LinkList)malloc(sizeof(Node)); //r指向尾部的结点 r = *L; for (int i=0; i<n; i++) { //生成新结点 p = (Node *)malloc(sizeof(Node)); p->data = i; //将表尾终端结点的指针指向新结点 r->next = p; //将当前的新结点定义为表尾终端结点 r = p; } //将尾指针的next = null r->next = NULL; }
思路:
循环:
代码实现以下:
/* 初始条件:顺序线性表L已存在。操做结果:将L重置为空表 */ Status ClearList(LinkList *L) { LinkList p,q; p=(*L)->next; /* p指向第一个结点 */ while(p) /* 没到表尾 */ { q=p->next; free(p); p=q; } (*L)->next=NULL; /* 头结点指针域为空 */ return OK; }
存储分配方式:
时间性能:
查找
插入与删除
空间性能:
经过对比,咱们可得知:
若线性表须要频繁查找,不多进行插入和删除操做时,宜采用顺序存储结构。若须要频繁插入和删除时,宜采用单链表结构。
当线性表中的元素个数变化较大或者根本不知道有多大的时候,最好采用单链表结构,这样能够不须要考虑存储空间大小的问题。而若是事先知道线性表的大体长度,就能够用这个顺序存储结构,用起来比较高效。
将单链表中的终端结点的指针从空指针改成指向头结点,就使整个单链表造成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。
为了使空链表与非空链表处理一致,咱们一般设一个头结点。
循环列表中带有头结点的空链表以下图:
对于非空的循环链表以下图:
思路:
代码实现:
/* 建立一个带有头结点的循环链表 */ Status CreateList(LinkList *L) { int item; LinkList temp = NULL; LinkList r = NULL; // 尾结点指针 printf("请输入新的结点, 当输入0时结束!\n"); while (1) { scanf("%d",&item); if (item == 0) { break; } // 当链表为空时,建立链表,带上头结点。 if (*L == NULL) { *L = (LinkList)malloc(sizeof(Node)); if (!*L) return ERROR; (*L)->next = *L; // 使其next指针指向本身 r = *L; } temp = (LinkList)malloc(sizeof(Node)); if(!temp) return ERROR; temp->data = item; temp->next = *L; r->next = temp; r = temp; } return OK; }
思路:
代码实现以下:
/* 循环链表插入数据 */ Status ListInsert2(LinkList *L, int place, int num) { if (place < 1) { return ERROR; } LinkList target; LinkList temp; int k; temp = (LinkList)malloc(sizeof(Node)); if (!temp) return ERROR; temp->data = num; // 查找插入位置的前一个结点 for (k = 1, target = *L; k < place && target->next != *L; k++, target = target->next); temp->next = target->next; target->next = temp; return OK; }
思路:
代码实现以下:
/* 循环链表删除数据,链表表L已存在,1≤place≤ListLength(L) */ Status LinkListDelete(LinkList *L,int place) { if (place < 1) { return ERROR; } LinkList temp, target; int k; for (k = 1, target = *L; k < place && target->next != *L; k++, target = target->next); if (place > k) { return ERROR; } temp = target->next; target->next = temp->next; free(temp); return OK; }
思路:
代码实现以下:
/* 循环链表查询值的位置 */ int findValue(LinkList L, int value) { int i = 1; LinkList p; p = L->next; while (p->data != value && p->next != L) { i++; p = p->next; } // 若是已经遍历到最后一个元素,且尚未找到,那么直接返回-1. if (p->next == L && p->data != value) { return -1; } return i; }