声明:本文章由小编亲自撰写,如内容有错误或不妥之处,欢迎评论指正
链表基础知识体系
链表的建立
结点的定义
- 结点的数据类型是结构体
- 结点中的内容分为两部分:数据域和指针域
- 数据域就是结点存储的数据
- 指针域就是用来存储上一个结点或下一个结点的地址
- 代码段示例:
struct Node
{
//数据域
int a;
float b;
double c;
char d;
//指针域
Node* next;
Node* front;
};
链表类型
无头结点链表
- 本文创建链表据采用函数形式进行建立,为了方便易懂,使用函数名Creat
- 这种链表没有头结点,就要有头指针
- 代码段
//建立无头结点链表
Node* Creat()
{
int n = 0; //记录此时链表中的结点个数
Node* head; //头指针
Node* p1 = NULL, * p2 = NULL; //p1是用来建立新的结点 p2是用来追踪链表中的尾结点
//准备工作已完成、开始建立
for (int i = 0;; i++)
{
p1 = (Node*)malloc(sizeof(Node)); //为p1申请新的内存空间
cin >> p1->data; //输入p1的数据域
//判断链表结束建立条件
if (p1->data == -1) break;
n++; //该结点符合条件,结点数加一
//将新结点添加到链表中,第一次是添加到头指针的后面,第二次是添加到链表尾结点的后面
if (n == 1) head = p1;
else if (n > 1) p2->next = p1;
p2 = p1; //将p2指向链表的尾结点
}
p2->next = NULL; //链表尾结点的指针域清空 !!!
return head; //做接口,返回头指针,有利于外部使用
}
有头结点链表
- 有头结点的链表就没有头指针
- 头结点的指针域相当于头指针
- 头结点的数据域可以用来存储其他的信息,比如链表的结点数
- 代码段
//建立有头结点链表
Node* Creat()
{
int n = 0; //记录此时链表中的结点个数
Node* head= (Node*)malloc(sizeof(Node)); //头结点
head->next = NULL; //头结点的指针域清空
Node* p1=NULL, *p2=NULL; //p1是用来建立新的结点 p2是用来追踪链表中的尾结点
//准备工作已完成、开始建立
for (int i = 0;; i++)
{
p1 = (Node*)malloc(sizeof(Node)); //为p1申请新的内存空间
cin >> p1->data; //输入p1的数据域
//判断链表结束建立条件
if (p1->data == -1) break;
n++; //该结点符合条件,结点数加一
//将新结点添加到链表中,第一次是添加到头指针的后面,第二次是添加到链表尾结点的后面
if (n == 1) head->next = p1;
else if (n > 1) p2->next = p1;
p2 = p1; //将p2指向链表的尾结点
}
p2->next = NULL; //链表尾结点的指针域清空 !!!
return head; //做接口,返回头指针,有利于外部使用
}
- 无结点链表和有结点链表的区别
- 头指针是结构体指针,头结点是头指针指向了自己申请的结点的内存空间
- 在建立链表时,第一个新的结点添加到链表时,代码不同
if (n == 1) head = p1; //头指针
if (n == 1) head->next = p1; //头结点
- 头指针实际上指向的是链表的第一个结点,而头结点实际上指的是自己的结点空间
头结点的指针域才真正指的是链表的第一个结点
FIFO型链表
- FIFO型链表指的是按照顺序的方式建立链表
先添加的结点会先输出,就像排队一样 - 该类型链表的建立方式和前面使用的有无头结点的程序类似,这里不作展示
FIFO型链表
- FIFO型链表指的是按照顺序的方式建立链表,先添加的结点会后输出
- 这种链表的程序逻辑主要是将新的结点不断的放到头结点的后面,之后将新结点的指针域指向之前头结 点的指针域指向的结点
- 逻辑示意图
- 代码段
//建立FILO链表
Node* Creat()
{
int n = 0; //记录此时链表中的结点个数
Node* head= (Node*)malloc(sizeof(Node)); //头结点
head->next = NULL; //头结点的指针域清空
Node* p1 = NULL; //p1是用来建立新的结点
//准备工作已完成、开始建立
for (int i = 0;; i++)
{
p1 = (Node*)malloc(sizeof(Node)); //为p1申请新的内存空间
cin >> p1->data; //输入p1的数据域
//判断链表结束建立条件
if (p1->data == -1) break;
n++; //该结点符合条件,结点数加一
//将新结点添加到链表中
if (n == 1)
{
head->next = p1;
p1->next = NULL; //此时p1是第一个结点,同时也是最后一个结点,所以p1指针域一定要清空
}
else if (n>1)
{
p1->next = head->next; //新结点p1指向头结点之前指向的第一个结点
head->next = p1; //头结点指向p1,使得p1成为第一个结点
}
}
return head; //做接口,返回头指针,有利于外部使用
}
Note:
- 建立FILO链表时,不需要用p2结点持续指向尾结点
- 在添加第一个新结点时,要注意将新结点的指针域清空,因为这个结点是最后链表的尾结点
输入方式
以特定数字或字母结束
- 以特定数字结束
if (p1->data == -1) break;
- 以特定字母或字符串结束
if (strcmp(p1->data, "特定字母或字符串")==0) break;
以回车结束
划重点!!!
- 当使用回车作为 结束输入时,就不能再用for循环了,此时应该用while来判断输入的状态
while (cin >> p1->data)
{
//建立链表相关代码
}
其他零碎知识点
- 结点代表对象时的输入建立问题
如果链表的建立过程是需要将同类的结点合并,同时对结点的数据域进行操作
那么就需要在链表的建立过程中,进行判断和处理 - 该类型链表的建立采用投票的例子进行讲解
- 题目:用链表实现对候选人的得票进行统计、name存放候选人的姓名。若在链表的结点上找到name,则将姓名为name的结点上将得票数加1;否则新建一个结点,初始化其姓名和所得的票数,并将新结点插入链尾。
- 代码示例
//创建投票链表
struct Node* creat()
{
int n = 0; //用于判断结点数
struct Node* head = NULL; //头指针
struct Node* p1 = NULL; //用于不断地创建新的结点
struct Node* p2 = NULL; //用于指向链表的最后的一个结点
struct Node* search = NULL; //用于遍历链表
int flag = 0; //标志位 用于记录是否有该名字
p1 = (Node*)malloc(sizeof(Node));
//准备就绪,开始建立链表
for (int i = 0;; i++)
{
flag = 0; //标志位置零
//初始化新链表
cin >> p1->name;
p1->next = NULL;
p1->fare = 0;
//如果输入0结束建立链表
if (strcmp(p1->name, "0") == 0)
break;
n++;
if (n == 1) //第一个结点在链表中肯定是没有重复的
{
p1->fare++;
head = p1;
sum = 1;
}
else
{
search = head; //将搜索指针处于第一个结点的位置
for (int j = 0; j < sum; j++)
{
if (strcmp(search->name, p1->name) == 0) //发现链表中已经有相同名字的喽
{
search->fare++; //票数加一
flag = 1;
break;
}
search = search->next;
}
if (flag == 0) //未发现这个名字,新建结点
{
p1->fare++;
p2->next = p1;
sum++;
}
}
if (flag == 0) //对于新建的结点的一些处理
{
p2 = p1;
p1 = (Node*)malloc(sizeof(Node));
}
}
if (sum != 0)
p2->next = NULL;
return head;
}
链表的操作
对于链表的操作,一般都是需要进行链表的遍历,所以这个功能的写法一定要非常的熟练
对于链表的操作,一般会另写一个函数,这里就使用函数名Func
判断型
- 判断链表是否递增的
代码段
//判断链表是否是递增的
int Func(struct Node* head, int a)
{
int flag = 1; //递增标志位
struct Node* p = head; //p是用于遍历的结点指针
for (int i = 0; i < a - 1; i++)
{
if (p->data > (p->next)->data)
{
flag = 0;
return 0;
}
p = p->next;
}
if (flag == 1)
{
return 1;
}
}
- 判断链表的数据是否对称
//判断链表是否对称函数
int Func2(Node* head)
{
int flag = 1;
if (sum % 2 == 0)
{
for (int j = 1; j <= sum / 2; j++)
{
if (Find(head, j) != Find(head, sum - j + 1))
{
flag = 0;
break;
}
}
}
if (sum % 2 != 0)
{
for (int j = 1; j <= sum / 2; j++)
{
if (Find(head, j) != Find(head, sum - j + 1))
{
flag = 0;
break;
}
}
}
return flag;
}
//用于查找链表特定位置的值
char Find(Node* head, int n)
{
Node* search = head;
for (int i = 0; i < n-1; i++)
{
search = search->next;
}
return search->data;
}
查找型
- 查找链表中的最大整数,并将其和表尾的数值对换
//查找链表的最大数值并交换
void HandleLinkList(NODE* head)
{
int pos = 1; //记录最大值结点的位置
int sum = head->sum; //记录结点的数量
int max; //记录最大值
//准备工作
NODE* p = head;
max = p->num; //起始默认第一个结点的数值是最大的
NODE* end_last = p; //尾指针,用于最后的数据交换
NODE* t; //交换结点数据的临时结点
NODE* sec = head->next; //最大值的结点的前一个结点
NODE* max_point = p; //最大值的结点
int temp;
//找到倒数第二个结点
for (int i = 0; i < sum - 2; i++)
{
end_last = end_last->next;
}
//找到最大结点的位置
for (int i = 1; i <= sum; i++)
{
if (p->num > max)
{
pos = i;
max = p->num;
}
p = p->next;
}
//如果链表中只有一个结点的话,就不需要进行交换的
if (head->sum == 1)
return;
//如果链表中有两个结点的话,直接进行判断
if (head->sum == 2)
{
if (head->num > head->next->num)
{
temp = head->num;
head->num = head->next->num;
head->next->num = temp;
}
return;
}
//对于三个结点以上的处理
//如果最大值是第一个结点
if (pos == 1)
{
temp = head->num;
head->num = end_last->next->num;
end_last->next->num = temp;
}
//如果最大值不是第一个结点
else if (pos >= 2 && pos <= sum)
{
max_point = head;
for (int i = 0; i < pos - 2; i++)
max_point = max_point->next;
t = max_point->next;
max_point->next = end_last->next;
if (t != end_last)
{
max_point->next->next = t->next;
end_last->next = t;
}
else
{
max_point->next->next = t;
}
t->next = NULL;
}
}
- 查找某数据出现的次数
int Func(Num* head,int seek)
{
Num* p = head->next;
int sum = head->sum;
int times=0; //出现次数
//如果等于查找的数,就加一
if (p->data == seek) times++;
for (int i = 0; i < sum-1; i++)
{
p = p->next;
if (p->data == seek)
{
times++;
}
}
return times;
}
- 查找链表的中间结点
//查找链表中的中间结点
int Func(struct Num* head)
{
struct Num* p = head; //头结点
int all = head->sum;
int result;
int time=1; //定义查询次数
//只有一个结点的情况
if (all == 1)
{
result = head->next->data;
}
else if (all % 2 == 0) //偶数个结点的情况
{
time = all / 2;
}
else if (all % 2 != 0) //奇数个结点的情况
{
time = all / 2 + 1;
}
for (int i = 1; i <= time; i++)
{
p = p->next;
}
result = p->data;
return result;
}
删除型
- 删除最小的元素
void Func(Num* head)
{
Num* p = head;
int flag = 0;
for(int i = 0; i < head->sum; i++)
{
if (p->nxet->data < head->min)
{
head->min = p->nxet->data;
}
p = p->nxet;
}
p = head;
for (int i = 0; i < head->sum; i++)
{
if (p->nxet->data == head->min)
{
if (head->sum == 1)
{
head->nxet = NULL;
}
else
p->nxet = p->nxet->nxet;
flag = 1;
}
if (flag == 1)
break;
p = p->nxet;
}
}
- 删除重复元素
//删除链表中的重复元素
void Func(struct Num* head)
{
int t; int flag = 0;
struct Num* p = head; //第一个结点
struct Num* k = p->next;
t = p->next->data;
int all = head->sum;
for (int i = 0; i < head->sum-1; i++)
{
for (int j = i; j < all; j++)
{
if (k->next == NULL)
continue;
if (k->next->data == t)
{
k->next = k->next->next;
head->sum--;
flag = 1;
}
if (flag == 0)
{
k = k->next;
}
flag = 0;
}
p = p->next;
k = p->next;
if(p->next!=NULL)
t = k->data;
}
}
移动型
- 最大值移动到表尾
//将链表中的结点最大值放到链表最后
void Func(struct Num* head)
{
int max;
struct Num* p = head; //头结点
struct Num* k = p->next;
struct Num* t;
max = p->next->data;
int all = head->sum;
for (int i = 0; i < all-1; i++)
{
if (max < k->next->data)
{
max = k->next->data;
}
k = k->next;
}
k = p->next;
for (int i = 0; i < all; i++)
{
if (k->data == max && i == all - 1) //最后一个结点是最大值
break;
if (k->data == max) //第一个结点就是最大值
{
p->next = k->next;
t = head->end;
t->next = k;
k->next = NULL;
break;
}
p = p->next;
k = p->next;
}
}
插入型
- 升序插入结点
//插入结点
struct Num* Func(struct Num* head, struct Num* insert)
{
int flag = 0;
Num* p = head;
if (sum == 0)
{
head = insert;
}
else if (sum == 1)
{
if (head->data < insert->data)
{
head->next = insert;
insert->next = NULL;
}
else
{
insert->next = head;
head = insert;
}
}
else
{
for (int i = 0; i < sum; i++)
{
if (p->next==NULL)
{
if (p->data <= insert->data)
{
p->next = insert;
insert->next = NULL;
flag = 1;
}
}
else if (i == 0 && head->data >= insert->data)
{
insert->next = head;
head = insert;
flag = 1;
}
else if (p->data <= insert->data && insert->data <= p->next->data)
{
insert->next = p->next;
p->next = insert;
flag = 1;
}
if (flag == 1)break;
p = p->next;
}
}
sum++;
return head;
}
逆置型
- 链表逆置
//链表逆置
void Func(struct Num* head)
{
struct Num* p = head->next;
p->last = NULL;
for (int i = 0; i < head->sum; i++)
{
if (p->next == NULL)
continue;
p->next->last = p;
p = p->next;
}
}
链表的输出
顺序输出
- 输出一般都是固定的格式,基本都是输出链表,大多数情况下是不做任何处理的,一般是在Func函数中进行处理
//输出链表
void Print(struct Num* head)
{
struct Num* pp = head;
for (int i = 0; i <sum; i++)
{
cout << pp->data << " ";
if (pp->next == NULL)
continue;
pp = pp->next;
}
}
特定输出
- 边输出边删除结点
//输出链表
void Print(struct Num* head)
{
struct Num* pp = head->next;
int all = head->sum -1;
for (int i = 0; i < head->sum; i++)
{
if (pp == NULL)
continue;
cout << pp->data << " "<< all-- <<" ";
head->next = pp->next;
pp->next = NULL;
delete pp;
pp = head->next;
}
}
输出特定条件的结点
- 输出特定位置的结点
//输出特定结点
void Print(struct Num* head,int postion)
{
struct Num* pp = head;
if (postion <= 0||postion>sum)
cout << "Not Found";
else
{
for (int i = 0; i < postion-1; i++)
{
pp = pp->next;
}
cout << pp->data;
}
}
结语:
本文涉及的完整程序已经上传到小编的个人Github上,如有兴趣或需要 ,欢迎访问
网址:https://github.com/LeiChengWang/CSDN_related_code.git