注意:
我认为对于数据结构的深入理解并不是掌握书本上的概念,最重要的是自己的动手编写过程,其中有不少饶人的注意点,望读者有了基本了解之后,动手能够亲自实现,这是一个不一样的过程。
**链表中的数据是以节点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
· 以“结点的序列”表示线性表称作线性链表(单链表)
· 单链表是链式存取的结构,为找第 i 个数据元素,必须先找到第 i-1 个数据元素。
· 因此,查找第 i 个数据元素的基本操作为:移动指针,比较 j 和 i
单链表
1、链接存储方法
链接方式存储的线性表简称为链表(Linked List)。
链表的具体存储表示为:
① 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))
注意:
链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。
2、链表的结点结构
┌───┬───┐
│data │next │
└───┴───┘
data域–存放结点值的数据域
next域–存放结点的直接后继的地址(位置)的指针域(链域)
注意:
①链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。
②每个结点只有一个链域的链表称为单链表(Single Linked List)。
【例】线性表(bat,cat,eat,fat,hat,jat,lat,mat)的单链表示如示意图
3、头指针head和终端结点指针域的表示
单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。
注意:
链表由头指针唯一确定,单链表可以用头指针的名字来命名。
终端结点无后继,故终端结点的指针域为空,即NULL。
4、单链表注意点
①LinkList和ListNode是不同名字的同一个指针类型(命名的不同是为了概念上更明确)
②*LinkList类型的指针变量head表示它是单链表的头指针
③ListNode类型的指针变量p表示它是指向某一结点的指针
5、编写过程应了解的函数
①生成结点变量的标准函数
p=( ListNode *)malloc(sizeof(ListNode));
//函数malloc分配一个类型为ListNode的结点变量的空间,并将其首地址放入指针变量p中
②释放结点变量空间的标准函数
free(p);//释放p所指的结点变量空间
③结点分量的访问
利用结点变量的名字*p访问结点分量
方法一:(*p).data和(*p).next
方法二:p-﹥data和p-﹥next
④指针变量p和结点变量*p的关系
指针变量p的值——结点地址
结点变量*p的值——结点内容
(*p).data的值——p指针所指结点的data域的值
(*p).next的值——*p后继结点的地址
*((*p).next)——*p后继结点
注意:
① 若指针变量p的值为空(NULL),则它不指向任何结点。此时,若通过*p来访问结点就意味着访问一个不存在的变量,从而引起程序的错误。
② 有关指针类型的意义和说明方式的详细解释
可见,在链表中插入结点只需要修改指针。但同时,若要在第 i 个结点之前插入元素,修改的是第 i-1 个结点的指针。
因此,在单链表中第 i 个结点之前进行插入的基本操作为:
找到线性表中第i-1个结点,然后修改其指向后继的指针
代码如下:
#include
#include
#include
#define Error 0
#define Ok 1
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*LinkList;
typedef struct Node* LinkList;
/**
* 获得单链表的元素
*/
Status GetElem_L(LinkList L, int i, ElemType *e)
{
//工作指针后移;
int j = 1;
LinkList p ;
p = L->next;
while (p && j < i) {
p = p->next;
++j;
}
if (!p || j > i) {
return Error;
}
*e = p->data;
return Ok;
}
/**
* 单链表的插入
*/
LinkList ListInsert(LinkList *L, int i, ElemType e)
{
int j = 1;
LinkList p,s;
p = *L;
while (p && j < i) {
p = p->next;
++j;
}
if (!p || j > i) {
return Error;
}
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return *L;
}
/**
* 单链表的删除
*/
LinkList ListDelete(LinkList *L, int i)
{
int j = 1;
LinkList p;
p = *L;
while (p && j < i) {
p = p->next;
++j;
}
if (!p || j > i) {
return Error;
}
p->next = p->next->next;
// e = p->data;
free(p->next);
return *L;
}
/**
* 单链表的整表创建
动态生成
(头插法)始终让新节点插在第一的位置
*/
LinkList CreatList_LH(LinkList L,int n)
{
LinkList p;
int i ;
L = (LinkList)malloc(sizeof(Node));
L->next = NULL;
srand((unsigned)time(0));
for (i = 0; i < n; i++) {
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100 + 1;
//printf("%d ",p->data);
p->next = L->next;
L->next = p;
}
return L;
}
/**
* 尾插法
*/
LinkList CreatList_LD(LinkList *L,int n)
{
LinkList p,r;
int i ;
srand((unsigned)time(0));
*L = (LinkList)malloc(sizeof(Node));
//L->next = NULL;
r = *L;
for (i = 0; i < n; i ++) {
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100 + 1;
//printf("%d ",p->data);
r->next = p;
r = p;
}
r->next = NULL;
return *L;
}
/**
* 单链表的清除
*/
Status ClearList(LinkList L)
{
LinkList q,p;
p = L->next;
while (p) {
q = p->next;
free(p);
p = q;
}
L->next = NULL;
return Ok;
}
Status LengthOfList(LinkList L)
{
int length;
while (L->next != NULL) {
length++;
L = L->next;
}
return length;
}
ElemType GetMidNode(LinkList *L)
{
LinkList search,mid;
mid = search = *L;
while (search->next != NULL) {
if (search->next->next != NULL) {
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
mid = mid->next;
}
}
return mid->data;
}
int main()
{
LinkList list,start;
/* printf("请输入单链表的数据:");
list = LinkedListCreatH();
for(start = list->next; start != NULL; start = start->next)
printf("%d ",start->data);
printf("/n");
*/ printf("请输入单链表的个数:");
int j = 0;
scanf("%d",&j);
list = CreatList_LD(&list, j);
for(start = list->next; start != NULL; start = start->next)
printf("%d ",start->data);
printf("\n");
int length = LengthOfList(list);
printf("%d",length);
printf("\n");
int i;
ElemType x;
printf("请输入插入数据的位置:");
scanf("%d",&i);
while (i > j||i <= 0) {
printf("请输入插入数据的位置:");
scanf("%d",&i);
}
printf("请输入插入数据的值:");
scanf("%d",&x);
list = ListInsert(&list, i, x);
for(start = list->next; start != NULL; start = start->next)
printf("%d ",start->data);
printf("\n");
ElemType k;
k = GetMidNode(&list);
printf("%d",k);
printf("\n");
printf("请输入删除数据的位置:");
scanf("%d",&i);
list = ListDelete(&list, i);
for(start = list->next; start != NULL; start = start->next)
{ printf("%d ",start->data);}
printf("\n");
ClearList(list);
return 0;
}
**用数组描述的链表,即称为静态链表。
· 在C语言中,静态链表的表现形式即为结构体数组,结构体变量包括数据域data和游标cur。
· 这种存储结构,仍需要预先分配一个较大的空间,但在作为线性表的插入和删除操作时不需移动元素,仅需修改指针,故仍具有链式存储结构的主要优点。
#include
#include
#include
#define MaxSize 100
typedef int ElemType;
typedef struct {
ElemType data;
int cur;
}SList,StaticLinkList[MaxSize];
/**
* 初始化
*/
void init_sl(StaticLinkList space)
{
int i;
for (i = 0; i < MaxSize - 1; i++) {
space[i].cur = i +1;
}
space[MaxSize-1].cur = 0;
}
/**
* 分配空闲结点
*/
int malloc_sl(StaticLinkList space)
{
int i = space[0].cur;
if (i) {
space[0].cur = space[i].cur;
}
return i;
}
int GetListLength(StaticLinkList space)
{
int j = 0;
int k = space[MaxSize - 1].cur;
while (k) {
j++;
k = space[k].cur;
}
return j;
}
void ListInsert(StaticLinkList L, int i, ElemType e)
{
int k = MaxSize - 1;
if (i < 1|| i > GetListLength(L) + 1) {
return;
}
int v = malloc_sl(L);
{
for (int j= 0 ; j < i- 1; j++) {
k = L[k].cur;//插入的前一个结点
}
L[v].data = e;
L[v].cur = L[k].cur;
L[k].cur = v;
}
}
void Free_SLL(StaticLinkList L,int j)
{
L[j].cur = L[0].cur;
L[0].cur = j;
}
/**
* 删除i位置的元素
*/
void DeleteList(StaticLinkList L, int i)
{
int k = MaxSize - 1;
if (i < 1|| i > GetListLength(L) + 1) {
return;
}
for (int j = 0; j < i -1; j++) {
k = L[k].cur;
}
int temp = L[k].cur;
L[k].cur = L[temp].cur;
Free_SLL(L, temp);
}
void Print(StaticLinkList L) //打印链表
{
int k = L[MaxSize - 1].cur;
while(k)
{
printf("%d ",L[k].data);
k = L[k].cur;
}
printf("\n");
}
int main(int argc, const char * argv[]) {
srand((unsigned)time(0));
StaticLinkList L;
init_sl(L); //初始化链表
for(int i = 0;i < 8;++i)
ListInsert(L, i, rand()%100 + 1);
Print(L); //输出链表
int i,j;
printf("请输入插入的位置:");
scanf("%d",&j);
printf("请输入插入的值:");
scanf("%d",&i);
ListInsert(L,j,i); //在链表的第3个位置插入22
Print(L);
printf("请输入删除的位置:");
scanf("%d",&j);
DeleteList(L,j);
Print(L);
return 0;
}