链表的基本定义与方法列表
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int Element;
typedef struct LNode{
Element data;
LNode *next;
}*LinkList;
bool InitList(LinkList &L); //初始化线性表 L
bool DestoryList(LinkList &L); //销毁单链表 L
int Length(LinkList &L); //求线性表 L 的长度
void PrintList(LinkList &L); //输入单链表 L 中的所有元素
void PrintList(LinkList &L); //判断单链表 L 是否为 NULL
bool HeadInsert(LinkList &L); //使用 头 插法向单链表 L 中插入数据
bool TailInsert(LinkList &L); //使用 尾 插法向单链表 L 中插入数据
bool DeleteList(LinkList &L, int i); //删除单链表 L 中的第i个元素
bool InsertList(LinkList &L, int i, Element data); //在单链表 L 的第i个位置插入一个新的元素
bool PreInsert(LinkList &L, int i); //已知链表 L 为单链表, 要求在第i个结点 前插结点s(删除同理)
LNode *GetElem(LinkList L, int i); //按照 序号 在单链表中查找所对应的结点
LNode *LocateElem(LinkList L, Element e); //按照 值 检索单链表中所对应的结点
实现课后题的函数列表
/*-------王道课后习题-----------*/
void RecursionDeleteX(LinkList &L, Element x); //1.设计一个递归算法,删除不带头结点的单链表 L 中所有值为 x 的结点
bool DeleteX(LinkList &L, Element x); //2.删除单链表 L 中所有值为x的结点,并释放其空间
void ReversePrint(LinkList &L); //3.从尾至首依次输出单链表 L 的所有值
bool DeleteMinValue(LinkList &L); //4.编写一个删除单链表 L 中最小值的高效算法
LinkList ReverseList(LinkList &L); //5.将一个单链表 L 就地逆序存放
void SortLinkList(LinkList &L); //6.设计一个带头结点的单链表 L , 设计一个算法使其元素递增有序
bool DeleteBetweenST(LinkList &L, Element s, Element t); //7. 删除单链表中所有介于给定值之间的结点(同2)
LinkList GetPublicNode(LinkList &L1, LinkList &L2); //8.给定两个单链表,编写算法找到两个结点的公共结点
bool FindMax(LinkList &L, LinkList &max, LinkList &pre_max); //找到一个单链表中最大的结点和它的前驱结点
bool OrderDelete(LinkList &L); //9.按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间
bool DevideList(LinkList &LA, LinkList &LB); //10.链表A分为两个链表A和B,链表A存放原链表奇数部分,链表B存放原链表偶数部分
bool DeleteOrderSameValue(LinkList &L); //12.删除递增有序链表中的重复元素
LinkList MergeList(LinkList &L1, LinkList &L2); //13.将两个元素值递增的单链表合并,生成一个元素值递减的新链表
LinkList MergeSameValue(LinkList L1, LinkList L2); //14. 设计一个算法从有序单链表A和B的公共元素产生链表C
bool isSubsequence(LinkList &LA, LinkList &LB); //16. 判断序列B是否为序列A的连续子序列
全部函数的具体代码(因为时间有限,没有来得及整理,比较重要的代码会有注释在里面)
/*----------------------------------------------------------------------单链表的基本函数-------------------------------------------------------*/
//使用头插法插入向单链表 L 中插入数据
bool HeadInsert(LinkList &L){
int is_insert;
LNode *p;
printf("插入数据(输入9999结束插入):\n");
scanf("%d", &is_insert);
while(is_insert != 9999){
p = (LNode *)malloc(sizeof(LNode));
p->data = is_insert;
p->next = L->next;
L->next = p;
printf("插入成功, 请继续插入数据(输入9999结束插入):\n");
scanf("%d", &is_insert);
}
return true;
}
//使用尾插法向线性表 L 中插入数据
bool TailInsert(LinkList &L){
int is_insert;
LNode *p, *tail = L;
printf("插入数据(输入9999结束插入):\n");
scanf("%d", &is_insert);
while(is_insert != 9999){
p = (LNode *)malloc(sizeof(LNode));
p->data = is_insert;
tail->next = p;
tail = p;
printf("插入成功, 请继续插入数据(输入9999结束插入):\n");
scanf("%d", &is_insert);
}
tail->next = NULL;
return true;
}
//按照序号查找结点值
LNode *GetElem(LinkList L, int i){
int count = 0;
LNode *p = L;
if(i == 0) return L;
if(i < 0) return NULL;
while(p != NULL && count < i){
p = p->next;
count++;
}
if(p != NULL)
return p;
else
return NULL;
}
//按值查找表结点
LNode *LocateElem(LinkList L, Element e){
LNode *p = L->next;
while(p != NULL){
if(p->data == e)
return p;
p = p->next;
}
return NULL;
}
//插入结点操作
bool InsertList(LinkList &L, int i, Element data){
if(i < 1 || i > Length(L)+1)
return false;
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = data;
s->next = NULL;
LNode *p = GetElem(L, i-1);
s->next = p->next;
p->next = s;
return true;
}
//已知链表 L 为单链表, 要求在第i个结点 前插结点s(删除同理)
bool PreInsert(LinkList &L, int i){
if(i < 1 || i > Length(L)) //仅可以插入到第一个结点和最后一个结点之前
return false;
int is_insert = 0, temp;
printf("请输入要插入的数据:\n"); //生成要插入的数据结点
scanf("%d", &is_insert);
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = is_insert;
s->next = NULL;
LNode *p = GetElem(L, i); //找到第i个位置的结点
s->next = p->next; //逻辑上实现前插操作
p->next = s;
temp = s->data;
s->data = p->data;
p->data = temp;
return true;
}
//删除单链表中的第i个元素
bool DeleteList(LinkList &L, int i){
if(i<1 || i>Length(L))
return false;
LNode *p = GetElem(L, i-1); //找到第i-1个元素
LNode *q = p->next; //在单链表中删除第i个元素
p->next = q->next;
free(q);
return true;
}
//初始化线性表 L
bool InitList(LinkList &L){
L->data = 0;
L->next = NULL;
return true;
}
//求线性表的长度
int Length(LinkList &L){
LNode *p = L->next;
int count = 0;
while(p != NULL){
count++;
p = p->next;
}
return count;
}
//输入单链表中的所有元素
void PrintList(LinkList &L){
LNode *p = L->next;
while(p != NULL){
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//判断单链表是否为 NULL
bool Empty(LinkList &L){
if(L->next == NULL)
return true;
else
return false;
}
//销毁单链表 L
bool DestoryList(LinkList &L){
LNode *p = NULL;
while(L->next != NULL){
p = L->next;
L->next = p->next;
free(p);
}
L->data = 0;
return true;
}
/*-------------------------------------------------------------------王道课后练习习题-------------------------------------------------------------------------------*/
/*
1.设计一个递归算法,删除不带头结点的单链表 L 中所有值为 x 的结点
*/
void RecursionDeleteX(LinkList &L, Element x){
if(L == NULL) //若 L 为空表,则直接进行返回
return;
if(L->data == x){ //若 L->data 为 x, 则对其进行删除
LNode *p = L; //定义指针变量 p 指向当前结点
/*
非常重要!!!
该结点指针 L 后移,指向下一结点,注意,由于 L指针为引用数据类型,
所以L指向结点变化时,它上一层结点的next域也将指向下一结点,这样就可以避免删除操作造成的断链现象
*/
L = L->next;
free(p); //此时再将该结点删除
RecursionDeleteX(L, x); //继续递归查找删除子链表中值为 x 的结点
}else{
RecursionDeleteX(L->next, x); //结点不是x的话就继续递归下层子链表
}
}
/*
2.删除单链表中所有值为x的结点,并释放其空间
*/
bool DeleteX(LinkList &L, Element x){
if(L->next == NULL)
return false;
LNode *p = L->next, *q = L, *temp;
while(p != NULL){
if(p->data == x){
temp = p;
p = p->next;
q->next = p;
free(temp);
}else{
q = p;
p = p->next;
}
}
return true;
}
/*
3.从尾至首依次输出单链表的所有值
*/
void ReversePrint(LinkList &L){
if(L->next != NULL)
ReversePrint(L->next);
if(L != NULL && L->data != 0)
printf("%d ", L->data);
}
/*
4.编写一个删除单链表 L 中最小值的高效算法
(注意王道书上实现方式,更加方便,类似于打擂台算法,使用两个指针分别存放最小值结点及其前驱)
*/
bool DeleteMinValue(LinkList &L){
if(L->next == NULL)
return false;
if(L->next->next == NULL){
LNode *temp = L->next;
L->next = temp->next;
free(temp);
}
int tempi = 0;
LNode *q = L->next, *p = L->next->next;
while(p->next != NULL){ //边循环边比较,将最小的元素值移动至倒数第二个
if(q->data < p->data){
tempi = q->data;
q->data = p->data;
p->data = tempi;
}
q = p;
p = p->next;
}
if(q->data < p->data) //将最小元素再与倒数第一元素比较大小,决定删除哪一个
q->data = p->data;
q->next = p->next;
free(p);
}
/*
5.将一个单链表 L 就地逆序存放
*/
LinkList ReverseList(LinkList &L){
if(L->next == NULL) //若单链表为空则直接返回
return L;
LNode *q = L, *p = L->next, *temp; //定义q指针指向前一个元素,p指针指向后一个元素,temp为临时指针
q->next = NULL; //将即将形成的链表链尾的指针域置 NULL
while(p != NULL){
q->data = p->data; //将后一个元素的值赋值给前一个元素
temp = p->next; //temp暂时记录下一元素
p->next = q; //p指针此时反向指向q,即q将作为p的后继指针
q = p; //q指针后挪
p = temp; //p指针后挪
}
q->data = 0; //将最后形成的头结点数据域清 0
L = q; //最终 将 q赋值给 L 作为头结点
return L;
}
/*
6.设计一个带头结点的单链表 L , 设计一个算法使其元素递增有序
本题算法思想,将单链表 L 从头部拆分为两个链表,第一个链表初始化仅有一个元素,
故有序,逐渐将第二个链表中的元素插入到第一个链表 L , 使其依旧有序,当全部插入完成后,该链表就实现了排序
类似于 H---->12---->2---->1----->5---->3......
L1 = (H---->12---->NULL) //pre指针作为循环遍历该链表的指针, 用于确定插入位置
L2 = (2---->1---->5---->3......) //逐渐将L2的结点按序插入L1即可完成排序, 其中p指向该链表的表头结点, r为p后继结点
*/
void SortLinkList(LinkList &L){
if(L->next == NULL)
return;
LNode *p, *pre, *r; //p 作为 循环遍历链表 L2, pre 作为 循环遍历 L1, r暂存p的后继结点
p = L->next; //实现链表的分割
r = p->next;
p->next = NULL;
p = r;
while(p != NULL){ //循环遍历第二个链表,将其有序插入链表1中
r = p->next;
pre = L;
while(pre->next!=NULL && pre->next->data < p->data) //确定插入位置
pre = pre->next;
p->next = pre->next; //进行插入操作
pre->next = p;
p = r; // p 指针后移
}
}
/*
7. 删除单链表中所有介于给定值之间的结点(同2)
*/
bool DeleteBetweenST(LinkList &L, Element s, Element t){
if(L->next == NULL)
return false;
LNode *p = L->next, *q = L, *temp;
while(p != NULL){
if(p->data>=s && p->data<=t){
temp = p;
p = p->next;
q->next = p;
free(temp);
}else{
q = p;
p = p->next;
}
}
return true;
}
//返回单链表中最大值结点的前一个结点
bool FindMax(LinkList &L, LinkList &max, LinkList &pre_max){
if(L->next == NULL) return false;
LNode *q = L, *p = L->next;
pre_max = L, max = L->next;
while(p != NULL){
if(p->data > max->data){
max = p;
pre_max = q;
}
q = p;
p = p->next;
}
return true;
}
/*
8.给定两个单链表,编写算法找到两个结点的公共结点
*/
LinkList GetPublicNode(LinkList &L1, LinkList &L2){
if(L1->next==NULL || L2->next==NULL)
return false;
int len1 = Length(L1), len2 = Length(L2), dist;
LinkList short_list, long_list;
if(len1 > len2){ //确定一个较长的链表和较短的链表,并得到他们的差值
long_list = L1->next;
short_list = L2->next;
dist = len1-len2;
}else{
long_list = L2->next;
short_list = L1->next;
dist = len2-len1;
}
for(int i=1; i<=dist; i++){ //根据差值,长链表指针依次后移,目的实现长短链表同步开始寻找公共结点
long_list = long_list->next;
}
while(long_list != NULL){ //循环遍历寻找公共结点
if(short_list->data == long_list->data)
return long_list;
short_list = short_list->next;
long_list = long_list->next;
}
return NULL;
}
/*
9.按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间
*/
bool OrderDelete(LinkList &L){
LNode *p, *pre_max, *temp;
while(L->next != NULL){ //单链表不为NULL,则一直循环查找最小值并删除
pre_max = L; //pre_max始终指向当前最小值元素的前驱结点
p = L->next; //作为工作指针
//查找出单链表的最小值结点,pre_max将指向其前驱结点
while(p->next != NULL){
if(p->next->data < pre_max->next->data) //循环遍历比较
pre_max = p; //记住当前最小值结点的前驱结点
p = p->next;
}
printf("正在删除数据---->\t%d.\n", pre_max->next->data); //输出结点
temp = pre_max->next; //删除结点
pre_max->next = temp->next;
free(temp);
}
}
/*
10.链表A分为两个链表A和B,链表A存放原链表奇数部分,链表B存放原链表偶数部分
*/
bool DevideList(LinkList &LA, LinkList &LB){
if(LA->next == NULL)
return false;
InitList(LB);
int count = 0;
LNode *p1 = LA, *p2 = LB, *temp;
while(p1->next != NULL){
if((++count)%2 == 0){
temp = p1->next->next;
p1->next->next = NULL;
p2->next = p1->next;
p2 = p1->next;
// p1->next->next = LB->next; //第11题答案只需将上三行换为该两行即可,是为头插法
// LB->next = p1->next;
p1->next = temp;
}else
p1 = p1->next;
}
return true;
}
/*
12.删除递增有序链表中的重复元素
*/
bool DeleteOrderSameValue(LinkList &L){
if(L->next == NULL)
return false;
LNode *q = L->next, *p = L->next->next, *temp;
while(p != NULL){
if(q->data != p->data){
q = p;
p = p->next;
}else{
temp = p->next;
q->next = temp->next;;
free(temp);
}
}
return true;
}
/*
13.将两个元素值递增的单链表合并,生成一个元素值递减的新链表
*/
LinkList MergeList(LinkList &L1, LinkList &L2){
if(L1->next == NULL || L2->next == NULL)
return NULL;
LNode *p1 = L1, *p2 = L2, *temp;
while(p1->next != NULL && p2->next != NULL){
if(p1->next->data > p2->next->data){ //若p1所指结点的值大于 p2所指结点的值
temp = p2->next->next;
p2->next->next = p1->next; //将 p2所指结点 连接到 p1所指结点之前
p1->next = p2->next;
p2->next = temp; // p2 指针后移
}else
p1 = p1->next; //否则 p1 指针后移
}
while(p2->next != NULL){ //将剩余结点全部连接到 p1 之上
p1->next = p2->next;
p2 = p2->next;
}
L1 = ReverseList(L1); //将最后所得结果再逆序存放一下即可
return L1;
}
/*
14. 设计一个算法从有序单链表A和B的公共元素产生链表C
*/
LinkList MergeSameValue(LinkList L1, LinkList L2){
LinkList L = (LNode *)malloc(sizeof(LNode));
InitList(L);
LNode *p1 = L1->next, *p2 = L2->next;
while(p1!=NULL && p2!=NULL){
if(p1->data == p2->data){
InsertList(L, Length(L)+1, p1->data);
p1 = p1->next;
p2 = p2->next;
}else if(p1->data < p2->data){
p1 = p1->next;
}else{
p2 = p2->next;
}
}
return L;
}
/*
16. 判断序列B是否为序列A的连续子序列
*/
bool isSubsequence(LinkList &LA, LinkList &LB){
if(LB->next == NULL)
return true;
LNode *p1 = LA->next, *p2 = LB->next;
while(p1 != NULL){
LNode *k1 = p1, *k2 = p2;
while(k1!=NULL && k2!=NULL){
if(k1->data != k2->data) break;
k1 = k1->next;
k2 = k2->next;
}
if(k2 == NULL)
return true;
p1 = p1->next;
}
return false;
}