ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数据结构--线性表

2021-03-28 21:05:16  阅读:158  来源: 互联网

标签:p2 head p1 ListNode 线性表 -- NULL next 数据结构


线性表:零个或多个数据元素的有序序列

         从字面意思上来想,

首先,它是有序的,那么就可以认为,每个元素之间是有序的,如果存在多个元素,那么第一个元素只有它的后继,最后一个元素只有它的前驱,而位于中间的元素,既有一个前驱也有一个后继,
其次,它是序列,那么也就是说,它是有限的,如果它是无限的,你先问问你的内存答应吗。

用图来表示大话,他就张这个样子
在这里插入图片描述

n即为线性表的长度;

线性表的顺序存储:

顺序存储,指用一段连续的存储单元一次存储线性表的数据元素;
如图所示:
在这里插入图片描述

其实,说白了,就和咋们用的一维数组差不多,在内存开辟一段连续的内存用来存放数据,他的大小,一开始就是被固定好的,随后不能发生改变。
线性表长度:
数组的长度就是线性表的长度吗?不一定,数组的长度是存放线性表的长度,一开始就确定好的,而线性表的长度是其数据元素的个数,会随着数据元素的增加和删除改变,所以,在任意时刻,线性表的长度都小于等于数组长度;
线性表地址
由于线性表是连续存储的,所以当我们确定一个数据的地址后,别的元素地址也都能确定,比如,现在知道,第5个元素的地址是5,那么5+1就是第六个元素地址,5+2,就是第七个元素地址,依次类推,
假如c是占用的存储单元,那么a(i)=a(i-1)+c;
或者是a(i)=a(1)+(i-1)*c;
在这里插入图片描述

插入与删除
获取第i个元素内容:
很简单,只要这东西存在,返回它的地址就行
在这里插入图片描述

插入操作:
如果说,要在第i个位置上插入一个数,如果,这个位置及其后面没有别的元素,那么就可以直接将数据放入第i个位置,但是,如果这个位置存在元素那么我们就得把第i及其后面的元素同一向后移动,让插入数据可以放在第i个位置;
在这里插入图片描述

插入之后:
在这里插入图片描述

那如果后面没有空闲位置,就会导致插入出错,可能就需要在动态调整,在增加位置
在这里插入图片描述

删除操作:

  • 如果删除位置不对,返回错误
  • 取出删除元素
  • 从删除元素开始遍历,后面的位置一次减一
  • 线性表表长减一
    在这里插入图片描述在这里插入图片描述

顺序表的优缺点
优点

  • 无需为表中的逻辑关系而增加存储空间,
  • 可以快速得到元素地址

缺点:

  • 无法动态变化其容量
  • 每次增加和删除需要大量操作

链式表

链式表不需要想顺序表那样“连续”,他可以是碎片式的
在这里插入图片描述

每个元素都存储着其表的逻辑关系,即每个元素的前驱或者后继
储存数据的部分,我们称为:数据域,储存下一个地址的部分称为:指针域。
链表的第一个节点的储存位置叫做它的“头指针”;所以,如果我们要对链表进行操作的时候,我们就需要从头指针开始进行;
在这里插入图片描述

有的时候,我们会创建一个“头结点”,它的数据域里面不储存内容,只储存指针域对其下一个节点的地址
在这里插入图片描述

链表的创建:

ListNode *creat(int num)
{
	ListNode*head=NULL,*p1=NULL,p2=NULL;
    p2=p1=(ListNode*)mallco(sizeof(ListNode));//创建头结点 
    head=p1;
    for(int i=1;i<=num;i++)
    {
    	p1=(ListNode*)malloc(sizeof(ListNode));//创建节点
		p2->next=p1; 
	}
	p2->next=NULL;
}

删除节点:

ListNode *del(int num)
{
	if(head->next==NULL)//如果链表为空,删除失败 
	return false;
	ListNode*p=head,*p2;int i=0;
	while(p)//如果p不是最后一个,就让他遍历
	{
		if(i==num)
		{
			if(i==1)
			{
				head->next=head->next->next;//如果是第一个节点,直接改变头结点的位置 
			}
			else{			
			p2->next=p->next;
			free(p);
			return true;
				
			} 

		}
		p2=p1;
		p=p->next;
		i++;		
	 } 
	 return false;
}

增加节点:

ListNode *add(int num)
{
	ListNode*p=head,*p1,*p2;
	for(int i=0;p;i++,p=p->next)
	{
		if(i==num)
		{
			if(i==1)//头插 
			{
				p1=(ListNode *)malloc(sizeof(ListNode));
				p1->next=head->next->next;
				head->next=p1;
				return true;
			}
			else if(p->next==NULL)//尾插 
			{
				p1=(ListNode *)malloc(sizeof(ListNode));
				p->next=p1;
				p1->next=NULL;
				return true;
			}
			else
			{
				p1=(ListNode *)malloc(sizeof(ListNode));
				p1->next=p;
				p2->next=p1;
				return true;
			}
		}
		p2=p1;
		p=p->next;
	}
	 return false;
}

循环链表

正如标题所言,这个链表,它具有环,有可能是完全环,也有可能是部分环
,用完全环来举列子,他的尾节点的next指针指向的是头结点
所以在创建循环链表的时候,只需在最后一步写上p2->next=head;即可

ListNode *creat(int num)
{
	ListNode*head=NULL,*p1=NULL,p2=NULL;
    p2=p1=(ListNode*)mallco(sizeof(ListNode));//创建头结点 
    head=p1;
    for(int i=1;i<=num;i++)
    {
    	p1=(ListNode*)malloc(sizeof(ListNode));//创建节点
		p2->next=p1; 
	}
	p2->next=NULL;
}

举个例子
在这里插入图片描述

洛谷的一个题来讲解,首先构建头尾相连的循环链表

struct Student* head = NULL, * p1 = NULL, * p2 = NULL;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		p1 = (struct Student*)malloc(sizeof(struct Student));
		p1->num = i;
		if (i == 1)
		{
			head = p1;
		}
		else
		{
			p2->next = p1;
		}
		p2 = p1;
	}
	p2->next = head;

根据题目所说,每次位置前进三,所以
每一次指针移动的就是p=p->next->next->next;
完整代码:

#include<iostream>
#include<cstdlib>
using namespace std;
struct Student
{
	int num;
	struct Student* next;
}s;
int main()
{
	int n, m;
	struct Student* head = NULL, * p1 = NULL, * p2 = NULL;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		p1 = (struct Student*)malloc(sizeof(struct Student));
		p1->num = i;
		if (i == 1)
		{
			head = p1;
		}
		else
		{
			p2->next = p1;
		}
		p2 = p1;
	}
	p2->next = head;
	struct Student* p = head; struct Student* p3 = NULL;
	int h=m-1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= h; j++)
		{
			p3 = p;
			p = p->next;
		}
		cout << p->num << " ";
		p3->next = p->next;
		h = m;
	}
	return 0;
}

如何判断有环?
最简单的办法:快慢指针!!!
让一个指针每次前进1个节点,另一个指针前进2个节点,如果他们相遇,说明有环,(这个很简单,就像钟表上的指针一样的道理,就不解释了);

ListNode circle(ListNode *head)
{
	ListNode *p1,*p2;
	p1=head;
	p2=head;
	while(1)
	{
		p1=p1->next;
		p2=p2->next->next;
		if(p1==p2)
		{
			return p1;
		}
	}
 } 

双向链表

刚才的链表,每个节点只有其对应的后继,而没有前驱,这就导致,如果我们要对链表进行操作,只能从前向后遍历,
双向链表,顾名思义,每个节点,既有前驱,也有后继,已到达前进后退自如,就像我们平时打开网页一样,也返回上一页,也可以前进下一页;
所以我们构建的时候,指针域就必须加上前驱指针了

typedef struct ListNode1
{
	int num;
	struct ListNoded1*prior;
	struct ListNode1*next;
}ListNode;
ListNode *creat(int n)
{
	ListNode *head,*p1,*p2;
	head=p1=p2=NULL;
	p1=(ListNode*)malloc(sizeof(ListNode));
	head=p1;
	p2=p1;
	for(int i=1;i<=n;i++)
	{
		p1=(ListNode*)malloc(sizeof(ListNode));
		p2->next=p1;
		p1->prior=p2;
		p2=p1;
	}
	p2->next=NULL;

}

标签:p2,head,p1,ListNode,线性表,--,NULL,next,数据结构
来源: https://blog.csdn.net/weixin_52313562/article/details/115216767

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有