参考书目:附免费高清扫描版带书签《大话数据结构-第二版》
链接:https://pan.baidu.com/s/1E1_hA-hofkt7f0r61ktWXg
提取码:cmgz
复制这段内容后打开百度网盘手机App,操作更方便哦
视频11-16-——线性表
单链表的插入
/**< 单链表第i个数据插入结点
初始条件:顺序线性表L已经存在,1 <= i <= Listlength(L)
操作结果:在L中第i个位置之前插入新的数据元素e,L的长度+1
*/
typedef struct Node
{
ElemType daTa;//数据域
struct Node *Next;//指针域
}Node;
typedef struct Node* LinkList;//至此struct Node*等价于LinkList
Status ListInsert(LinkList *L, int i, ElemType e)
{
//声明一个结点p,并指向链表的第一个结点
LinkList p,s;
p = *L;
int j = 1;
//遍历链表:寻找第i个结点
while((p != NULL)&&(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 OK;
}
单链表的删除
/**< 单链表第i个数据删除
初始条件:顺序线性表L已经存在,1 <= i <= Listlength(L)
操作结果:在L中第i个位置的数据元素删除,并用e返回其值,L的长度-1
*/
typedef struct Node
{
ElemType daTa;//数据域
struct Node *Next;//指针域
}Node;
typedef struct Node* LinkList;
Status ListInsert(LinkList *L, int i, ElemType e)
{
//声明一个结点p,并指向链表的第一个结点
LinkList p,q;
p = *L;
int j = 1;
//遍历链表:寻找第i个结点
while((p != NULL)&&(j < i))
{
p = p->next;
++j;
}
if( !p || j >i)
return ERROR;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);//c语言中采用free释放,C++中使用delete释放
return OK;
}
单链表和顺序存储结构效率比较
单链表的整表创建
顺序存储结构线性表的整表创建可以用数组初始化来理解。单链表的整表创建是一个动态生成链表的过程,从“空表”的初始化状态起,一次建立各元素结点并逐个插入链表。
头插法建立单链表
PS:最终生成的链表中结点的次序和输入时的顺序相反【尾插法顺序是相同的】。
/**< 头插法建立单链表 */
typedef struct Node
{
ElemType daTa;//数据域
struct Node *Next;//指针域
}Node;
typedef struct Node* LinkList;
void CreateListHead(LinkList *L, int n)
{
LinkList p;
int i;
srand(time(0));//初始化随机数发生器
*L = (LinkList)malloc(sizeof(Node));//malloc动态内存分配
(*L)->next = NULL;
for(i = 0; i < n; i++)
{
p = (LinkList)malloc(sizeof(Node));//生成新的结点
//数据域写入数据
p->data = rand()%100 + 1;//写入0-100之间的数据
p->next = (*L)->next;//新的结点指向原来结点的指针
(*L)->next = p;//头结点重新指向新的结点指针
}
}
尾插法建立单链表【重点】
/**< 尾插法建立单链表 */
typedef struct Node
{
ElemType daTa;//数据域
struct Node *Next;//指针域
}Node;
typedef struct Node* LinkList;
void CreateListTall(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0));//初始化随机数发生器
*L = (LinkList)malloc(sizeof(Node));//malloc动态内存分配
r = (*L);
for(i = 0; i < n; i++)
{
p = (LinkList)malloc(sizeof(Node));//生成新的结点
//数据域写入数据
p->data = rand()%100 + 1;//写入0-100之间的数据
r->next = p;//新的结点指向临时结点p
r = p;//重点!!!将此次的p重命名为r,为下次做准备
}
}
单链表的整表删除
/**< 单链表的整表删除 */
typedef struct Node
{
ElemType daTa;//数据域
struct Node *Next;//指针域
}Node;
typedef struct Node* LinkList;
void DeleteList(LinkList *L)
{
LinkList p,q;
p = (*L)->next;
while(i++ < n)
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL;
}
顺序存储结构和单链表存储比较
静态链表
静态链表:用数组描述的链表。数组的元素都是由两个数据域组成,data和cur,即数组的每一个下标都对应一个data和一个cur。数据域data用来存放数据元素(要处理的数据);而游标cur相当于单链表中的next指针,存放该元素的后继在数组中的下标。数组的第一个元素cur存放备用链表的第一个结点(未被占用的元素)的下标,而数组的最后一个元素cur存放第一个有值的元素下标,相当于头结点作用
/**< 静态链表的初始化 */
#define MAXSIZE 1000
typedef struct
{
ElemType data;//数据
int cur;//游标
}Component,StaticLinkList[MAXSIZE];
//静态链表的初始化,相当于初始化数组
Status InitList(StaticLinkList space)
{
int i;
for(i = 0; i < MAXSIZE-1; i++)
space[i].cur = i + 1;
space[MAXSIZE-1].cur = 0;//因为链表此时为空,所以赋值为0
return OK;
}
静态链表的插入
动态链表中,结点的申请和释放采用C语言的mallo()函数和free()函数实现。
/**< 静态链表插入
(1)获取空闲分量的下标
(2)在静态链表L中第i个元素之前插入新的数据元素e
*/
//获取空闲分量的下标
int Mallo_SLL(StaticLinkList space)
{
int i = space[0].cur;
if(space[0].cur)
space[0].cur = space[i].cur;//吧下一个分量用来作为备用
return i;
}
//在静态链表L中第i个元素之前插入新的数据元素e
Status ListInsert(StaticLinkList L, int i, ElemType e)
{
int j,k,l;
k = MAXSIZE - 1;//数组的最后一个元素
if(i < 1 || i > ListLength(L)+1)
return ERROR;
j = Mallo_SLL(L);
if(j)
{
L[j].data = e;
for(l = 1; l <= i-1; l++)
{
k = L[k].cur;
}
L[j].cur = L[k].cur;
L[k].cur = j;
return OK;
}
return ERROR;
}
静态链表的插删除
删除链表下标2的数据B,链表变为【注意各个游标的变化】
/**< 删除在L中的第i个数据元素 */
Status ListDelete(StaticLinkList L, int i)
{
int j,k;
if(i < 1 || i > ListLength(L))//异常判断
{
return ERROR;
}
k = MAXSIZE - 1;//链表的最后一个元素的下标
//遍历寻找第i个元素对应的游标
for(j = 1; j <= i-1; j++)
{
k = L[k].cur;//k1=1,k2=5
}
j = L[k].cur;//j=2。将第i-1元素对应的游标存放在j中
L[k].cur = L[j].cur;
Free_SLL(L, j);
return OK;
}
//将下标为k的空间结点回收到备用链表
void Free_SLL(StaticLinkList space, int k)
{
space[k].cur = sapce[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;
}
静态链表优缺点
总的来说,静态链表其实是为了给没有指针的编程语言设计一种实现单链表功能的方法。
单链表小结—腾讯面试题
题目:快速找到未知长度单链表的中间节点
/**< 快慢指针原理实现快速查找未知单链表的中间结点
时间复杂度为O(n/2)
*/
Status GetMidNode(LinkList L, ElemType *e)
{
LinkList search, mid;
mid = search = L;
while(search->next != NULL)
{
//arch移动的速度是mid的2倍
if(search->next->next != NULL)
{
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
}
}
*e = mid->next;
return OK;
}
作业:写一个完整的程序,实现随机生成20个元素的链表【头插法或尾插法】,采用“快慢指针原理”快速查找中间结点的值并且显示。
#include <stdio.h>
#include <stdlib.h>
/**< 函数结果状态代码 */
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
/**< typedef作用可以和#define来理解 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct Node
{
ElemType data;//数据域
struct Node *next;//指针域
}Node;
typedef struct Node *LinkList; /* 定义LinkList:结构体指针等价于结构体数组 */
/**< 将每个结点传递过来的数据元素打印输出 */
Status visit(ElemType c)
{
printf("%d ",c);
return OK;
}
/* 初始化顺序线性表 */
Status InitList(LinkList *L)//LinkList *L结构体指针的指针,等价于结构体指针的数组,即数组内都是指针
{
/* 产生头结点,并使L指向此头结点 */
*L=(LinkList)malloc(sizeof(Node));//结构体指针L指向Node大小的内存地址
/* 存储分配失败 */
if(!(*L))//等价于if((*L)==NULL)
{
return ERROR;
}
(*L)->next=NULL; /* 指针域为空 */
return OK;
}
/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L)
{
int i=0;
LinkList p=L->next; /* p指向第一个结点 */
while(p)
{
i++;
p=p->next;
}
return i;
}
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status ListTraverse(LinkList L)
{
LinkList p=L->next;
while(p)
{
visit(p->data);//输出各个结点对应的元素
p = p->next;
}
printf("\n");
return OK;
}
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0)); /* 初始化随机数种子 */
*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
r=*L; /* r为指向尾部的结点 */
for (i=0; i < n; i++)
{
p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */
p->data = rand()%100+1; /* 随机生成100以内的数字 */
r->next=p; /* 将表尾终端结点的指针指向新结点 */
r = p; /* 将当前的新结点定义为表尾终端结点 */
}
r->next = NULL; /* 表示当前链表结束 */
}
Status GetMidNode(LinkList L, ElemType *e)
{
LinkList search, mid;
mid = search = L;
while (search->next != NULL)
{
//arch移动的速度是 mid 的2倍
if (search->next->next != NULL)
{
search = search->next->next;
mid = mid->next;
}
else
{
search = search->next;
}
}
*e = mid->data;
return OK;
}
int main()
{
LinkList L;
Status i;
char opp;//接收当前输入的指令代号
ElemType e;
i=InitList(&L);
printf("初始化L后:ListLength(L)=%d\n",ListLength(L));
printf("\n1.查看链表 \n2.创建链表(尾插法) \n3.链表长度 \n4.中间结点值 \n0.退出 \n请选择你的操作:\n");
while(opp != '0')
{
scanf("%c",&opp);
switch(opp)
{
case '1'://输出链表中的数据元素
ListTraverse(L);
printf("\n");
break;
case '2':
CreateListTail(&L,20);
printf("整体创建L的元素(尾插法):\n");
ListTraverse(L);
printf("\n");
break;
case '3':
printf("ListLength(L)=%d \n",ListLength(L));
printf("\n");
break;
case '4':
GetMidNode(L, &e);
printf("链表中间结点的值为:%d\n", e);
printf("\n");
break;
case '0':
exit(0);
}
}
}