链表知识体系——实战篇(结合代码讲解、超详细)


声明:本文章由小编亲自撰写,如内容有错误或不妥之处,欢迎评论指正


链表的建立

结点的定义

  • 结点的数据类型是结构体
  • 结点中的内容分为两部分:数据域和指针域
  • 数据域就是结点存储的数据
  • 指针域就是用来存储上一个结点或下一个结点的地址
  • 代码段示例:
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;   //做接口,返回头指针,有利于外部使用
}
  • 无结点链表和有结点链表的区别
  1. 头指针是结构体指针,头结点是头指针指向了自己申请的结点的内存空间
  2. 在建立链表时,第一个新的结点添加到链表时,代码不同
if (n == 1)  head = p1;         //头指针
if (n == 1)  head->next = p1;   //头结点
  1. 头指针实际上指向的是链表的第一个结点,而头结点实际上指的是自己的结点空间
    头结点的指针域才真正指的是链表的第一个结点

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:

  1. 建立FILO链表时,不需要用p2结点持续指向尾结点
  2. 在添加第一个新结点时,要注意将新结点的指针域清空,因为这个结点是最后链表的尾结点

输入方式

以特定数字或字母结束

  • 以特定数字结束
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


判断型

  1. 判断链表是否递增的
    代码段
//判断链表是否是递增的
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;
	}
}
  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;
}

查找型

  1. 查找链表中的最大整数,并将其和表尾的数值对换
//查找链表的最大数值并交换
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;
	}
}
  1. 查找某数据出现的次数
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;
}
  1. 查找链表的中间结点
//查找链表中的中间结点
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;
}

删除型

  1. 删除最小的元素
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;

	}
}
  1. 删除重复元素
//删除链表中的重复元素
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;
	}
}

移动型

  1. 最大值移动到表尾
//将链表中的结点最大值放到链表最后
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;
	}
}

插入型

  1. 升序插入结点
//插入结点
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;
}

逆置型

  1. 链表逆置
//链表逆置 
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;			
	}
}

特定输出

  1. 边输出边删除结点
//输出链表
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;						
	}
}

输出特定条件的结点

  1. 输出特定位置的结点
//输出特定结点 
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


  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值