// 2.3线性表的链式存储结构————链表
/*
单链表(线性单向链接表):每个结点中除包含数据域以外只设置一个指针域,用于指向其后继节点。
双链表(线性双向链接表):每个结点中除包含数据域以外设置两个指针域,分别用于指向其前驱结点和后继结点。
头指针:头结点的指针。
首指针:指向首结点或者开始结点的指针。
尾指针:指向尾结点的指针。
从一个链表的头指针所指的头结点出发,沿着结点的链(即指针域的值)可以访问到每个结点,当进行插入删除操作时,只需修改指针域。
顺序表是线性表的直接映射,具有随机存取特性;链表不具有该特性。
顺序表的存储密度高(存储密度=结点中数据元素所占的存储量/结点所占的存储量),一般情况下,存储密度越大,存储空间的利用率越高。
顺序表的存储密度为1,链表小于1,单链表为50%。
在单链表中增加一个头结点的优点:
①单链表中首结点的插入与删除操作同其他结点一致,无需进行特殊处理。
②无论单链表是否为空都有一个头结点,统一了空表与非空表的处理过程。
*/
typedef int ElemType;
typedef struct LNode
{
ElemType data;// 存放元素值
struct LNode * next;// 指向后继结点
} LinkNode;// 单链表结点类型
/*
①单链表的插入结点操作:
在单链表的两个数据域分别为a和b的结点(已知a结点的指针p)之间插入一个数据域为x的结点(由s指向它)
s->next = p->next;
p->next = s;
顺序不能颠倒,因为先p->next = s;指向b的指针就不存在了,s->next = p->next = s会出现插入错误。
②单链表的删除结点操作:
删除结点p的后继结点:
p->next = p->next->next;
删除结点后释放其存储空间:
q = p->next;
p->next = q->next;
free(q);
*/
void CreateListF(LinkNode *&L, ElemType a[], int n);// 头插法建立单链表
void CreateListR(LinkNode *&L, ElemType a[], int n);// 尾插法建立单链表
void InitList(LinkNode *&L);
void DestoryList(LinkNode *&L);// 双指针法
bool ListEmpty(LinkNode *L);
int ListLength (LinkNode *L);
void DispList(LinkNode *L);
bool GetElem(LinkNode *L, int i, ElemType &e);
int LocateElem(LinkNode *L, ElemType e);
bool ListInsert(LinkNode *&L, int i, ElemType e);
bool DeteleList(LinkNode *&L, int i, ElemType &e);
void split(LinkNode *&L, LinkNode *&L1, LinkNode *&L2);// L1尾插法,L2头插法
void delmaxnode(LinkNode *&L);// 双指针法
void sort(LinkNode *&L);// 直接插入法
int main()
{
LinkNode *L, *L1, *L2;
int a[9] = {4, 8, 27, 89, -2, 7, 0, 68, 3};
ElemType e;
InitList(L);
InitList(L1);
InitList(L2);
if (ListEmpty(L))
printf("链表为空!\n");
else
printf("链表不为空!\n");
CreateListF(L, a, 9);
CreateListF(L1, a, 9);
CreateListF(L2, a, 9);
DispList(L);
CreateListR(L, a, 9);
DispList(L);
if (ListEmpty(L))
printf("链表为空!\n");
else
printf("链表不为空!\n");
printf("链表的长度为:%d\n", ListLength(L));
if (GetElem(L, 3, e))
printf("链表第3位的元素是:%d\n", e);
else
printf("查找序号有误!\n");
if (LocateElem(L, 16))
printf("元素%d在链表的第%d位。\n", 16, LocateElem(L, 16));
else
printf("链表中没有该元素!\n");
if (LocateElem(L, 27))
printf("元素%d在链表的第%d位。\n", 27, LocateElem(L, 27));
else
printf("链表中没有该元素!\n");
if (ListInsert(L, 6, 98))
DispList(L);
if (DeteleList(L, 6, e))
{
printf("被删除的元素是:%d\n", e);
DispList(L);
}
delmaxnode(L);
DispList(L);
sort(L);
DispList(L);
split(L, L1, L2);
DispList(L);
DispList(L1);
DispList(L2);
DestoryList(L);
return 0;
}
// 建立单链表
//(1)头插法:时间复杂度O为(n),单链表中数据结点的顺序与数组a中元素的顺序相反。
void CreateListF(LinkNode *&L, ElemType a[], int n)
{
LinkNode *s;
L = (LinkNode * )malloc(sizeof(LinkNode));
L->next = NULL;// 创建头结点,将next置空。
for (int i=0; i<n; i++)
{
s = (LinkNode * )malloc(sizeof(LinkNode));
s -> data = a[i];// 创建数据结点。
s -> next = L -> next;// 将结点s插入原首结点前、头结点后。
L -> next = s;
}
}
//(2)尾插法:时间复杂度为O(n),单链表中数据结点的顺序与数组a中元素的顺序相同,需要增加尾指针r。
void CreateListR(LinkNode *&L, ElemType a[], int n)
{
LinkNode *s, *r;
L = (LinkNode * )malloc(sizeof(LinkNode));
r = L;// r始终指向尾结点,初始时指向头结点。
for (int i=0; i<n; i++)
{
s = (LinkNode * )malloc(sizeof(LinkNode));
s -> data = a[i];
r -> next = s;
r = s;
}
r -> next = NULL;// 将尾结点的next域置为NULL。
}
// 1、初始化线性表,时间复杂度O(1)
void InitList(LinkNode *&L)
{
L = (LinkNode * )malloc(sizeof(LinkNode));
L -> next = NULL;
}
// 2、销毁线性表,时间复杂度O(n)
void DestoryList(LinkNode *&L)// 双指针法
{
LinkNode * pre = L, *p = L -> next;// pre指向结点p的前驱结点
while (p != NULL)// 遍历单链表
{
free(pre);// 释放pre结点
pre = p;// pre、p同步后移一个结点
p = pre->next;
}
free(pre);// 循环结束时p为NULL,pre指向尾结点,释放它。
}
// 3、判空,时间复杂度O(1)
bool ListEmpty(LinkNode *L)
{
return (L -> next == NULL);
}
// 4、求线性表的长度,时间复杂度为O(n)
int ListLength (LinkNode *L)
{
int n=0;
LinkNode *p = L;// p指向头结点,n置为0
while (p != NULL)
{
p = p->next;
n++;
}
return n;
}
// 5、输出线性表,时间复杂度为O(n)
void DispList(LinkNode *L)
{
LinkNode *p = L -> next;
while (p != NULL)
{
printf("%d ", p -> data);
p = p -> next;
}
printf("\n");
}
// 6、按序号求线性表中的元素,时间复杂度为O(n)
bool GetElem(LinkNode *L, int i, ElemType &e)
{
LinkNode *p = L;
int j=0;
if (i <= 0)
return false;
while (j<i && p != NULL)
{
j++;
p = p -> next;
}
if (p == NULL)
return false;
else
{
e = p -> data;
return true;
}
}
// 7、按元素值查找
int LocateElem(LinkNode *L, ElemType e)
{
LinkNode *p = L -> next;
int i=1;
while (p != NULL && p -> data != e)
{
p = p -> next;
i++;
}
if (p == NULL)
return 0;
else
return i;
}
// 8、插入数据元素,时间复杂度为O(n)
bool ListInsert(LinkNode *&L, int i, ElemType e)
{
LinkNode *p = L, *s;
int j=0;
if (i <= 0)
return false;
while (j < i-1 && p != NULL)
{
j++;
p = p -> next;
}
if (p == NULL)
return false;
else
{
s = (LinkNode * )malloc(sizeof(LinkNode));
s -> data = e;
s -> next = p -> next;
p -> next = s;
return true;
}
}
// 9、删除数据元素,时间复杂的为O(n)
bool DeteleList(LinkNode *&L, int i, ElemType &e)
{
LinkNode *p = L, *q;
int j=0;
if (i <= 0)
return false;
while (j<i-1 && p != NULL)
{
j++;
p = p -> next;
}
if (p == NULL)
return false;
else
{
q = p -> next;
if (q == NULL)
return false;
e = q -> data;
p -> next = q -> next;
free(q);
return true;
}
}
// 单链表的应用示例
// 1、将一个带头结点的单链表L拆分成两个带头结点的单链表L1和L2。
void split(LinkNode *&L, LinkNode *&L1, LinkNode *&L2)// L1尾插法,L2头插法
{
LinkNode *p = L -> next, *q2, *r1;
L1 = L;// L1利用L的头结点
r1 = L1;// r1始终指向L1的尾结点
L2 = (LinkNode * )malloc(sizeof(LinkNode));
L2 -> next = NULL;// 创建L2的头结点
while(p != NULL)
{
r1 -> next = p;
r1 = p;
p = p -> next;
q2 = p -> next;
p -> next = L2 -> next;
L2 -> next = p;
p = q2;
}
r1 -> next = NULL;
}
// 2、设计一个算法,删除一个单链表L中元素值最大的结点(假设该结点只有一个)
void delmaxnode(LinkNode *&L)// 双指针法
{
LinkNode *p = L -> next, *pre = L, *maxp = p, *maxpre = pre;
while(p != NULL)
{
if (p -> data > maxp -> data)
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p -> next;
}
maxpre -> next = maxp -> next;
free(maxp);
}
// 3、有一个带头结点的单链表L(至少有一个数据结点),设计一个算法实现所有数据结点按data域递增排序。
void sort(LinkNode *&L)// 直接插入法
{
LinkNode *p, *pre, *q;
p = L -> next -> next; // p指向L的第二个数据结点
L -> next -> next = NULL; // 构造只含一个数据结点的有序单链表。
while (p != NULL)
{
q = p -> next;
pre = L;
while (pre -> next != NULL && pre -> next -> data < p -> data)
pre = pre -> next;
p -> next = pre -> next;
pre -> next = p;
p = q;
}
}
【数据结构】 单链表的基本算法实现与应用
最新推荐文章于 2025-09-19 19:45:47 发布